smart_proxy_dns_powerdns 0.1.0 → 0.2.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 +26 -8
- data/lib/smart_proxy_dns_powerdns/backend/dummy.rb +24 -0
- data/lib/smart_proxy_dns_powerdns/backend/mysql.rb +49 -0
- data/lib/smart_proxy_dns_powerdns/backend/postgresql.rb +42 -0
- data/lib/smart_proxy_dns_powerdns/dependencies.rb +15 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_configuration_validator.rb +32 -0
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb +38 -85
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb +8 -3
- data/lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb +1 -1
- data/test/integration/integration_test.rb +83 -0
- data/test/unit/dns_powerdns_configuration_validator_test.rb +78 -0
- data/test/unit/dns_powerdns_record_mysql_test.rb +77 -0
- data/test/unit/dns_powerdns_record_postgresql_test.rb +20 -0
- data/test/unit/dns_powerdns_record_test.rb +115 -0
- metadata +39 -14
- data/test/dns_powerdns_record_test.rb +0 -121
- data/test/integration_tests.py +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0bc4acefdd0c176ea6f1d9fcbb4037f36da5606
|
4
|
+
data.tar.gz: 4386be7b8b5d77463f91fd005a35735893fa64f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a4143021040bf61c3de7509fe63db0c3951cc3bc3a3f42ab8ce3f9e95777228282063801cae652de32b7e8d51e5fe9f18a61b66e5061021fc0934cf05d692b3
|
7
|
+
data.tar.gz: 6ef93f4db7399b45c31937ba71a5a5fbb208880a5ec503c4a3c82ff7255167850f78ce9a6f7951dc4beb6dc7e612105ac5531116da6be32c6a33140f26e2d237
|
data/README.md
CHANGED
@@ -7,7 +7,15 @@ This plugin adds a new DNS provider for managing records in PowerDNS.
|
|
7
7
|
See [How_to_Install_a_Smart-Proxy_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
|
8
8
|
for how to install Smart Proxy plugins
|
9
9
|
|
10
|
-
This plugin is compatible with Smart Proxy 1.
|
10
|
+
This plugin is compatible with Smart Proxy 1.11 or higher.
|
11
|
+
|
12
|
+
When installing using "gem", make sure to install the bundle file:
|
13
|
+
|
14
|
+
echo "gem 'smart_proxy_dns_powerdns'" > /usr/share/foreman-proxy/bundler.d/dns_powerdns.rb
|
15
|
+
|
16
|
+
## Upgrading
|
17
|
+
|
18
|
+
Per version 0.2.0 the backend is a required parameter.
|
11
19
|
|
12
20
|
## Configuration
|
13
21
|
|
@@ -15,13 +23,25 @@ To enable this DNS provider, edit `/etc/foreman-proxy/settings.d/dns.yml` and se
|
|
15
23
|
|
16
24
|
:use_provider: dns_powerdns
|
17
25
|
|
18
|
-
Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dns_powerdns.yml
|
26
|
+
Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dns_powerdns.yml`.
|
27
|
+
|
28
|
+
### MySQL
|
19
29
|
|
30
|
+
To use MySQL, set the following parameters:
|
31
|
+
|
32
|
+
:powerdns_backend: 'mysql'
|
20
33
|
:powerdns_mysql_hostname: 'localhost'
|
21
34
|
:powerdns_mysql_username: 'powerdns'
|
22
35
|
:powerdns_mysql_password: ''
|
23
36
|
:powerdns_mysql_database: 'powerdns'
|
24
37
|
|
38
|
+
### PostgreSQL
|
39
|
+
|
40
|
+
To use PostgreSQL, set the following parameters:
|
41
|
+
|
42
|
+
:powerdns_backend: 'postgresql'
|
43
|
+
:powerdns_postgresql_connection: 'host=localhost user=powerdns password=mypassword dbname=powerdns'
|
44
|
+
|
25
45
|
### DNSSEC
|
26
46
|
|
27
47
|
In case you've enabled DNSSEC (as you should), a rectify-zone is required after every zone change. The pdnssec command is configurable:
|
@@ -38,22 +58,20 @@ Fork and send a Pull Request. Thanks!
|
|
38
58
|
|
39
59
|
### Running the integration tests
|
40
60
|
|
41
|
-
|
42
|
-
|
43
|
-
First you need to run the smart proxy on `http://localhost:8000` and a powerdns instance on `127.0.0.1:5300` or change it in the fixtures.
|
61
|
+
First you need to run the smart proxy on `http://localhost:8000` and a powerdns instance on `127.0.0.1:5300`.
|
44
62
|
|
45
63
|
It is assumed the powerdns instance has both the `example.com` and `in-addr.arpa` domains configured. If not, create them:
|
46
64
|
|
47
65
|
INSERT INTO domains (name, type) VALUES ('example.com', 'master'), ('in-addr.arpa', 'master');
|
48
66
|
INSERT INTO records (domain_id, name, type, content) SELECT id domain_id, name, 'SOA', 'ns1.example.com hostmaster.example.com. 0 3600 1800 1209600 3600' FROM domains WHERE NOT EXISTS (SELECT 1 FROM records WHERE records.domain_id=domains.id AND records.name=domains.name AND type='SOA');
|
49
67
|
|
50
|
-
Then
|
68
|
+
Then run the tests:
|
51
69
|
|
52
|
-
|
70
|
+
bundle exec rake test:integration
|
53
71
|
|
54
72
|
## Copyright
|
55
73
|
|
56
|
-
Copyright (c) 2015 Ewoud Kohl van Wijngaarden
|
74
|
+
Copyright (c) 2015 - 2016 Ewoud Kohl van Wijngaarden
|
57
75
|
|
58
76
|
This program is free software: you can redistribute it and/or modify
|
59
77
|
it under the terms of the GNU General Public License as published by
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Proxy::Dns::Powerdns::Backend
|
2
|
+
class Dummy < ::Proxy::Dns::Powerdns::Record
|
3
|
+
|
4
|
+
def initialize(a_server = nil, a_ttl = nil)
|
5
|
+
super(a_server, a_ttl)
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_zone name
|
9
|
+
{
|
10
|
+
'id' => 1,
|
11
|
+
'name' => name.partition('.')[2]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_record domain_id, name, ttl, content, type
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete_record domain_id, name, type
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
|
3
|
+
module Proxy::Dns::Powerdns::Backend
|
4
|
+
class Mysql < ::Proxy::Dns::Powerdns::Record
|
5
|
+
|
6
|
+
attr_reader :hostname, :username, :password, :database
|
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
|
13
|
+
|
14
|
+
super(a_server, a_ttl)
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
@connection ||= Mysql2::Client.new(:host => hostname, :username => username, :password => password, :database => database)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_zone name
|
22
|
+
domain = nil
|
23
|
+
|
24
|
+
name = connection.escape(name)
|
25
|
+
connection.query("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE '#{name}' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").each do |row|
|
26
|
+
domain = row
|
27
|
+
end
|
28
|
+
|
29
|
+
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless domain
|
30
|
+
|
31
|
+
domain
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_record domain_id, name, type, content
|
35
|
+
name = connection.escape(name)
|
36
|
+
content = connection.escape(content)
|
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}')")
|
39
|
+
connection.affected_rows == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete_record domain_id, name, type
|
43
|
+
name = connection.escape(name)
|
44
|
+
type = connection.escape(type)
|
45
|
+
connection.query("DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{name}' AND type='#{type}'")
|
46
|
+
connection.affected_rows == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
module Proxy::Dns::Powerdns::Backend
|
4
|
+
class Postgresql < ::Proxy::Dns::Powerdns::Record
|
5
|
+
|
6
|
+
attr_reader :connection_str
|
7
|
+
|
8
|
+
def initialize(a_server = nil, a_ttl = nil)
|
9
|
+
@connection_str = Proxy::Dns::Powerdns::Plugin.settings.powerdns_postgresql_connection
|
10
|
+
|
11
|
+
super(a_server, a_ttl)
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection
|
15
|
+
@connection ||= PG.connect(connection_str)
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_zone name
|
19
|
+
domain = nil
|
20
|
+
|
21
|
+
connection.exec_params("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE $1 LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1", [name]) do |result|
|
22
|
+
result.each do |row|
|
23
|
+
domain = row
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS." unless domain
|
28
|
+
|
29
|
+
domain
|
30
|
+
end
|
31
|
+
|
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])
|
34
|
+
result.cmdtuples == 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete_record domain_id, name, type
|
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
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'dns_common/dependency_injection/dependencies'
|
2
|
+
|
3
|
+
class Proxy::Dns::DependencyInjection::Dependencies
|
4
|
+
case Proxy::Dns::Powerdns::Plugin.settings.powerdns_backend
|
5
|
+
when 'mysql'
|
6
|
+
require 'smart_proxy_dns_powerdns/backend/mysql'
|
7
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Mysql
|
8
|
+
when 'postgresql'
|
9
|
+
require 'smart_proxy_dns_powerdns/backend/postgresql'
|
10
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Postgresql
|
11
|
+
when 'dummy'
|
12
|
+
require 'smart_proxy_dns_powerdns/backend/dummy'
|
13
|
+
dependency :dns_provider, Proxy::Dns::Powerdns::Backend::Dummy
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
2
|
+
|
3
|
+
module Proxy::Dns::Powerdns
|
4
|
+
class ConfigurationValidator
|
5
|
+
def validate_settings!(settings)
|
6
|
+
validate_choice(settings, :powerdns_backend, ['mysql', 'postgresql', 'dummy'])
|
7
|
+
|
8
|
+
case settings.powerdns_backend
|
9
|
+
when 'mysql'
|
10
|
+
validate_presence(settings, [:powerdns_mysql_username, :powerdns_mysql_password, :powerdns_mysql_database])
|
11
|
+
when 'postgresql'
|
12
|
+
validate_presence(settings, [:powerdns_postgresql_connection])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_choice(settings, setting, choices)
|
17
|
+
value = settings.send(setting)
|
18
|
+
unless choices.include?(value)
|
19
|
+
raise ::Proxy::Error::ConfigurationError, "Parameter '#{setting}' is expected to be one of #{choices.join(",")}"
|
20
|
+
end
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_presence(settings, names)
|
25
|
+
names.each do |name|
|
26
|
+
value = settings.send(name)
|
27
|
+
raise ::Proxy::Error::ConfigurationError, "Parameter '#{name}' is expected to have a non-empty value" if value.nil? || value.to_s.empty?
|
28
|
+
end
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,120 +1,73 @@
|
|
1
1
|
require 'dns/dns'
|
2
|
+
require 'dns_common/dns_common'
|
2
3
|
require 'ipaddr'
|
3
|
-
require 'mysql2'
|
4
4
|
|
5
5
|
module Proxy::Dns::Powerdns
|
6
6
|
class Record < ::Proxy::Dns::Record
|
7
7
|
include Proxy::Log
|
8
8
|
include Proxy::Util
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :pdnssec
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
:powerdns_mysql_username => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_username,
|
16
|
-
:powerdns_mysql_password => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_password,
|
17
|
-
:powerdns_mysql_database => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_mysql_database,
|
18
|
-
:powerdns_pdnssec => ::Proxy::Dns::Powerdns::Plugin.settings.powerdns_pdnssec
|
19
|
-
))
|
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)
|
20
15
|
end
|
21
16
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
raise "dns_powerdns provider needs 'powerdns_mysql_password' option" unless options[:powerdns_mysql_password]
|
26
|
-
raise "dns_powerdns provider needs 'powerdns_mysql_database' option" unless options[:powerdns_mysql_database]
|
27
|
-
@mysql_connection = Mysql2::Client.new(
|
28
|
-
:host => options[:powerdns_mysql_hostname],
|
29
|
-
:username => options[:powerdns_mysql_username],
|
30
|
-
:password => options[:powerdns_mysql_password],
|
31
|
-
:database => options[:powerdns_mysql_database]
|
32
|
-
)
|
33
|
-
|
34
|
-
@powerdns_pdnssec = options[:powerdns_pdnssec] || false
|
35
|
-
|
36
|
-
# Normalize the somewhat weird PTR API spec to name / content
|
37
|
-
case options[:type]
|
38
|
-
when "PTR"
|
39
|
-
if options[:value] =~ /\.(in-addr|ip6)\.arpa$/
|
40
|
-
@name = options[:value]
|
41
|
-
else
|
42
|
-
@name = IPAddr.new(options[:value]).reverse
|
43
|
-
end
|
44
|
-
@content = options[:fqdn]
|
45
|
-
else
|
46
|
-
@name = options[:fqdn]
|
47
|
-
@content = options[:value]
|
17
|
+
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
|
48
20
|
end
|
49
21
|
|
50
|
-
|
22
|
+
do_create(fqdn, ip, "A")
|
51
23
|
end
|
52
24
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if ip = dns_find(domain_row['id'], @name)
|
58
|
-
raise Proxy::Dns::Collision, "#{@name} is already in use by #{ip}"
|
25
|
+
def create_ptr_record(fqdn, ip)
|
26
|
+
if found = dns_find(ip)
|
27
|
+
raise Proxy::Dns::Collision, "#{ip} is already in use by #{found}" unless found == fqdn
|
59
28
|
end
|
60
29
|
|
61
|
-
|
62
|
-
|
63
|
-
rectify_zone(domain_row['name'])
|
30
|
+
name = IPAddr.new(ip).reverse
|
31
|
+
do_create(name, fqdn, "PTR")
|
64
32
|
end
|
65
33
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
delete_record(domain_row['id'], @name, @type)
|
71
|
-
|
72
|
-
rectify_zone(domain_row['name'])
|
34
|
+
def do_create(name, value, type)
|
35
|
+
zone = get_zone(name)
|
36
|
+
create_record(zone['id'], name, type, value) and rectify_zone(zone['name'])
|
73
37
|
end
|
74
38
|
|
75
|
-
|
76
|
-
|
77
|
-
|
39
|
+
def remove_a_record(fqdn)
|
40
|
+
do_remove(fqdn, "A")
|
41
|
+
end
|
78
42
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
43
|
+
def remove_ptr_record(ip)
|
44
|
+
name = ip # Note ip is already in-addr.arpa
|
45
|
+
do_remove(name, "PTR")
|
46
|
+
end
|
83
47
|
|
84
|
-
|
48
|
+
def do_remove(name, type)
|
49
|
+
zone = get_zone(name)
|
50
|
+
delete_record(zone['id'], name, type) and rectify_zone(zone['name'])
|
85
51
|
end
|
86
52
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
key = mysql_connection.escape(key)
|
91
|
-
mysql_connection.query("SELECT content FROM records WHERE domain_id=#{domain_id} AND name = '#{key}' LIMIT 1").each do |row|
|
92
|
-
value = row["content"]
|
93
|
-
end
|
94
|
-
value || false
|
53
|
+
def get_zone(fqdn)
|
54
|
+
# TODO: backend specific
|
55
|
+
raise Proxy::Dns::Error, "Unable to determine zone. Zone must exist in PowerDNS."
|
95
56
|
end
|
96
57
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
content = mysql_connection.escape(content)
|
101
|
-
type = mysql_connection.escape(type)
|
102
|
-
mysql_connection.query("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (#{domain_id}, '#{name}', #{ttl.to_i}, '#{content}', '#{type}')")
|
103
|
-
true
|
58
|
+
def create_record(domain_id, name, type, content)
|
59
|
+
# TODO: backend specific
|
60
|
+
false
|
104
61
|
end
|
105
62
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
type = mysql_connection.escape(type)
|
110
|
-
mysql_connection.query("DELETE FROM records WHERE domain_id=#{domain_id} AND name='#{name}' AND type='#{type}'")
|
111
|
-
true
|
63
|
+
def delete_record(domain_id, name, type)
|
64
|
+
# TODO: backend specific
|
65
|
+
false
|
112
66
|
end
|
113
67
|
|
114
|
-
private
|
115
68
|
def rectify_zone domain
|
116
|
-
if @
|
117
|
-
%x(#{@
|
69
|
+
if @pdnssec
|
70
|
+
%x(#{@pdnssec} rectify-zone "#{domain}")
|
118
71
|
|
119
72
|
$?.exitstatus == 0
|
120
73
|
else
|
@@ -2,13 +2,18 @@ require 'smart_proxy_dns_powerdns/dns_powerdns_version'
|
|
2
2
|
|
3
3
|
module Proxy::Dns::Powerdns
|
4
4
|
class Plugin < ::Proxy::Provider
|
5
|
-
plugin :dns_powerdns, ::Proxy::Dns::Powerdns::VERSION
|
6
|
-
:factory => proc { |attrs| ::Proxy::Dns::Powerdns::Record.record(attrs) }
|
5
|
+
plugin :dns_powerdns, ::Proxy::Dns::Powerdns::VERSION
|
7
6
|
|
8
|
-
requires :dns, '>= 1.
|
7
|
+
requires :dns, '>= 1.11'
|
8
|
+
|
9
|
+
validate_presence :powerdns_backend
|
9
10
|
|
10
11
|
after_activation do
|
12
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_configuration_validator'
|
13
|
+
::Proxy::Dns::Powerdns::ConfigurationValidator.new.validate_settings!(settings)
|
14
|
+
|
11
15
|
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
16
|
+
require 'smart_proxy_dns_powerdns/dependencies'
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
class DnsPowerdnsIntegrationTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_forward_dns
|
9
|
+
data = {'fqdn' => fqdn, 'value' => ip, 'type' => 'A'}
|
10
|
+
|
11
|
+
uri = URI(smart_proxy_url)
|
12
|
+
|
13
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
14
|
+
request = Net::HTTP::Post.new(smart_proxy_url + 'dns/')
|
15
|
+
request.form_data = data
|
16
|
+
response = http.request request
|
17
|
+
assert_equal(200, response.code.to_i)
|
18
|
+
|
19
|
+
addresses = resolver.getaddresses(data['fqdn'])
|
20
|
+
assert_equal([Resolv::IPv4.create(data['value'])], addresses, "#{data['fqdn']} should resolve to #{data['value']}")
|
21
|
+
|
22
|
+
request = Net::HTTP::Delete.new(smart_proxy_url + 'dns/' + data['fqdn'])
|
23
|
+
response = http.request request
|
24
|
+
assert_equal(200, response.code.to_i)
|
25
|
+
|
26
|
+
assert(purge_cache data['fqdn'])
|
27
|
+
|
28
|
+
addresses = resolver.getaddresses(data['fqdn'])
|
29
|
+
assert_equal([], addresses)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_reverse_dns
|
34
|
+
data = {'fqdn' => fqdn, 'value' => ip, 'type' => 'PTR'}
|
35
|
+
|
36
|
+
uri = URI(smart_proxy_url)
|
37
|
+
|
38
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
39
|
+
request = Net::HTTP::Post.new(smart_proxy_url + 'dns/')
|
40
|
+
request.form_data = data
|
41
|
+
response = http.request request
|
42
|
+
assert_equal(200, response.code.to_i)
|
43
|
+
|
44
|
+
name = Resolv::IPv4.create(data['value']).to_name.to_s
|
45
|
+
|
46
|
+
addresses = resolver.getnames(data['value'])
|
47
|
+
assert_equal([Resolv::DNS::Name.create(data['fqdn'] + '.')], addresses, "#{data['value']} should reverse to #{data['fqdn']}")
|
48
|
+
|
49
|
+
request = Net::HTTP::Delete.new(smart_proxy_url + 'dns/' + name)
|
50
|
+
response = http.request request
|
51
|
+
assert_equal(200, response.code.to_i)
|
52
|
+
|
53
|
+
assert(purge_cache name)
|
54
|
+
|
55
|
+
addresses = resolver.getnames(data['value'])
|
56
|
+
assert_equal([], addresses)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def resolver
|
63
|
+
Resolv::DNS.new(:nameserver_port => [['127.0.0.1', 5300]])
|
64
|
+
end
|
65
|
+
|
66
|
+
def smart_proxy_url
|
67
|
+
'http://localhost:8000/'
|
68
|
+
end
|
69
|
+
|
70
|
+
def fqdn
|
71
|
+
set = ('a' .. 'z').to_a + ('0' .. '9').to_a
|
72
|
+
10.times.collect {|i| set[rand(set.size)] }.join + '.example.com'
|
73
|
+
end
|
74
|
+
|
75
|
+
def ip
|
76
|
+
IPAddr.new(rand(2 ** 32), Socket::AF_INET).to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
def purge_cache name
|
80
|
+
%x{#{ENV['PDNS_CONTROL'] || "pdns_control"} purge "#{name}"}
|
81
|
+
$? == 0
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
5
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_configuration_validator'
|
6
|
+
|
7
|
+
class DnsPowerdnsConfigurationValidatorTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@config_validator = Proxy::Dns::Powerdns::ConfigurationValidator.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_initialize_missing_backend
|
13
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => nil)
|
14
|
+
|
15
|
+
assert_raise Proxy::Error::ConfigurationError do
|
16
|
+
@config_validator.validate_settings!(settings)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_initialize_invalid_backend
|
21
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'invalid')
|
22
|
+
|
23
|
+
assert_raise Proxy::Error::ConfigurationError do
|
24
|
+
@config_validator.validate_settings!(settings)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_initialize_dummy_with_settings
|
29
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'dummy')
|
30
|
+
|
31
|
+
assert_nothing_raised do
|
32
|
+
@config_validator.validate_settings!(settings)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_initialize_mysql_without_settings
|
37
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'mysql')
|
38
|
+
|
39
|
+
assert_raise Proxy::Error::ConfigurationError do
|
40
|
+
@config_validator.validate_settings!(settings)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_initialize_mysql_with_settings
|
45
|
+
settings = OpenStruct.new(
|
46
|
+
:dns_provider => 'powerdns',
|
47
|
+
:powerdns_backend => 'mysql',
|
48
|
+
:powerdns_mysql_hostname => 'localhost',
|
49
|
+
:powerdns_mysql_username => 'username',
|
50
|
+
:powerdns_mysql_password => 'password',
|
51
|
+
:powerdns_mysql_database => 'powerdns'
|
52
|
+
)
|
53
|
+
|
54
|
+
assert_nothing_raised do
|
55
|
+
@config_validator.validate_settings!(settings)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_initialize_postgresql_without_settings
|
60
|
+
settings = OpenStruct.new(:dns_provider => 'powerdns', :powerdns_backend => 'postgresql')
|
61
|
+
|
62
|
+
assert_raise Proxy::Error::ConfigurationError do
|
63
|
+
@config_validator.validate_settings!(settings)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_initialize_postgresql_with_settings
|
68
|
+
settings = OpenStruct.new(
|
69
|
+
:dns_provider => 'powerdns',
|
70
|
+
:powerdns_backend => 'postgresql',
|
71
|
+
:powerdns_postgresql_connection => 'dbname=powerdns'
|
72
|
+
)
|
73
|
+
|
74
|
+
assert_nothing_raised do
|
75
|
+
@config_validator.validate_settings!(settings)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
require 'smart_proxy_dns_powerdns/backend/mysql'
|
6
|
+
|
7
|
+
class DnsPowerdnsBackendMysqlTest < Test::Unit::TestCase
|
8
|
+
# Test that correct initialization works
|
9
|
+
def test_initialize_dummy_with_settings
|
10
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(
|
11
|
+
:powerdns_mysql_hostname => 'db.example.com',
|
12
|
+
:powerdns_mysql_username => 'the_user',
|
13
|
+
:powerdns_mysql_password => 'something_secure',
|
14
|
+
:powerdns_mysql_database => 'db_pdns'
|
15
|
+
)
|
16
|
+
provider = klass.new
|
17
|
+
assert_equal 'db.example.com', provider.hostname
|
18
|
+
assert_equal 'the_user', provider.username
|
19
|
+
assert_equal 'something_secure', provider.password
|
20
|
+
assert_equal 'db_pdns', provider.database
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_get_zone_with_existing_zone
|
24
|
+
instance = klass.new
|
25
|
+
|
26
|
+
connection = mock()
|
27
|
+
instance.stubs(:connection).returns(connection)
|
28
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
29
|
+
connection.expects(:query).with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE 'test.example.com' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").returns([{'id' => 1, 'name' => 'example.com'}])
|
30
|
+
|
31
|
+
assert_equal(instance.get_zone('test.example.com'), {'id' => 1, 'name' => 'example.com'})
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_zone_without_existing_zone
|
35
|
+
instance = klass.new
|
36
|
+
|
37
|
+
connection = mock()
|
38
|
+
instance.stubs(:connection).returns(connection)
|
39
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
40
|
+
connection.expects(:query).with("SELECT LENGTH(name) domain_length, id, name FROM domains WHERE 'test.example.com' LIKE CONCAT('%%.', name) ORDER BY domain_length DESC LIMIT 1").returns([])
|
41
|
+
|
42
|
+
assert_raise(Proxy::Dns::Error) { instance.get_zone('test.example.com') }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_create_record
|
46
|
+
instance = klass.new
|
47
|
+
|
48
|
+
connection = mock()
|
49
|
+
instance.stubs(:connection).returns(connection)
|
50
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
51
|
+
connection.expects(:escape).with('A').returns('A')
|
52
|
+
connection.expects(:escape).with('10.1.1.1').returns('10.1.1.1')
|
53
|
+
connection.expects(:query).with("INSERT INTO records (domain_id, name, ttl, content, type) VALUES (1, 'test.example.com', 86400, '10.1.1.1', 'A')")
|
54
|
+
connection.expects(:affected_rows).returns(1)
|
55
|
+
|
56
|
+
assert instance.create_record(1, 'test.example.com', 'A', '10.1.1.1')
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_delete_record
|
60
|
+
instance = klass.new
|
61
|
+
|
62
|
+
connection = mock()
|
63
|
+
instance.stubs(:connection).returns(connection)
|
64
|
+
connection.expects(:escape).with('test.example.com').returns('test.example.com')
|
65
|
+
connection.expects(:escape).with('A').returns('A')
|
66
|
+
connection.expects(:query).with("DELETE FROM records WHERE domain_id=1 AND name='test.example.com' AND type='A'")
|
67
|
+
connection.expects(:affected_rows).returns(1)
|
68
|
+
|
69
|
+
assert instance.delete_record(1, 'test.example.com', 'A')
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def klass
|
75
|
+
Proxy::Dns::Powerdns::Backend::Mysql
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
require 'smart_proxy_dns_powerdns/backend/postgresql'
|
6
|
+
|
7
|
+
class DnsPowerdnsBackendPostgresqlTest < Test::Unit::TestCase
|
8
|
+
# Test that correct initialization works
|
9
|
+
def test_initialize_dummy_with_settings
|
10
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(:powerdns_postgresql_connection => 'dbname=powerdns')
|
11
|
+
provider = klass.new
|
12
|
+
assert_equal 'dbname=powerdns', provider.connection_str
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def klass
|
18
|
+
Proxy::Dns::Powerdns::Backend::Postgresql
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_plugin'
|
4
|
+
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
5
|
+
|
6
|
+
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
7
|
+
# Test that correct initialization works
|
8
|
+
def test_initialize_dummy_with_settings
|
9
|
+
Proxy::Dns::Powerdns::Plugin.load_test_settings(:powerdns_pdnssec => 'sudo pdnssec')
|
10
|
+
provider = klass.new
|
11
|
+
assert_equal 'sudo pdnssec', provider.pdnssec
|
12
|
+
end
|
13
|
+
|
14
|
+
# Test A record creation
|
15
|
+
def test_create_a
|
16
|
+
instance = klass.new
|
17
|
+
|
18
|
+
instance.expects(:dns_find).with('test.example.com').returns(false)
|
19
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
20
|
+
instance.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
21
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
22
|
+
|
23
|
+
assert instance.create_a_record(fqdn, ip)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Test A record creation fails if the record exists
|
27
|
+
def test_create_a_conflict
|
28
|
+
instance = klass.new
|
29
|
+
|
30
|
+
instance.expects(:dns_find).with('test.example.com').returns('192.168.1.1')
|
31
|
+
|
32
|
+
assert_raise(Proxy::Dns::Collision) { instance.create_a_record(fqdn, ip) }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Test PTR record creation
|
36
|
+
def test_create_ptr
|
37
|
+
instance = klass.new
|
38
|
+
|
39
|
+
instance.expects(:dns_find).with('10.1.1.1').returns(false)
|
40
|
+
instance.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
41
|
+
instance.expects(:create_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR', 'test.example.com').returns(true)
|
42
|
+
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
43
|
+
|
44
|
+
assert instance.create_ptr_record(fqdn, ip)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Test PTR record creation fails if the record exists
|
48
|
+
def test_create_ptr_conflict
|
49
|
+
instance = klass.new
|
50
|
+
|
51
|
+
instance.expects(:dns_find).with('10.1.1.1').returns('test2.example.com')
|
52
|
+
|
53
|
+
assert_raise(Proxy::Dns::Collision) { instance.create_ptr_record(fqdn, ip) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Test A record removal
|
57
|
+
def test_remove_a
|
58
|
+
instance = klass.new
|
59
|
+
|
60
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
61
|
+
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
62
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
63
|
+
|
64
|
+
assert instance.remove_a_record(fqdn)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Test PTR record removal
|
68
|
+
def test_remove_ptr
|
69
|
+
instance = klass.new
|
70
|
+
|
71
|
+
instance.expects(:get_zone).with('1.1.1.10.in-addr.arpa').returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
72
|
+
instance.expects(:delete_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR').returns(true)
|
73
|
+
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
74
|
+
|
75
|
+
assert instance.remove_ptr_record(reverse_ip)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_do_create
|
79
|
+
instance = klass.new
|
80
|
+
|
81
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
82
|
+
instance.expects(:create_record).with(1, 'test.example.com', 'A', '10.1.1.1').returns(true)
|
83
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
84
|
+
|
85
|
+
assert instance.do_create('test.example.com', '10.1.1.1', 'A')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_do_remove
|
89
|
+
instance = klass.new
|
90
|
+
|
91
|
+
instance.expects(:get_zone).with('test.example.com').returns({'id' => 1, 'name' => 'example.com'})
|
92
|
+
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
93
|
+
instance.expects(:rectify_zone).with('example.com').returns(true)
|
94
|
+
|
95
|
+
assert instance.do_remove('test.example.com', 'A')
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def klass
|
101
|
+
Proxy::Dns::Powerdns::Record
|
102
|
+
end
|
103
|
+
|
104
|
+
def fqdn
|
105
|
+
'test.example.com'
|
106
|
+
end
|
107
|
+
|
108
|
+
def ip
|
109
|
+
'10.1.1.1'
|
110
|
+
end
|
111
|
+
|
112
|
+
def reverse_ip
|
113
|
+
'1.1.1.10.in-addr.arpa'
|
114
|
+
end
|
115
|
+
end
|
metadata
CHANGED
@@ -1,55 +1,69 @@
|
|
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.2.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: 2016-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mocha
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mysql2
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pg
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
69
|
description: PowerDNS DNS provider plugin for Foreman's smart proxy
|
@@ -64,12 +78,20 @@ files:
|
|
64
78
|
- bundler.d/dns_powerdns.rb
|
65
79
|
- config/dns_powerdns.yml
|
66
80
|
- lib/smart_proxy_dns_powerdns.rb
|
81
|
+
- lib/smart_proxy_dns_powerdns/backend/dummy.rb
|
82
|
+
- lib/smart_proxy_dns_powerdns/backend/mysql.rb
|
83
|
+
- lib/smart_proxy_dns_powerdns/backend/postgresql.rb
|
84
|
+
- lib/smart_proxy_dns_powerdns/dependencies.rb
|
85
|
+
- lib/smart_proxy_dns_powerdns/dns_powerdns_configuration_validator.rb
|
67
86
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_main.rb
|
68
87
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_plugin.rb
|
69
88
|
- lib/smart_proxy_dns_powerdns/dns_powerdns_version.rb
|
70
|
-
- test/
|
71
|
-
- test/integration_tests.py
|
89
|
+
- test/integration/integration_test.rb
|
72
90
|
- test/test_helper.rb
|
91
|
+
- test/unit/dns_powerdns_configuration_validator_test.rb
|
92
|
+
- test/unit/dns_powerdns_record_mysql_test.rb
|
93
|
+
- test/unit/dns_powerdns_record_postgresql_test.rb
|
94
|
+
- test/unit/dns_powerdns_record_test.rb
|
73
95
|
homepage: https://github.com/theforeman/smart_proxy_dns_powerdns
|
74
96
|
licenses:
|
75
97
|
- GPLv3
|
@@ -80,12 +102,12 @@ require_paths:
|
|
80
102
|
- lib
|
81
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
82
104
|
requirements:
|
83
|
-
- -
|
105
|
+
- - ">="
|
84
106
|
- !ruby/object:Gem::Version
|
85
107
|
version: '0'
|
86
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
109
|
requirements:
|
88
|
-
- -
|
110
|
+
- - ">="
|
89
111
|
- !ruby/object:Gem::Version
|
90
112
|
version: '0'
|
91
113
|
requirements: []
|
@@ -96,5 +118,8 @@ specification_version: 4
|
|
96
118
|
summary: PowerDNS DNS provider plugin for Foreman's smart proxy
|
97
119
|
test_files:
|
98
120
|
- test/test_helper.rb
|
99
|
-
- test/
|
100
|
-
- test/
|
121
|
+
- test/integration/integration_test.rb
|
122
|
+
- test/unit/dns_powerdns_record_postgresql_test.rb
|
123
|
+
- test/unit/dns_powerdns_configuration_validator_test.rb
|
124
|
+
- test/unit/dns_powerdns_record_mysql_test.rb
|
125
|
+
- test/unit/dns_powerdns_record_test.rb
|
@@ -1,121 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
require 'smart_proxy_dns_powerdns/dns_powerdns_main'
|
4
|
-
|
5
|
-
class DnsPowerdnsRecordTest < Test::Unit::TestCase
|
6
|
-
# Test that a missing :powerdns_mysql_hostname throws an error
|
7
|
-
def test_initialize_without_settings
|
8
|
-
assert_raise(RuntimeError) do
|
9
|
-
klass.new(settings.delete_if { |k,v| k == :powerdns_mysql_hostname })
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# Test that correct initialization works
|
14
|
-
def test_initialize_with_settings
|
15
|
-
assert_nothing_raised do
|
16
|
-
mock_mysql
|
17
|
-
|
18
|
-
klass.new(settings)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Test A record creation
|
23
|
-
def test_create_a
|
24
|
-
mock_mysql
|
25
|
-
|
26
|
-
instance = klass.new(settings)
|
27
|
-
|
28
|
-
instance.expects(:domain).returns({'id' => 1})
|
29
|
-
instance.expects(:dns_find).with(1, 'test.example.com').returns(false)
|
30
|
-
instance.expects(:create_record).with(1, 'test.example.com', 84600, '10.1.1.1', 'A').returns(true)
|
31
|
-
|
32
|
-
assert instance.create
|
33
|
-
end
|
34
|
-
|
35
|
-
# Test A record creation fails if the record exists
|
36
|
-
def test_create_a_conflict
|
37
|
-
mock_mysql
|
38
|
-
|
39
|
-
instance = klass.new(settings)
|
40
|
-
|
41
|
-
instance.expects(:domain).returns({'id' => 1})
|
42
|
-
instance.expects(:dns_find).with(1, 'test.example.com').returns('192.168.1.1')
|
43
|
-
|
44
|
-
assert_raise(Proxy::Dns::Collision) { instance.create }
|
45
|
-
end
|
46
|
-
|
47
|
-
# Test PTR record creation
|
48
|
-
def test_create_ptr
|
49
|
-
mock_mysql
|
50
|
-
|
51
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
52
|
-
|
53
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => 'example.com'})
|
54
|
-
instance.expects(:dns_find).with(1, '1.1.1.10.in-addr.arpa').returns(false)
|
55
|
-
instance.expects(:create_record).with(1, '1.1.1.10.in-addr.arpa', 84600, 'test.example.com', 'PTR').returns(true)
|
56
|
-
instance.expects(:rectify_zone).with('example.com').returns(true)
|
57
|
-
|
58
|
-
assert instance.create
|
59
|
-
end
|
60
|
-
|
61
|
-
# Test PTR record creation fails if the record exists
|
62
|
-
def test_create_ptr_conflict
|
63
|
-
mock_mysql
|
64
|
-
|
65
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
66
|
-
|
67
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
68
|
-
instance.expects(:dns_find).with(1, '1.1.1.10.in-addr.arpa').returns('test2.example.com')
|
69
|
-
|
70
|
-
assert_raise(Proxy::Dns::Collision) { instance.create }
|
71
|
-
end
|
72
|
-
|
73
|
-
# Test A record removal
|
74
|
-
def test_remove_a
|
75
|
-
mock_mysql
|
76
|
-
|
77
|
-
instance = klass.new(settings)
|
78
|
-
|
79
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => 'example.com'})
|
80
|
-
instance.expects(:delete_record).with(1, 'test.example.com', 'A').returns(true)
|
81
|
-
instance.expects(:rectify_zone).with('example.com').returns(true)
|
82
|
-
|
83
|
-
assert instance.remove
|
84
|
-
end
|
85
|
-
|
86
|
-
# Test PTR record removal
|
87
|
-
def test_remove_ptr
|
88
|
-
mock_mysql
|
89
|
-
|
90
|
-
instance = klass.new(settings.merge(:type => 'PTR'))
|
91
|
-
|
92
|
-
instance.expects(:domain).returns({'id' => 1, 'name' => '1.1.10.in-addr.arpa'})
|
93
|
-
instance.expects(:delete_record).with(1, '1.1.1.10.in-addr.arpa', 'PTR').returns(true)
|
94
|
-
instance.expects(:rectify_zone).with('1.1.10.in-addr.arpa').returns(true)
|
95
|
-
|
96
|
-
assert instance.remove
|
97
|
-
end
|
98
|
-
|
99
|
-
def mock_mysql
|
100
|
-
Mysql2::Client.expects(:new).with(:host => 'localhost', :username => 'username', :password => 'password', :database => 'powerdns').returns(false)
|
101
|
-
end
|
102
|
-
|
103
|
-
private
|
104
|
-
|
105
|
-
def klass
|
106
|
-
Proxy::Dns::Powerdns::Record
|
107
|
-
end
|
108
|
-
|
109
|
-
def settings
|
110
|
-
{
|
111
|
-
:powerdns_mysql_hostname => 'localhost',
|
112
|
-
:powerdns_mysql_username => 'username',
|
113
|
-
:powerdns_mysql_password => 'password',
|
114
|
-
:powerdns_mysql_database => 'powerdns',
|
115
|
-
:fqdn => 'test.example.com',
|
116
|
-
:value => '10.1.1.1',
|
117
|
-
:type => 'A',
|
118
|
-
:ttl => 84600,
|
119
|
-
}
|
120
|
-
end
|
121
|
-
end
|
data/test/integration_tests.py
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
import dns.resolver
|
3
|
-
import dns.reversename
|
4
|
-
import pytest
|
5
|
-
import random
|
6
|
-
import requests
|
7
|
-
import socket
|
8
|
-
import string
|
9
|
-
import struct
|
10
|
-
import subprocess
|
11
|
-
|
12
|
-
|
13
|
-
@pytest.fixture
|
14
|
-
def resolver():
|
15
|
-
"""
|
16
|
-
Return a resolver object against the configured powerdns server
|
17
|
-
"""
|
18
|
-
resolver = dns.resolver.Resolver(configure=False)
|
19
|
-
resolver.nameservers = ['127.0.0.1']
|
20
|
-
resolver.port = 5300
|
21
|
-
|
22
|
-
return resolver
|
23
|
-
|
24
|
-
|
25
|
-
@pytest.fixture
|
26
|
-
def smart_proxy_url():
|
27
|
-
return 'http://localhost:8000/'
|
28
|
-
|
29
|
-
|
30
|
-
@pytest.fixture
|
31
|
-
def fqdn():
|
32
|
-
return ''.join(random.sample(string.lowercase, 10)) + '.' + 'example.com'
|
33
|
-
|
34
|
-
|
35
|
-
@pytest.fixture
|
36
|
-
def ip():
|
37
|
-
return socket.inet_ntoa(struct.pack("!I", random.randint(1, 2 ** 32)))
|
38
|
-
|
39
|
-
|
40
|
-
def purge_cache(name):
|
41
|
-
subprocess.check_output(['sudo', 'pdns_control', 'purge', name])
|
42
|
-
|
43
|
-
|
44
|
-
def test_forward_dns(resolver, smart_proxy_url, fqdn, ip):
|
45
|
-
response = requests.post(smart_proxy_url + 'dns/',
|
46
|
-
data={'fqdn': fqdn, 'value': ip, 'type': 'A'})
|
47
|
-
response.raise_for_status()
|
48
|
-
|
49
|
-
answer = resolver.query(fqdn, 'A')
|
50
|
-
assert len(answer.rrset.items) == 1
|
51
|
-
assert answer.rrset.items[0].address == ip
|
52
|
-
|
53
|
-
response = requests.delete(smart_proxy_url + 'dns/' + fqdn)
|
54
|
-
response.raise_for_status()
|
55
|
-
|
56
|
-
purge_cache(fqdn)
|
57
|
-
|
58
|
-
with pytest.raises(dns.resolver.NXDOMAIN):
|
59
|
-
resolver.query(fqdn, 'A')
|
60
|
-
|
61
|
-
|
62
|
-
def test_reverse_dns(resolver, smart_proxy_url, fqdn, ip):
|
63
|
-
response = requests.post(smart_proxy_url + 'dns/',
|
64
|
-
data={'fqdn': fqdn, 'value': ip, 'type': 'PTR'})
|
65
|
-
response.raise_for_status()
|
66
|
-
|
67
|
-
name = dns.reversename.from_address(ip)
|
68
|
-
|
69
|
-
answer = resolver.query(name, 'PTR')
|
70
|
-
assert len(answer.rrset.items) == 1
|
71
|
-
assert answer.rrset.items[0].target.to_text() == fqdn + '.'
|
72
|
-
|
73
|
-
response = requests.delete(smart_proxy_url + 'dns/' + name.to_text().rstrip('.'))
|
74
|
-
response.raise_for_status()
|
75
|
-
|
76
|
-
purge_cache(name.to_text())
|
77
|
-
|
78
|
-
with pytest.raises(dns.resolver.NXDOMAIN):
|
79
|
-
resolver.query(name, 'PTR')
|