smart_proxy_dns_powerdns 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +47 -8
- data/lib/smart_proxy_dns_powerdns/backend/mysql.rb +12 -3
- data/lib/smart_proxy_dns_powerdns/backend/postgresql.rb +12 -3
- data/lib/smart_proxy_dns_powerdns/backend/rest.rb +1 -1
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb +18 -68
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb +1 -1
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb +1 -1
- data/test/unit/dns_powerdns_record_mysql_test.rb +63 -6
- data/test/unit/dns_powerdns_record_postgresql_test.rb +71 -13
- data/test/unit/dns_powerdns_record_test.rb +28 -141
- data/test/unit/internal_api_test.rb +63 -0
- metadata +9 -14
- data/test/config/smart-proxy-settings.d/dns.yml +0 -3
- data/test/config/smart-proxy-settings.d/dns_powerdns.yml +0 -4
- data/test/config/smart-proxy-settings.yml +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91fabd73d9aa9962f5117f82375dbf50ebd42f1535ab1162b18f5af6eccb20a1
|
4
|
+
data.tar.gz: ebcaed367e7e65a911d8711e881ff9471e7ef731015cf4c7935ba330b8f464ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49e9f311cc142d0daf1ff414d715c52950e4ff3d599c267fae33932648aa8578fbd2b1ce8e298c4d4425e7d60e6dcc69b9c63a98d3fff2c3107558914af87c53
|
7
|
+
data.tar.gz: d21fc640d1f82569fa90718912ebca748c17a66caef9ba8966cb1c1e65a01179a0eea692e9f4f727e215577bb1143b58ad32a49f82b6cb2086b2573a9571fbee
|
data/README.md
CHANGED
@@ -8,10 +8,10 @@ This plugin adds a new DNS provider for managing records in PowerDNS.
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
See [
|
11
|
+
See [How\_to\_Install\_a\_Smart-Proxy\_Plugin](https://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
|
12
12
|
for how to install Smart Proxy plugins
|
13
13
|
|
14
|
-
This plugin is compatible with Smart Proxy 1.
|
14
|
+
This plugin is compatible with Smart Proxy 1.15 or higher.
|
15
15
|
|
16
16
|
When installing using "gem", make sure to install the bundle file:
|
17
17
|
|
@@ -19,6 +19,11 @@ When installing using "gem", make sure to install the bundle file:
|
|
19
19
|
|
20
20
|
## Upgrading
|
21
21
|
|
22
|
+
### 0.4.0
|
23
|
+
|
24
|
+
* The minimum Smart Proxy version is now 1.15
|
25
|
+
* The MySQL and PostgreSQL backends are officially deprecated and will be removed in the next release.
|
26
|
+
|
22
27
|
### 0.3.0
|
23
28
|
|
24
29
|
* The minimum Smart Proxy version is now 1.13
|
@@ -44,7 +49,11 @@ To use the REST backend, set the following parameters:
|
|
44
49
|
:powerdns_rest_url: 'http://localhost:8081/api/v1/servers/localhost'
|
45
50
|
:powerdns_rest_api_key: 'apikey'
|
46
51
|
|
47
|
-
Note
|
52
|
+
**Note** only API v1 from PowerDNS 4.x is supported. The v0 API from 3.x is unsupported.
|
53
|
+
|
54
|
+
### DNSSEC with REST
|
55
|
+
|
56
|
+
Domains in PowerDNS need a rectify action after modification. In the past this was done using pdnsutil (which can still be set) but since PowerDNS 4.1.0 the API can do this automatically. The [domain metadata API-RECTIFY](https://doc.powerdns.com/authoritative/domainmetadata.html#metadata-api-rectify) needs to be set to `1`. When it's unset, the config variable [default-api-rectify](https://doc.powerdns.com/authoritative/settings.html#setting-default-api-rectify) will be used. PowerDNS 4.2.0 started to default to true. When this is used, the value for `:powerdns_pdnssec` in this plugin should be empty (default).
|
48
57
|
|
49
58
|
### MySQL
|
50
59
|
|
@@ -56,6 +65,8 @@ To use MySQL, set the following parameters:
|
|
56
65
|
:powerdns_mysql_password: ''
|
57
66
|
:powerdns_mysql_database: 'powerdns'
|
58
67
|
|
68
|
+
**Note** use of this backend is deprecated. REST should be used.
|
69
|
+
|
59
70
|
### PostgreSQL
|
60
71
|
|
61
72
|
To use PostgreSQL, set the following parameters:
|
@@ -63,17 +74,45 @@ To use PostgreSQL, set the following parameters:
|
|
63
74
|
:powerdns_backend: 'postgresql'
|
64
75
|
:powerdns_postgresql_connection: 'host=localhost user=powerdns password=mypassword dbname=powerdns'
|
65
76
|
|
77
|
+
**Note** use of this backend is deprecated. REST should be used.
|
78
|
+
|
66
79
|
### DNSSEC with MySQL and PostgreSQL
|
67
80
|
|
68
81
|
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:
|
69
82
|
|
70
|
-
:powerdns_pdnssec: '
|
83
|
+
:powerdns_pdnssec: 'pdnsutil'
|
71
84
|
|
72
85
|
Or a more complex example:
|
73
86
|
|
74
|
-
:powerdns_pdnssec: 'sudo
|
87
|
+
:powerdns_pdnssec: 'sudo pdnsutil --config-name=myconfig'
|
88
|
+
|
89
|
+
Note that PowerDNS 3.x used `pdnssec` rather than `pdnsutil` which explains the naming of the option.
|
90
|
+
|
91
|
+
### SOA autoserial with MySQL and PostgreSQL
|
92
|
+
|
93
|
+
PowerDNS (>= 3.3) provides a feature called `autoserial` that takes care of managing the serial of `SOA` records.
|
94
|
+
|
95
|
+
There are many options available regarding how PowerDNS generates the serial and details can be found looking for the `SOA-EDIT` option in PowerDNS.
|
96
|
+
|
97
|
+
One option is to let the PowerDNS backend determine the `SOA` serial using the biggest `change_date` of the records associated with the DNS domain.
|
98
|
+
`smart_proxy_dns_powerdns` uses this approach and updates the `change_date` field of changed records, setting them to the current timestamp of the database server, represented as **the number of seconds since EPOCH**.
|
99
|
+
|
100
|
+
* when a new record is created, its `change_date` is set accordingly
|
101
|
+
* when a record is deleted, the `change_date` of the `SOA` record for the domain is updated
|
102
|
+
|
103
|
+
### Updating the SOA serial when using the REST backend
|
104
|
+
|
105
|
+
When using the REST backend, the `change_date` of records isn't modified by this plugin. To automatically increment the serial number of a zone, you can configure the [SOA-EDIT-API](https://doc.powerdns.com/authoritative/domainmetadata.html#soa-edit-api) zone metadata. For example:
|
106
|
+
|
107
|
+
```shell
|
108
|
+
pdnsutil set-meta example.com SOA-EDIT-API DEFAULT
|
109
|
+
```
|
110
|
+
|
111
|
+
Other methods for managing the serial number are also available. Alternatives to `SOA-EDIT-API` you might want to investigate include:
|
112
|
+
* Installing database triggers that update the SOA record.
|
113
|
+
* Reconfiguring powerdns's prepared statements such that the `change\_date` column gets updated when records are updated.
|
75
114
|
|
76
|
-
|
115
|
+
Full discussion of these methods is beyond the scope of this README.
|
77
116
|
|
78
117
|
## Contributing
|
79
118
|
|
@@ -94,7 +133,7 @@ Then run the tests:
|
|
94
133
|
|
95
134
|
## Copyright
|
96
135
|
|
97
|
-
Copyright (c) 2015 -
|
136
|
+
Copyright (c) 2015 - 2019 Ewoud Kohl van Wijngaarden
|
98
137
|
|
99
138
|
This program is free software: you can redistribute it and/or modify
|
100
139
|
it under the terms of the GNU General Public License as published by
|
@@ -107,5 +146,5 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
107
146
|
GNU General Public License for more details.
|
108
147
|
|
109
148
|
You should have received a copy of the GNU General Public License
|
110
|
-
along with this program. If not, see <
|
149
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
111
150
|
|
@@ -26,7 +26,7 @@ module Proxy::Dns::Powerdns::Backend
|
|
26
26
|
domain = row
|
27
27
|
end
|
28
28
|
|
29
|
-
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless domain
|
29
|
+
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless domain
|
30
30
|
|
31
31
|
domain
|
32
32
|
end
|
@@ -35,7 +35,7 @@ module Proxy::Dns::Powerdns::Backend
|
|
35
35
|
name = connection.escape(name)
|
36
36
|
content = connection.escape(content)
|
37
37
|
type = connection.escape(type)
|
38
|
-
connection.query("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}')")
|
38
|
+
connection.query("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}', UNIX_TIMESTAMP())")
|
39
39
|
connection.affected_rows == 1
|
40
40
|
end
|
41
41
|
|
@@ -43,7 +43,16 @@ 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
|
+
return false if connection.affected_rows == 0
|
47
|
+
|
48
|
+
connection.query("UPDATE records SET change_date=UNIX_TIMESTAMP() WHERE domain_id=#{domain_id} AND type='SOA'")
|
49
|
+
affected_rows = connection.affected_rows
|
50
|
+
if affected_rows > 1
|
51
|
+
logger.warning("Updated multiple SOA records (host=#{name}, domain_id=#{domain_id}). Check your zone records for duplicate SOA entries.")
|
52
|
+
elsif affected_rows == 0
|
53
|
+
logger.info("No SOA record updated (host=#{name}, domain_id=#{domain_id}). This can be caused by either a missing SOA record for the zone or consecutive updates of the same zone during the same second.")
|
54
|
+
end
|
55
|
+
true
|
47
56
|
end
|
48
57
|
end
|
49
58
|
end
|
@@ -24,19 +24,28 @@ module Proxy::Dns::Powerdns::Backend
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless domain
|
27
|
+
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless domain
|
28
28
|
|
29
29
|
domain
|
30
30
|
end
|
31
31
|
|
32
32
|
def create_record domain_id, name, type, content
|
33
|
-
result = connection.exec_params("INSERT INTO records (domain_id, name, ttl, content, type) VALUES ($1::int, $2, $3::int, $4, $5)", [domain_id, name, ttl, content, type])
|
33
|
+
result = connection.exec_params("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES ($1::int, $2, $3::int, $4, $5, extract(epoch from now()))", [domain_id, name, ttl, content, type])
|
34
34
|
result.cmdtuples == 1
|
35
35
|
end
|
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
|
+
return false if result.cmdtuples == 0
|
40
|
+
|
41
|
+
result = connection.exec_params("UPDATE records SET change_date=extract(epoch from now()) WHERE domain_id=$1::int AND type='SOA'", [domain_id])
|
42
|
+
affected_rows = result.cmdtuples
|
43
|
+
if affected_rows > 1
|
44
|
+
logger.warning("Updated multiple SOA records (host=#{name}, domain_id=#{domain_id}). Check your zone records for duplicate SOA entries.")
|
45
|
+
elsif affected_rows == 0
|
46
|
+
logger.info("No SOA record updated (host=#{name}, domain_id=#{domain_id}). This can be caused by either a missing SOA record for the zone or consecutive updates of the same zone during the same second.")
|
47
|
+
end
|
48
|
+
true
|
40
49
|
end
|
41
50
|
end
|
42
51
|
end
|
@@ -33,7 +33,7 @@ module Proxy::Dns::Powerdns::Backend
|
|
33
33
|
}.max_by { |zone| zone['name'].length }
|
34
34
|
end
|
35
35
|
|
36
|
-
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless result
|
36
|
+
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS." unless result
|
37
37
|
|
38
38
|
result
|
39
39
|
end
|
@@ -13,85 +13,28 @@ module Proxy::Dns::Powerdns
|
|
13
13
|
super(a_server, a_ttl)
|
14
14
|
end
|
15
15
|
|
16
|
-
def create_a_record(fqdn, 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")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
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
|
36
|
-
end
|
37
|
-
|
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")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
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
|
58
|
-
end
|
59
|
-
|
60
16
|
def do_create(name, value, type)
|
61
17
|
zone = get_zone(name)
|
62
|
-
|
63
|
-
raise Proxy::Dns::Error.new("Failed to
|
18
|
+
if create_record(zone['id'], name, type, value)
|
19
|
+
raise Proxy::Dns::Error.new("Failed to rectify zone #{zone['name']}") unless rectify_zone(zone['name'])
|
20
|
+
else
|
21
|
+
raise Proxy::Dns::Error.new("Failed to insert record #{name} #{type} #{value}")
|
64
22
|
end
|
65
23
|
true
|
66
24
|
end
|
67
25
|
|
68
|
-
def remove_a_record(fqdn)
|
69
|
-
do_remove(fqdn, "A")
|
70
|
-
end
|
71
|
-
|
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")
|
82
|
-
end
|
83
|
-
|
84
26
|
def do_remove(name, type)
|
85
27
|
zone = get_zone(name)
|
86
28
|
if delete_record(zone['id'], name, type)
|
87
|
-
raise Proxy::Dns::Error.new("Failed to
|
29
|
+
raise Proxy::Dns::Error.new("Failed to rectify zone #{name}") unless rectify_zone(zone['name'])
|
88
30
|
end
|
89
31
|
true
|
90
32
|
end
|
91
33
|
|
92
|
-
|
34
|
+
# :nocov:
|
35
|
+
def get_zone(name)
|
93
36
|
# TODO: backend specific
|
94
|
-
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS."
|
37
|
+
raise Proxy::Dns::Error, "Unable to determine zone for #{name}. Zone must exist in PowerDNS."
|
95
38
|
end
|
96
39
|
|
97
40
|
def create_record(domain_id, name, type, content)
|
@@ -103,12 +46,19 @@ module Proxy::Dns::Powerdns
|
|
103
46
|
# TODO: backend specific
|
104
47
|
false
|
105
48
|
end
|
49
|
+
# :nocov:
|
106
50
|
|
107
51
|
def rectify_zone domain
|
108
52
|
if @pdnssec
|
109
|
-
|
110
|
-
|
111
|
-
|
53
|
+
logger.debug("running: #{@pdnssec} rectify-zone \"#{domain}\"")
|
54
|
+
pdnsout = %x(#{@pdnssec} rectify-zone "#{domain}" 2>&1)
|
55
|
+
|
56
|
+
if $?.exitstatus != 0
|
57
|
+
logger.debug("#{@pdnssec} (exit: #{$?.exitstatus}) says: #{pdnsout}")
|
58
|
+
false
|
59
|
+
else
|
60
|
+
true
|
61
|
+
end
|
112
62
|
else
|
113
63
|
true
|
114
64
|
end
|
@@ -39,19 +39,76 @@ class DnsPowerdnsBackendMysqlTest < Test::Unit::TestCase
|
|
39
39
|
@connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
40
40
|
@connection.expects(:escape).with('A').returns('A')
|
41
41
|
@connection.expects(:escape).with('10.1.1.1').returns('10.1.1.1')
|
42
|
-
@connection.expects(:query).with("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (1, 'test.example.com', 86400, '10.1.1.1', 'A')")
|
42
|
+
@connection.expects(:query).with("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES (1, 'test.example.com', 86400, '10.1.1.1', 'A', UNIX_TIMESTAMP())")
|
43
43
|
@connection.expects(:affected_rows).returns(1)
|
44
44
|
|
45
45
|
assert @provider.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
46
46
|
end
|
47
47
|
|
48
48
|
def test_delete_record
|
49
|
-
|
50
|
-
@connection.expects(:
|
51
|
-
@connection.expects(:query).with(
|
52
|
-
@connection.expects(:affected_rows).returns(1)
|
49
|
+
mock_escapes(fqdn, 'A')
|
50
|
+
@connection.expects(:query).with(query_delete)
|
51
|
+
@connection.expects(:query).with(query_update_soa)
|
52
|
+
@connection.expects(:affected_rows).twice.returns(1)
|
53
|
+
assert @provider.delete_record(domain_id, fqdn, 'A')
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_delete_no_record
|
57
|
+
mock_escapes(fqdn, 'A')
|
58
|
+
@connection.expects(:query).with(query_delete)
|
59
|
+
@connection.expects(:affected_rows).returns(0)
|
60
|
+
|
61
|
+
assert_false @provider.delete_record(domain_id, fqdn, 'A')
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_delete_record_no_soa
|
65
|
+
mock_escapes(fqdn, 'A')
|
66
|
+
@connection.expects(:query).with(query_delete)
|
67
|
+
@connection.expects(:query).with(query_update_soa)
|
68
|
+
@connection.expects(:affected_rows).twice.returns(1, 0)
|
69
|
+
logger = mock()
|
70
|
+
logger.expects(:info)
|
71
|
+
@provider.stubs(:logger).returns(logger)
|
72
|
+
|
73
|
+
assert @provider.delete_record(domain_id, fqdn, 'A')
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_delete_record_multiple_soa
|
77
|
+
mock_escapes(fqdn, 'A')
|
78
|
+
@connection.expects(:query).with(query_delete)
|
79
|
+
@connection.expects(:query).with(query_update_soa)
|
80
|
+
@connection.expects(:affected_rows).twice.returns(1, 2)
|
81
|
+
logger = mock()
|
82
|
+
logger.expects(:warning)
|
83
|
+
@provider.stubs(:logger).returns(logger)
|
84
|
+
|
85
|
+
assert @provider.delete_record(domain_id, fqdn, 'A')
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def mock_escapes(*elts)
|
91
|
+
elts.each { |e| @connection.expects(:escape).with(e).returns(e) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def domain
|
95
|
+
'example.com'
|
96
|
+
end
|
97
|
+
|
98
|
+
def fqdn
|
99
|
+
"test.#{domain}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def domain_id
|
103
|
+
1
|
104
|
+
end
|
105
|
+
|
106
|
+
def query_delete(type='A')
|
107
|
+
"DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{fqdn}' AND type='#{type}'"
|
108
|
+
end
|
53
109
|
|
54
|
-
|
110
|
+
def query_update_soa
|
111
|
+
"UPDATE records SET change_date=UNIX_TIMESTAMP() WHERE domain_id=#{domain_id} AND type='SOA'"
|
55
112
|
end
|
56
113
|
|
57
114
|
end
|
@@ -35,33 +35,91 @@ class DnsPowerdnsBackendPostgresqlTest < Test::Unit::TestCase
|
|
35
35
|
|
36
36
|
def test_create_record
|
37
37
|
@connection.expects(:exec_params).
|
38
|
-
with("INSERT INTO records (domain_id, name, ttl, content, type) VALUES ($1::int, $2, $3::int, $4, $5)", [1, 'test.example.com', 86400, '10.1.1.1', 'A']).
|
38
|
+
with("INSERT INTO records (domain_id, name, ttl, content, type, change_date) VALUES ($1::int, $2, $3::int, $4, $5, extract(epoch from now()))", [1, 'test.example.com', 86400, '10.1.1.1', 'A']).
|
39
39
|
returns(mock(:cmdtuples => 1))
|
40
40
|
|
41
41
|
assert_true @provider.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
42
42
|
end
|
43
43
|
|
44
44
|
def test_delete_record_no_records
|
45
|
-
|
46
|
-
|
47
|
-
returns(mock(:cmdtuples => 0))
|
48
|
-
|
49
|
-
assert_false @provider.delete_record(1, 'test.example.com', 'A')
|
45
|
+
mock_delete_tuples(0)
|
46
|
+
assert_false run_delete_record
|
50
47
|
end
|
51
48
|
|
52
49
|
def test_delete_record_single_record
|
53
|
-
|
54
|
-
|
55
|
-
returns(mock(:cmdtuples => 1))
|
50
|
+
mock_delete_tuples(1)
|
51
|
+
mock_update_soa_tuples(1)
|
56
52
|
|
57
|
-
assert_true
|
53
|
+
assert_true run_delete_record
|
58
54
|
end
|
59
55
|
|
60
56
|
def test_delete_record_multiple_records
|
57
|
+
mock_delete_tuples(2)
|
58
|
+
mock_update_soa_tuples(1)
|
59
|
+
|
60
|
+
assert_true run_delete_record
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_delete_record_no_soa
|
64
|
+
mock_delete_tuples(1)
|
65
|
+
mock_update_soa_tuples(0)
|
66
|
+
logger = mock()
|
67
|
+
logger.expects(:info)
|
68
|
+
@provider.stubs(:logger).returns(logger)
|
69
|
+
|
70
|
+
assert_true run_delete_record
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_delete_record_multiple_soa
|
74
|
+
mock_delete_tuples(1)
|
75
|
+
mock_update_soa_tuples(2)
|
76
|
+
logger = mock()
|
77
|
+
logger.expects(:warning)
|
78
|
+
@provider.stubs(:logger).returns(logger)
|
79
|
+
|
80
|
+
assert_true run_delete_record
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def mock_delete_tuples(cmdtuples)
|
61
86
|
@connection.expects(:exec_params).
|
62
|
-
with(
|
63
|
-
returns(mock(:cmdtuples =>
|
87
|
+
with(query_delete, [domain_id, fqdn, record_type]).
|
88
|
+
returns(mock(:cmdtuples => cmdtuples))
|
89
|
+
end
|
64
90
|
|
65
|
-
|
91
|
+
def mock_update_soa_tuples(cmdtuples)
|
92
|
+
@connection.expects(:exec_params).
|
93
|
+
with(query_update_soa, [domain_id]).
|
94
|
+
returns(mock(:cmdtuples => cmdtuples))
|
66
95
|
end
|
96
|
+
|
97
|
+
def run_delete_record
|
98
|
+
@provider.delete_record(domain_id, fqdn, record_type)
|
99
|
+
end
|
100
|
+
|
101
|
+
def domain
|
102
|
+
'example.com'
|
103
|
+
end
|
104
|
+
|
105
|
+
def fqdn
|
106
|
+
"test.#{domain}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def domain_id
|
110
|
+
1
|
111
|
+
end
|
112
|
+
|
113
|
+
def record_type
|
114
|
+
'A'
|
115
|
+
end
|
116
|
+
|
117
|
+
def query_delete
|
118
|
+
"DELETE FROM records WHERE domain_id=$1::int AND name=$2 AND type=$3"
|
119
|
+
end
|
120
|
+
|
121
|
+
def query_update_soa
|
122
|
+
"UPDATE records SET change_date=extract(epoch from now()) WHERE domain_id=$1::int AND type='SOA'"
|
123
|
+
end
|
124
|
+
|
67
125
|
end
|
@@ -4,153 +4,39 @@ require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
|
4
4
|
|
5
5
|
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400, '
|
7
|
+
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400, 'echo pdnssec')
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_initialize
|
11
11
|
assert_equal 86400, @provider.ttl
|
12
|
-
assert_equal '
|
12
|
+
assert_equal 'echo pdnssec', @provider.pdnssec
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
def test_create_a
|
17
|
-
@provider.expects(:a_record_conflicts).with('test.example.com', '10.1.1.1').returns(-1)
|
15
|
+
def test_do_create_success
|
18
16
|
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
19
17
|
@provider.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
20
18
|
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
21
19
|
|
22
|
-
assert @provider.
|
23
|
-
end
|
24
|
-
|
25
|
-
# Test A record creation does nothing if the same record exists
|
26
|
-
def test_create_a_duplicate
|
27
|
-
@provider.expects(:a_record_conflicts).with('test.example.com', '10.1.1.1').returns(0)
|
28
|
-
|
29
|
-
assert_equal nil, @provider.create_a_record(fqdn, ipv4)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Test A record creation fails if the record exists
|
33
|
-
def test_create_a_conflict
|
34
|
-
@provider.expects(:a_record_conflicts).with('test.example.com', '10.1.1.1').returns(1)
|
35
|
-
|
36
|
-
assert_raise(Proxy::Dns::Collision) { @provider.create_a_record(fqdn, ipv4) }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Test AAAA record creation
|
40
|
-
def test_create_aaaa
|
41
|
-
@provider.expects(:aaaa_record_conflicts).with('test.example.com', '2001:db8:1234:abcd::1').returns(-1)
|
42
|
-
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
43
|
-
@provider.expects(:create_record).with(1, 'test.example.com', 'AAAA', '2001:db8:1234:abcd::1').returns(true)
|
44
|
-
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
45
|
-
|
46
|
-
assert @provider.create_aaaa_record(fqdn, ipv6)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Test AAAA record creation does nothing if the same record exists
|
50
|
-
def test_create_aaaa_duplicate
|
51
|
-
@provider.expects(:aaaa_record_conflicts).with('test.example.com', '2001:db8:1234:abcd::1').returns(0)
|
52
|
-
|
53
|
-
assert_equal nil, @provider.create_aaaa_record(fqdn, ipv6)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Test AAAA record creation fails if the record exists
|
57
|
-
def test_create_aaaa_conflict
|
58
|
-
@provider.expects(:aaaa_record_conflicts).with('test.example.com', '2001:db8:1234:abcd::1').returns(1)
|
59
|
-
|
60
|
-
assert_raise(Proxy::Dns::Collision) { @provider.create_aaaa_record(fqdn, ipv6) }
|
61
|
-
end
|
62
|
-
|
63
|
-
# Test CNAME record creation
|
64
|
-
def test_create_cname
|
65
|
-
@provider.expects(:cname_record_conflicts).with('test.example.com', 'something.example.com').returns(-1)
|
66
|
-
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
67
|
-
@provider.expects(:create_record).with(1, 'test.example.com', 'CNAME', 'something.example.com').returns(true)
|
68
|
-
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
69
|
-
|
70
|
-
assert @provider.create_cname_record(fqdn, 'something.example.com')
|
71
|
-
end
|
72
|
-
|
73
|
-
# Test CNAME record creation does nothing if the same record exists
|
74
|
-
def test_create_cname_duplicate
|
75
|
-
@provider.expects(:cname_record_conflicts).with('test.example.com', 'something.example.com').returns(0)
|
76
|
-
|
77
|
-
assert_equal nil, @provider.create_cname_record(fqdn, 'something.example.com')
|
78
|
-
end
|
79
|
-
|
80
|
-
# Test CNAME record creation fails if the record exists
|
81
|
-
def test_create_cname_conflict
|
82
|
-
@provider.expects(:cname_record_conflicts).with('test.example.com', 'something.example.com').returns(1)
|
83
|
-
|
84
|
-
assert_raise(Proxy::Dns::Collision) { @provider.create_cname_record(fqdn, 'something.example.com') }
|
85
|
-
end
|
86
|
-
|
87
|
-
# Test PTR record creation
|
88
|
-
def test_create_ptr
|
89
|
-
@provider.expects(:ptr_record_conflicts).with('test.example.com', '10.1.1.1').returns(-1)
|
90
|
-
@provider.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
91
|
-
@provider.expects(:create_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR', 'test.example.com').returns(true)
|
92
|
-
@provider.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
93
|
-
|
94
|
-
assert @provider.create_ptr_record(fqdn, reverse_ipv4)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Test PTR record creation does nothing if the same record exists
|
98
|
-
def test_create_ptr_duplicate
|
99
|
-
@provider.expects(:ptr_record_conflicts).with('test.example.com', '10.1.1.1').returns(0)
|
100
|
-
|
101
|
-
assert_equal nil, @provider.create_ptr_record(fqdn, reverse_ipv4)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Test PTR record creation fails if the record exists
|
105
|
-
def test_create_ptr_conflict
|
106
|
-
@provider.expects(:ptr_record_conflicts).with('test.example.com', '10.1.1.1').returns(1)
|
107
|
-
|
108
|
-
assert_raise(Proxy::Dns::Collision) { @provider.create_ptr_record(fqdn, reverse_ipv4) }
|
109
|
-
end
|
110
|
-
|
111
|
-
# Test PTR record creation
|
112
|
-
def test_create_ptr_ipv6
|
113
|
-
@provider.expects(:ptr_record_conflicts).with('test.example.com', '2001:0db8:1234:abcd:0000:0000:0000:0001').returns(-1)
|
114
|
-
@provider.expects(:get_zone).with('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa').returns({'id' => 1, 'name' => 'd.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa'})
|
115
|
-
@provider.expects(:create_record).with(1, '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa', 'PTR', 'test.example.com').returns(true)
|
116
|
-
@provider.expects(:rectify_zone).with('d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa').returns(true)
|
117
|
-
|
118
|
-
assert @provider.create_ptr_record(fqdn, reverse_ipv6)
|
20
|
+
assert @provider.do_create('test.example.com', '10.1.1.1', 'A')
|
119
21
|
end
|
120
22
|
|
121
|
-
|
122
|
-
def test_remove_a
|
23
|
+
def test_do_create_failure_in_create
|
123
24
|
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
124
|
-
@provider.expects(:
|
125
|
-
@provider.expects(:rectify_zone).with('example.com').returns(true)
|
126
|
-
|
127
|
-
assert @provider.remove_a_record(fqdn)
|
128
|
-
end
|
129
|
-
|
130
|
-
# Test PTR record removal
|
131
|
-
def test_remove_ptr_ipv4
|
132
|
-
@provider.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
133
|
-
@provider.expects(:delete_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR').returns(true)
|
134
|
-
@provider.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
135
|
-
|
136
|
-
assert @provider.remove_ptr_record(reverse_ipv4)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Test PTR record removal
|
140
|
-
def test_remove_ptr_ipv6
|
141
|
-
@provider.expects(:get_zone).with('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa').returns({'id' => 1, 'name' => 'd.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa'})
|
142
|
-
@provider.expects(:delete_record).with(1, '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa', 'PTR').returns(true)
|
143
|
-
@provider.expects(:rectify_zone).with('d.c.b.a.4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa').returns(true)
|
25
|
+
@provider.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(false)
|
144
26
|
|
145
|
-
|
27
|
+
assert_raise(Proxy::Dns::Error) do
|
28
|
+
@provider.do_create('test.example.com', '10.1.1.1', 'A')
|
29
|
+
end
|
146
30
|
end
|
147
31
|
|
148
|
-
def
|
32
|
+
def test_do_create_failure_in_rectify
|
149
33
|
@provider.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
150
34
|
@provider.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
151
|
-
@provider.expects(:rectify_zone).with('example.com').returns(
|
35
|
+
@provider.expects(:rectify_zone).with('example.com').returns(false)
|
152
36
|
|
153
|
-
|
37
|
+
assert_raise(Proxy::Dns::Error) do
|
38
|
+
@provider.do_create('test.example.com', '10.1.1.1', 'A')
|
39
|
+
end
|
154
40
|
end
|
155
41
|
|
156
42
|
def test_do_remove
|
@@ -161,25 +47,26 @@ class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
|
161
47
|
assert @provider.do_remove('test.example.com', 'A')
|
162
48
|
end
|
163
49
|
|
164
|
-
|
50
|
+
def test_rectify_zone_success
|
51
|
+
@provider.logger.expects(:debug).with('running: echo pdnssec rectify-zone "example.com"')
|
165
52
|
|
166
|
-
|
167
|
-
'test.example.com'
|
53
|
+
assert_true @provider.rectify_zone 'example.com'
|
168
54
|
end
|
169
55
|
|
170
|
-
def
|
171
|
-
|
172
|
-
end
|
56
|
+
def test_rectify_zone_failure
|
57
|
+
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400, 'false')
|
173
58
|
|
174
|
-
|
175
|
-
|
176
|
-
end
|
59
|
+
@provider.logger.expects(:debug).with('running: false rectify-zone "example.com"')
|
60
|
+
@provider.logger.expects(:debug).with('false (exit: 1) says: ')
|
177
61
|
|
178
|
-
|
179
|
-
'2001:db8:1234:abcd::1'
|
62
|
+
assert_false @provider.rectify_zone 'example.com'
|
180
63
|
end
|
181
64
|
|
182
|
-
def
|
183
|
-
|
65
|
+
def test_rectify_zone_no_pdnssec
|
66
|
+
@provider = Proxy::Dns::Powerdns::Record.new('localhost', 86400, nil)
|
67
|
+
|
68
|
+
@provider.logger.stubs(:debug).raises(Exception)
|
69
|
+
|
70
|
+
assert_true @provider.rectify_zone 'example.com'
|
184
71
|
end
|
185
72
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'dns_common/dns_common'
|
3
|
+
require 'smart_proxy_dns_powerdns'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
require "rack/test"
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Proxy::Dns
|
9
|
+
module DependencyInjection
|
10
|
+
include Proxy::DependencyInjection::Accessors
|
11
|
+
def container_instance; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'dns/dns_api'
|
16
|
+
|
17
|
+
ENV['RACK_ENV'] = 'test'
|
18
|
+
|
19
|
+
class InternalApiTest < Test::Unit::TestCase
|
20
|
+
include Rack::Test::Methods
|
21
|
+
|
22
|
+
def app
|
23
|
+
app = Proxy::Dns::Api.new
|
24
|
+
app.helpers.server = @server
|
25
|
+
app
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup
|
29
|
+
@server = Proxy::Dns::Powerdns::Record.new('localhost', 3600)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_create_a_record
|
33
|
+
name = "test.com"
|
34
|
+
value = "192.168.33.33"
|
35
|
+
type = "A"
|
36
|
+
@server.expects(:do_create).with(name, value, type)
|
37
|
+
post '/', :fqdn => name, :value => value, :type => type
|
38
|
+
assert last_response.ok?, "Last response was not ok: #{last_response.status} #{last_response.body}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_create_ptr_record
|
42
|
+
name = "test.com"
|
43
|
+
value = "33.33.168.192.in-addr.arpa"
|
44
|
+
type = "PTR"
|
45
|
+
@server.expects(:do_create).with(value, name, type)
|
46
|
+
post '/', :fqdn => name, :value => value, :type => type
|
47
|
+
assert last_response.ok?, "Last response was not ok: #{last_response.status} #{last_response.body}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_delete_a_record
|
51
|
+
name = "test.com"
|
52
|
+
@server.expects(:do_remove).with(name, "A")
|
53
|
+
delete name
|
54
|
+
assert last_response.ok?, "Last response was not ok: #{last_response.status} #{last_response.body}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_delete_ptr_record
|
58
|
+
name = "33.33.168.192.in-addr.arpa"
|
59
|
+
@server.expects(:do_remove).with(name, "PTR")
|
60
|
+
delete name
|
61
|
+
assert last_response.ok?, "Last response was not ok: #{last_response.status} #{last_response.body}"
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dns_powerdns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ewoud Kohl van Wijngaarden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -86,9 +86,6 @@ files:
|
|
86
86
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb
|
87
87
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb
|
88
88
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb
|
89
|
-
- test/config/smart-proxy-settings.d/dns.yml
|
90
|
-
- test/config/smart-proxy-settings.d/dns_powerdns.yml
|
91
|
-
- test/config/smart-proxy-settings.yml
|
92
89
|
- test/integration/integration_test.rb
|
93
90
|
- test/test_helper.rb
|
94
91
|
- test/unit/dns_powerdns_configuration_test.rb
|
@@ -97,6 +94,7 @@ files:
|
|
97
94
|
- test/unit/dns_powerdns_record_postgresql_test.rb
|
98
95
|
- test/unit/dns_powerdns_record_rest_test.rb
|
99
96
|
- test/unit/dns_powerdns_record_test.rb
|
97
|
+
- test/unit/internal_api_test.rb
|
100
98
|
homepage: https://github.com/theforeman/smart_proxy_dns_powerdns
|
101
99
|
licenses:
|
102
100
|
- GPL-3.0
|
@@ -116,20 +114,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
114
|
- !ruby/object:Gem::Version
|
117
115
|
version: '0'
|
118
116
|
requirements: []
|
119
|
-
|
120
|
-
rubygems_version: 2.6.8
|
117
|
+
rubygems_version: 3.0.3
|
121
118
|
signing_key:
|
122
119
|
specification_version: 4
|
123
120
|
summary: PowerDNS DNS provider plugin for Foreman's smart proxy
|
124
121
|
test_files:
|
125
|
-
- test/integration/integration_test.rb
|
126
|
-
- test/unit/dns_powerdns_configuration_test.rb
|
127
|
-
- test/unit/dns_powerdns_record_mysql_test.rb
|
128
122
|
- test/unit/dns_powerdns_record_rest_test.rb
|
129
123
|
- test/unit/dns_powerdns_record_test.rb
|
130
|
-
- test/unit/
|
124
|
+
- test/unit/dns_powerdns_record_mysql_test.rb
|
125
|
+
- test/unit/dns_powerdns_configuration_test.rb
|
126
|
+
- test/unit/internal_api_test.rb
|
131
127
|
- test/unit/dns_powerdns_record_postgresql_test.rb
|
132
|
-
- test/
|
133
|
-
- test/config/smart-proxy-settings.d/dns_powerdns.yml
|
134
|
-
- test/config/smart-proxy-settings.yml
|
128
|
+
- test/unit/dns_powerdns_record_dummy_test.rb
|
135
129
|
- test/test_helper.rb
|
130
|
+
- test/integration/integration_test.rb
|