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 +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
|
+
[![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.
|
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
|