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 +4 -4
- data/README.md +27 -4
- data/lib/smart_proxy_dns_powerdns/backend/dummy.rb +2 -2
- data/lib/smart_proxy_dns_powerdns/backend/mysql.rb +7 -7
- data/lib/smart_proxy_dns_powerdns/backend/postgresql.rb +4 -4
- data/lib/smart_proxy_dns_powerdns/backend/rest.rb +97 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_configuration.rb +64 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb +56 -15
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb +4 -8
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb +1 -1
- data/test/config/smart-proxy-settings.d/dns.yml +3 -0
- data/test/config/smart-proxy-settings.d/dns_powerdns.yml +4 -0
- data/test/config/smart-proxy-settings.yml +4 -0
- data/test/integration/integration_test.rb +24 -4
- data/test/test_helper.rb +3 -0
- data/test/unit/dns_powerdns_configuration_test.rb +119 -0
- data/test/unit/dns_powerdns_record_dummy_test.rb +26 -0
- data/test/unit/dns_powerdns_record_mysql_test.rb +32 -52
- data/test/unit/dns_powerdns_record_postgresql_test.rb +56 -9
- data/test/unit/dns_powerdns_record_rest_test.rb +67 -0
- data/test/unit/dns_powerdns_record_test.rb +124 -54
- metadata +20 -10
- data/lib/smart_proxy_dns_powerdns/dependencies.rb +0 -15
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_configuration_validator.rb +0 -32
- data/test/unit/dns_powerdns_configuration_validator_test.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6aef9bd6276d31444fab893bb884ccc663820cba
|
4
|
+
data.tar.gz: e17f14a05adef3c63ec53c0d8f131a3668f962dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebf9237f705434eec15aa56008f4819270e7134816433fba310da2b5daa80574a7065f249fd848b32f612dc12a87917f14f6e9eb0743751b83dfa79c16252ca7
|
7
|
+
data.tar.gz: 4aae6514df432f499f87998e9c84a0ec1b4964146073bb9b59d60770d3f058b7d0cc8636762d0c7868bde48c674d1797c474c87a54069e2c1ac15938adbcb386
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# SmartProxyDnsPowerdns
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/smart_proxy_dns_powerdns)
|
4
|
+
[](https://travis-ci.org/theforeman/smart_proxy_dns_powerdns)
|
5
|
+
[](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.
|
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
|
-
|
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
|
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
|
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,
|
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
|
9
|
-
@hostname =
|
10
|
-
@username =
|
11
|
-
@password =
|
12
|
-
@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
|
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
|
9
|
-
@connection_str =
|
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
|
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
|
13
|
-
@pdnssec =
|
14
|
-
super(a_server, a_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
|
-
|
19
|
-
|
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
|
-
|
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
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
43
|
-
do_remove(
|
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)
|
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.
|
8
|
+
requires :dns, '>= 1.13'
|
8
9
|
|
9
10
|
validate_presence :powerdns_backend
|
10
11
|
|
11
|
-
|
12
|
-
|
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
|