smart_proxy_dns_powerdns 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5915405ebcc666b7bea1a17d2422b0209e25c140
4
- data.tar.gz: 51643c7ca288e9af25a0f3df8fd209b631098b48
3
+ metadata.gz: 6aef9bd6276d31444fab893bb884ccc663820cba
4
+ data.tar.gz: e17f14a05adef3c63ec53c0d8f131a3668f962dd
5
5
  SHA512:
6
- metadata.gz: 6e56cf914dbf2dce5334691d4bf332cf272c47e859402b202453f8998b90a6e993def0a38ca865f7906167d7769c94120b38f7efcf5842dd44a1fcdd583bc75c
7
- data.tar.gz: 40b5f8ebac73a321889d4f7e3edeb3e24f25050a4972df7d9a81f7e6ede6fb0ef08cd2c002e62561fd5d9c9cb8f5fb6f67abb571821139e8c3f8cefaadc1972a
6
+ metadata.gz: ebf9237f705434eec15aa56008f4819270e7134816433fba310da2b5daa80574a7065f249fd848b32f612dc12a87917f14f6e9eb0743751b83dfa79c16252ca7
7
+ data.tar.gz: 4aae6514df432f499f87998e9c84a0ec1b4964146073bb9b59d60770d3f058b7d0cc8636762d0c7868bde48c674d1797c474c87a54069e2c1ac15938adbcb386
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # SmartProxyDnsPowerdns
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/smart_proxy_dns_powerdns.svg)](https://badge.fury.io/rb/smart_proxy_dns_powerdns)
4
+ [![Build Status](https://travis-ci.org/theforeman/smart_proxy_dns_powerdns.svg?branch=master)](https://travis-ci.org/theforeman/smart_proxy_dns_powerdns)
5
+ [![Coverage Status](https://coveralls.io/repos/github/theforeman/smart_proxy_dns_powerdns/badge.svg?branch=master)](https://coveralls.io/github/theforeman/smart_proxy_dns_powerdns?branch=master)
6
+
3
7
  This plugin adds a new DNS provider for managing records in PowerDNS.
4
8
 
5
9
  ## Installation
@@ -7,7 +11,7 @@ This plugin adds a new DNS provider for managing records in PowerDNS.
7
11
  See [How_to_Install_a_Smart-Proxy_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
8
12
  for how to install Smart Proxy plugins
9
13
 
10
- This plugin is compatible with Smart Proxy 1.11 or higher.
14
+ This plugin is compatible with Smart Proxy 1.13 or higher.
11
15
 
12
16
  When installing using "gem", make sure to install the bundle file:
13
17
 
@@ -15,7 +19,14 @@ When installing using "gem", make sure to install the bundle file:
15
19
 
16
20
  ## Upgrading
17
21
 
18
- Per version 0.2.0 the backend is a required parameter.
22
+ ### 0.3.0
23
+
24
+ * The minimum Smart Proxy version is now 1.13
25
+ * The REST backend is now the preferred backend. Users are encouraged to use it.
26
+
27
+ ### 0.2.0
28
+
29
+ * The backend is a required parameter.
19
30
 
20
31
  ## Configuration
21
32
 
@@ -25,6 +36,16 @@ To enable this DNS provider, edit `/etc/foreman-proxy/settings.d/dns.yml` and se
25
36
 
26
37
  Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dns_powerdns.yml`.
27
38
 
39
+ ### REST
40
+
41
+ To use the REST backend, set the following parameters:
42
+
43
+ :powerdns_backend: 'rest'
44
+ :powerdns_rest_url: 'http://localhost:8081/api/v1/servers/localhost'
45
+ :powerdns_rest_api_key: 'apikey'
46
+
47
+ Note the API is only tested with 4.x. Older versions may work, but they can also break.
48
+
28
49
  ### MySQL
29
50
 
30
51
  To use MySQL, set the following parameters:
@@ -42,9 +63,9 @@ To use PostgreSQL, set the following parameters:
42
63
  :powerdns_backend: 'postgresql'
43
64
  :powerdns_postgresql_connection: 'host=localhost user=powerdns password=mypassword dbname=powerdns'
44
65
 
45
- ### DNSSEC
66
+ ### DNSSEC with MySQL and PostgreSQL
46
67
 
47
- In case you've enabled DNSSEC (as you should), a rectify-zone is required after every zone change. The pdnssec command is configurable:
68
+ In case you've enabled DNSSEC (as you should), all database backends require a rectify-zone after every zone change. The REST backend ignores this setting. The pdnssec command is configurable:
48
69
 
49
70
  :powerdns_pdnssec: 'pdnssec'
50
71
 
@@ -52,6 +73,8 @@ Or a more complex example:
52
73
 
53
74
  :powerdns_pdnssec: 'sudo pdnssec --config-name=myconfig'
54
75
 
76
+ Note that PowerDNS 4.x now uses `pdnsutil` rather than `pdnssec`.
77
+
55
78
  ## Contributing
56
79
 
57
80
  Fork and send a Pull Request. Thanks!
@@ -1,7 +1,7 @@
1
1
  module Proxy::Dns::Powerdns::Backend
2
2
  class Dummy < ::Proxy::Dns::Powerdns::Record
3
3
 
4
- def initialize(a_server = nil, a_ttl = nil)
4
+ def initialize(a_server, a_ttl)
5
5
  super(a_server, a_ttl)
6
6
  end
7
7
 
@@ -12,7 +12,7 @@ module Proxy::Dns::Powerdns::Backend
12
12
  }
13
13
  end
14
14
 
15
- def create_record domain_id, name, ttl, content, type
15
+ def create_record domain_id, name, content, type
16
16
  false
17
17
  end
18
18
 
@@ -5,13 +5,13 @@ module Proxy::Dns::Powerdns::Backend
5
5
 
6
6
  attr_reader :hostname, :username, :password, :database
7
7
 
8
- def initialize(a_server = nil, a_ttl = nil)
9
- @hostname = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_hostname || 'localhost'
10
- @username = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_username
11
- @password = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_password
12
- @database = Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_database
8
+ def initialize(a_server, a_ttl, pdnssec, hostname, username, password, database)
9
+ @hostname = hostname
10
+ @username = username
11
+ @password = password
12
+ @database = database
13
13
 
14
- super(a_server, a_ttl)
14
+ super(a_server, a_ttl, pdnssec)
15
15
  end
16
16
 
17
17
  def connection
@@ -43,7 +43,7 @@ module Proxy::Dns::Powerdns::Backend
43
43
  name = connection.escape(name)
44
44
  type = connection.escape(type)
45
45
  connection.query("DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{name}' AND type='#{type}'")
46
- connection.affected_rows == 1
46
+ connection.affected_rows >= 1
47
47
  end
48
48
  end
49
49
  end
@@ -5,10 +5,10 @@ module Proxy::Dns::Powerdns::Backend
5
5
 
6
6
  attr_reader :connection_str
7
7
 
8
- def initialize(a_server = nil, a_ttl = nil)
9
- @connection_str = Proxy::Dns::Powerdns::Plugin.settings.powerdns_postgresql_connection
8
+ def initialize(a_server, a_ttl, pdnssec, connection)
9
+ @connection_str = connection
10
10
 
11
- super(a_server, a_ttl)
11
+ super(a_server, a_ttl, pdnssec)
12
12
  end
13
13
 
14
14
  def connection
@@ -36,7 +36,7 @@ module Proxy::Dns::Powerdns::Backend
36
36
 
37
37
  def delete_record domain_id, name, type
38
38
  result = connection.exec_params("DELETE FROM records WHERE domain_id=$1::int AND name=$2 AND type=$3", [domain_id, name, type])
39
- result.cmdtuples == 1
39
+ result.cmdtuples >= 1
40
40
  end
41
41
  end
42
42
  end
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'resolv'
4
+
5
+ module Proxy::Dns::Powerdns::Backend
6
+ extend ::Proxy::Log
7
+
8
+ class Rest < ::Proxy::Dns::Powerdns::Record
9
+
10
+ attr_reader :url, :api_key
11
+
12
+ def initialize(a_server, a_ttl, url, api_key)
13
+ @url = url
14
+ @api_key = api_key
15
+
16
+ super(a_server, a_ttl)
17
+ end
18
+
19
+ def get_zone name
20
+ fqdn = Resolv::DNS::Name.create(name)
21
+ fqdn = Resolv::DNS::Name.create(name + '.') unless fqdn.absolute?
22
+ uri = URI("#{@url}/zones")
23
+
24
+ result = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
25
+ request = Net::HTTP::Get.new uri
26
+ request['X-API-Key'] = @api_key
27
+ response = http.request request
28
+ zones = JSON.parse(response.body) rescue []
29
+
30
+ zones.select { |zone|
31
+ domain = Resolv::DNS::Name.create(zone['name'])
32
+ domain == fqdn or fqdn.subdomain_of?(domain)
33
+ }.max_by { |zone| zone['name'].length }
34
+ end
35
+
36
+ raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless result
37
+
38
+ result
39
+ end
40
+
41
+ def create_record domain_id, name, type, content
42
+ content += '.' if ['PTR', 'CNAME'].include?(type)
43
+ rrset = {
44
+ :name => name + '.',
45
+ :type => type,
46
+ :ttl => @ttl.to_i,
47
+ :changetype => :REPLACE,
48
+ :records => [
49
+ {
50
+ :content => content,
51
+ :disabled => false
52
+ }
53
+ ]
54
+ }
55
+
56
+ patch_records domain_id, rrset
57
+ end
58
+
59
+ def delete_record domain_id, name, type
60
+ rrset = {
61
+ :name => name + '.',
62
+ :type => type,
63
+ :changetype => :DELETE,
64
+ :records => []
65
+ }
66
+
67
+ patch_records domain_id, rrset
68
+ end
69
+
70
+ private
71
+
72
+ def patch_records domain_id, rrset
73
+ uri = URI("#{@url}/zones/#{domain_id}")
74
+
75
+ data = { :rrsets => [rrset] }
76
+
77
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
78
+ request = Net::HTTP::Patch.new uri
79
+ request['X-API-Key'] = @api_key
80
+ request['Content-Type'] = 'application/json'
81
+ request.body = data.to_json
82
+ response = http.request request
83
+ unless response.is_a?(Net::HTTPSuccess)
84
+ begin
85
+ content = JSON.parse(response.body)
86
+ rescue
87
+ logger.debug "Failed to pach records for #{domain_id} with '#{rrset}': #{response.body}"
88
+ raise Proxy::Dns::Error.new("Failed to patch records")
89
+ end
90
+ raise Proxy::Dns::Error.new("Failed to patch records: #{content['error']}")
91
+ end
92
+ end
93
+
94
+ true
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,64 @@
1
+ module ::Proxy::Dns::Powerdns
2
+ class PluginConfiguration
3
+ def load_classes
4
+ require 'dns_common/dns_common'
5
+ require 'smart_proxy_dns_powerdns/dns_powerdns_main'
6
+ end
7
+
8
+ def load_dependency_injection_wirings(container_instance, settings)
9
+ valid_backends = ['mysql', 'postgresql', 'rest', 'dummy']
10
+ backend = settings[:powerdns_backend]
11
+ unless valid_backends.include?(backend)
12
+ raise ::Proxy::Error::ConfigurationError.new("Invalid backend, is expected to be mysql, postgresql, rest or dummy")
13
+ end
14
+
15
+ begin
16
+ require "smart_proxy_dns_powerdns/backend/#{backend}"
17
+ rescue LoadError, e
18
+ raise ::Proxy::Error::ConfigurationError.new("Failed to load backend #{backend}: #{e}")
19
+ end
20
+
21
+ case backend
22
+ when 'mysql'
23
+ container_instance.dependency :dns_provider, (lambda do
24
+ Proxy::Dns::Powerdns::Backend::Mysql.new(
25
+ settings[:dns_server],
26
+ settings[:dns_ttl],
27
+ settings[:powerdns_pdnssec],
28
+ settings[:powerdns_mysql_hostname] || 'localhost',
29
+ settings[:powerdns_mysql_username] || 'powerdns',
30
+ settings[:powerdns_mysql_password] || '',
31
+ settings[:powerdns_mysql_database] || 'pdns',
32
+ )
33
+ end)
34
+ when 'postgresql'
35
+ container_instance.dependency :dns_provider, (lambda do
36
+ Proxy::Dns::Powerdns::Backend::Postgresql.new(
37
+ settings[:dns_server],
38
+ settings[:dns_ttl],
39
+ settings[:powerdns_pdnssec],
40
+ settings[:powerdns_postgresql_connection] || 'dbname=pdns',
41
+ )
42
+ end)
43
+ when 'rest'
44
+ raise ::Proxy::Error::ConfigurationError.new("Setting powerdns_rest_api_key must be non-empty") unless settings[:powerdns_rest_api_key]
45
+
46
+ container_instance.dependency :dns_provider, (lambda do
47
+ Proxy::Dns::Powerdns::Backend::Rest.new(
48
+ settings[:dns_server],
49
+ settings[:dns_ttl],
50
+ settings[:powerdns_rest_url] || 'http://localhost:8081/api/v1/servers/localhost',
51
+ settings[:powerdns_rest_api_key],
52
+ )
53
+ end)
54
+ when 'dummy'
55
+ container_instance.dependency :dns_provider, (lambda do
56
+ Proxy::Dns::Powerdns::Backend::Dummy.new(
57
+ settings[:dns_server],
58
+ settings[:dns_ttl],
59
+ )
60
+ end)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,6 +1,5 @@
1
1
  require 'dns/dns'
2
2
  require 'dns_common/dns_common'
3
- require 'ipaddr'
4
3
 
5
4
  module Proxy::Dns::Powerdns
6
5
  class Record < ::Proxy::Dns::Record
@@ -9,43 +8,85 @@ module Proxy::Dns::Powerdns
9
8
 
10
9
  attr_reader :pdnssec
11
10
 
12
- def initialize(a_server = nil, a_ttl = nil)
13
- @pdnssec = Proxy::Dns::Powerdns::Plugin.settings.powerdns_pdnssec
14
- super(a_server, a_ttl || Proxy::Dns::Plugin.settings.dns_ttl)
11
+ def initialize(a_server, a_ttl, pdnssec = nil)
12
+ @pdnssec = pdnssec
13
+ super(a_server, a_ttl)
15
14
  end
16
15
 
17
16
  def create_a_record(fqdn, ip)
18
- if found = dns_find(fqdn)
19
- raise Proxy::Dns::Collision, "#{fqdn} is already in use by #{ip}" unless found == ip
17
+ case a_record_conflicts(fqdn, ip)
18
+ when 1
19
+ raise(Proxy::Dns::Collision, "'#{fqdn} 'is already in use")
20
+ when 0 then
21
+ return nil
22
+ else
23
+ do_create(fqdn, ip, "A")
20
24
  end
25
+ end
21
26
 
22
- do_create(fqdn, ip, "A")
27
+ def create_aaaa_record(fqdn, ip)
28
+ case aaaa_record_conflicts(fqdn, ip)
29
+ when 1
30
+ raise(Proxy::Dns::Collision, "'#{fqdn} 'is already in use")
31
+ when 0 then
32
+ return nil
33
+ else
34
+ do_create(fqdn, ip, "AAAA")
35
+ end
23
36
  end
24
37
 
25
- def create_ptr_record(fqdn, value)
26
- if found = dns_find(value)
27
- raise Proxy::Dns::Collision, "#{value} is already in use by #{found}" unless found == fqdn
38
+ def create_cname_record(fqdn, target)
39
+ case cname_record_conflicts(fqdn, target)
40
+ when 1 then
41
+ raise(Proxy::Dns::Collision, "'#{fqdn} 'is already in use")
42
+ when 0 then
43
+ return nil
44
+ else
45
+ do_create(fqdn, target, "CNAME")
28
46
  end
47
+ end
29
48
 
30
- do_create(value, fqdn, "PTR")
49
+ def create_ptr_record(fqdn, ptr)
50
+ case ptr_record_conflicts(fqdn, ptr_to_ip(ptr))
51
+ when 1
52
+ raise(Proxy::Dns::Collision, "'#{fqdn} 'is already in use")
53
+ when 0 then
54
+ return nil
55
+ else
56
+ do_create(ptr, fqdn, "PTR")
57
+ end
31
58
  end
32
59
 
33
60
  def do_create(name, value, type)
34
61
  zone = get_zone(name)
35
- create_record(zone['id'], name, type, value) and rectify_zone(zone['name'])
62
+ unless create_record(zone['id'], name, type, value) and rectify_zone(zone['name'])
63
+ raise Proxy::Dns::Error.new("Failed to create record #{name} #{type} #{value}")
64
+ end
65
+ true
36
66
  end
37
67
 
38
68
  def remove_a_record(fqdn)
39
69
  do_remove(fqdn, "A")
40
70
  end
41
71
 
42
- def remove_ptr_record(name)
43
- do_remove(name, "PTR")
72
+ def remove_aaaa_record(fqdn)
73
+ do_remove(fqdn, "AAAA")
74
+ end
75
+
76
+ def remove_cname_record(fqdn)
77
+ do_remove(fqdn, "CNAME")
78
+ end
79
+
80
+ def remove_ptr_record(ptr)
81
+ do_remove(ptr, "PTR")
44
82
  end
45
83
 
46
84
  def do_remove(name, type)
47
85
  zone = get_zone(name)
48
- delete_record(zone['id'], name, type) and rectify_zone(zone['name'])
86
+ if delete_record(zone['id'], name, type)
87
+ raise Proxy::Dns::Error.new("Failed to remove record #{name} #{type}") unless rectify_zone(zone['name'])
88
+ end
89
+ true
49
90
  end
50
91
 
51
92
  def get_zone(fqdn)
@@ -1,19 +1,15 @@
1
1
  require 'smart_proxy_dns_powerdns/dns_powerdns_version'
2
+ require 'smart_proxy_dns_powerdns/dns_powerdns_configuration'
2
3
 
3
4
  module Proxy::Dns::Powerdns
4
5
  class Plugin < ::Proxy::Provider
5
6
  plugin :dns_powerdns, ::Proxy::Dns::Powerdns::VERSION
6
7
 
7
- requires :dns, '>= 1.11'
8
+ requires :dns, '>= 1.13'
8
9
 
9
10
  validate_presence :powerdns_backend
10
11
 
11
- after_activation do
12
- require 'smart_proxy_dns_powerdns/dns_powerdns_configuration_validator'
13
- ::Proxy::Dns::Powerdns::ConfigurationValidator.new.validate_settings!(settings)
14
-
15
- require 'smart_proxy_dns_powerdns/dns_powerdns_main'
16
- require 'smart_proxy_dns_powerdns/dependencies'
17
- end
12
+ load_classes ::Proxy::Dns::Powerdns::PluginConfiguration
13
+ load_dependency_injection_wirings ::Proxy::Dns::Powerdns::PluginConfiguration
18
14
  end
19
15
  end