smart_proxy_dns_powerdns 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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