Merge lp:~mpontillo/maas/bug-1384334-add-dnssec-config-option into lp:~maas-committers/maas/trunk

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: no longer in the source branch.
Merged at revision: 3852
Proposed branch: lp:~mpontillo/maas/bug-1384334-add-dnssec-config-option
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 301 lines (+100/-10)
11 files modified
etc/maas/templates/dns/named.conf.options.inside.maas.template (+2/-0)
src/maasserver/dns/config.py (+11/-1)
src/maasserver/dns/tests/test_config.py (+2/-1)
src/maasserver/forms.py (+1/-0)
src/maasserver/forms_settings.py (+28/-1)
src/maasserver/management/commands/edit_named_options.py (+15/-3)
src/maasserver/models/config.py (+6/-0)
src/maasserver/tests/test_commands_edit_named_options.py (+22/-0)
src/provisioningserver/dns/actions.py (+3/-2)
src/provisioningserver/dns/config.py (+1/-0)
src/provisioningserver/dns/tests/test_actions.py (+9/-2)
To merge this branch: bzr merge lp:~mpontillo/maas/bug-1384334-add-dnssec-config-option
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+257451@code.launchpad.net

Commit message

Add configuration option for dnssec-validation.

Description of the change

Adds a configuration option for DNSSEC validation.

Requires packaging to update /etc/bind/named.conf.options, so that the dnssec-validation option can move to a MAAS-controlled configuration file.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

This change depends on a packaging change that I still need to propose, so that it will properly remove the dnssec-validation directive from /etc/bind/named.conf.options.

Revision history for this message
Mike Pontillo (mpontillo) wrote :
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good.

review: Approve
Revision history for this message
Mike Pontillo (mpontillo) :
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Thanks Gavin. I'm going to hold off on merging this until the packaging change is approved. (and I make the minor changes based on your comments)

Revision history for this message
Gavin Panella (allenap) :
Revision history for this message
Mike Pontillo (mpontillo) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'etc/maas/templates/dns/named.conf.options.inside.maas.template'
2--- etc/maas/templates/dns/named.conf.options.inside.maas.template 2014-11-14 21:39:55 +0000
3+++ etc/maas/templates/dns/named.conf.options.inside.maas.template 2015-04-28 21:54:03 +0000
4@@ -6,6 +6,8 @@
5 };
6 {{endif}}
7
8+dnssec-validation {{dnssec_validation}};
9+
10 allow-query { any; };
11 allow-recursion { trusted; };
12 allow-query-cache { trusted; };
13
14=== modified file 'src/maasserver/dns/config.py'
15--- src/maasserver/dns/config.py 2015-03-26 00:12:12 +0000
16+++ src/maasserver/dns/config.py 2015-04-28 21:54:03 +0000
17@@ -197,7 +197,9 @@
18 # expect this side-effect from calling dns_update_all_zones_now(), and
19 # some that call it for this side-effect alone. At present all it does is
20 # set the upstream DNS servers, nothing to do with serving zones at all!
21- bind_write_options(upstream_dns=get_upstream_dns())
22+ bind_write_options(
23+ upstream_dns=get_upstream_dns(),
24+ dnssec_validation=get_dnssec_validation())
25
26 # Nor should we be rewriting ACLs that are related only to allowing
27 # recursive queries to the upstream DNS servers. Again, this is legacy,
28@@ -357,6 +359,14 @@
29 return [] if upstream_dns is None else upstream_dns.split()
30
31
32+def get_dnssec_validation():
33+ """Return the configuration option for DNSSEC validation.
34+
35+ :return: "on", "off", or "auto"
36+ """
37+ return Config.objects.get_config("dnssec_validation")
38+
39+
40 def get_trusted_networks():
41 """Return the CIDR representation of all the Networks we know about.
42
43
44=== modified file 'src/maasserver/dns/tests/test_config.py'
45--- src/maasserver/dns/tests/test_config.py 2015-03-25 15:33:23 +0000
46+++ src/maasserver/dns/tests/test_config.py 2015-04-28 21:54:03 +0000
47@@ -503,7 +503,8 @@
48 dns_update_all_zones_now()
49 self.assertThat(
50 bind_write_options,
51- MockCalledOnceWith(upstream_dns=[random_ip]))
52+ MockCalledOnceWith(
53+ dnssec_validation='auto', upstream_dns=[random_ip]))
54
55 def test_dns_update_all_zones_now_writes_trusted_networks_parameter(self):
56 self.patch(settings, 'DNS_CONNECT', True)
57
58=== modified file 'src/maasserver/forms.py'
59--- src/maasserver/forms.py 2015-04-17 07:26:25 +0000
60+++ src/maasserver/forms.py 2015-04-28 21:54:03 +0000
61@@ -1292,6 +1292,7 @@
62 enlistment_domain = get_config_field('enlistment_domain')
63 http_proxy = get_config_field('http_proxy')
64 upstream_dns = get_config_field('upstream_dns')
65+ dnssec_validation = get_config_field('dnssec_validation')
66 ntp_server = get_config_field('ntp_server')
67
68
69
70=== modified file 'src/maasserver/forms_settings.py'
71--- src/maasserver/forms_settings.py 2015-03-25 15:33:23 +0000
72+++ src/maasserver/forms_settings.py 2015-04-28 21:54:03 +0000
73@@ -1,4 +1,4 @@
74-# Copyright 2012-2014 Canonical Ltd. This software is licensed under the
75+# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
76 # GNU Affero General Public License version 3 (see the file LICENSE).
77
78 """Configuration items definition and utilities."""
79@@ -28,6 +28,7 @@
80 from maasserver.models.config import (
81 Config,
82 DEFAULT_OS,
83+ DNSSEC_VALIDATION_CHOICES,
84 )
85 from maasserver.utils.forms import compose_invalid_choice_text
86 from maasserver.utils.osystems import (
87@@ -107,6 +108,19 @@
88 return field
89
90
91+def make_dnssec_validation_field(*args, **kwargs):
92+ """Build and return the make_dnssec_validation_field field."""
93+ field = forms.ChoiceField(
94+ initial=Config.objects.get_config('dnssec_validation'),
95+ choices=DNSSEC_VALIDATION_CHOICES,
96+ error_messages={
97+ 'invalid_choice': compose_invalid_choice_text(
98+ 'dnssec_validation', DNSSEC_VALIDATION_CHOICES)
99+ },
100+ **kwargs)
101+ return field
102+
103+
104 CONFIG_ITEMS = {
105 'check_compatibility': {
106 'default': False,
107@@ -197,6 +211,19 @@
108 "server config.")
109 }
110 },
111+ 'dnssec_validation': {
112+ 'default': 'auto',
113+ 'form': make_dnssec_validation_field,
114+ 'form_kwargs': {
115+ 'label': (
116+ "Enable DNSSEC validation of upstream zones"),
117+ 'required': False,
118+ 'help_text': (
119+ "Only used when MAAS is running its own DNS server. This "
120+ "value is used as the value of 'dnssec_validation' in the DNS "
121+ "server config.")
122+ }
123+ },
124 'ntp_server': {
125 'default': None,
126 'form': forms.CharField,
127
128=== modified file 'src/maasserver/management/commands/edit_named_options.py'
129--- src/maasserver/management/commands/edit_named_options.py 2015-03-25 15:33:23 +0000
130+++ src/maasserver/management/commands/edit_named_options.py 2015-04-28 21:54:03 +0000
131@@ -46,9 +46,10 @@
132 help = (
133 "Edit the named.conf.options file so that it includes the "
134 "named.conf.options.inside.maas file, which contains the "
135- "'forwarders' setting. A backup of the old file will be made "
136- "with the suffix '.maas-YYYY-MM-DDTHH:MM:SS.mmmmmm'. This "
137- "program must be run as root.")
138+ "'forwarders' and 'dnssec-validation' settings. A backup "
139+ "of the old file will be made with the suffix "
140+ "'.maas-YYYY-MM-DDTHH:MM:SS.mmmmmm'. This program must be run as "
141+ "root.")
142
143 def read_file(self, config_path):
144 """Open the named file and return its contents as a string."""
145@@ -95,6 +96,16 @@
146 if 'forwarders' in options_block:
147 del options_block['forwarders']
148
149+ def remove_dnssec_validation(self, options_block):
150+ """Remove existing dnssec-validation from the options block.
151+
152+ It's a syntax error to have more than one in the combined
153+ configuration for named so we just remove whatever was there.
154+ There is no data loss due to the backup file made later.
155+ """
156+ if 'dnssec-validation' in options_block:
157+ del options_block['dnssec-validation']
158+
159 def back_up_existing_file(self, config_path):
160 now = datetime.now().isoformat()
161 backup_destination = config_path + '.' + now
162@@ -116,6 +127,7 @@
163 # Modify the config.
164 self.set_up_include_statement(options_block, config_path)
165 self.remove_forwarders(options_block)
166+ self.remove_dnssec_validation(options_block)
167 new_content = MakeISC(config_dict)
168
169 # Back up and write new file.
170
171=== modified file 'src/maasserver/models/config.py'
172--- src/maasserver/models/config.py 2015-03-27 06:41:39 +0000
173+++ src/maasserver/models/config.py 2015-04-28 21:54:03 +0000
174@@ -33,6 +33,11 @@
175
176
177 DEFAULT_OS = UbuntuOS()
178+DNSSEC_VALIDATION_CHOICES = [
179+ ("auto", "Automatic (use default root key)"),
180+ ("yes", "Yes (manually configured root key)"),
181+ ("no", "No (Disable DNSSEC; useful when upstream DNS is misconfigured)")
182+]
183
184
185 def get_default_config():
186@@ -53,6 +58,7 @@
187 'default_distro_series': DEFAULT_OS.get_default_release(),
188 'http_proxy': None,
189 'upstream_dns': None,
190+ 'dnssec_validation': "auto",
191 'ntp_server': 'ntp.ubuntu.com',
192 # RPC configuration.
193 'rpc_region_certificate': None,
194
195=== modified file 'src/maasserver/tests/test_commands_edit_named_options.py'
196--- src/maasserver/tests/test_commands_edit_named_options.py 2015-03-25 15:33:23 +0000
197+++ src/maasserver/tests/test_commands_edit_named_options.py 2015-04-28 21:54:03 +0000
198@@ -40,6 +40,15 @@
199 };
200 """)
201
202+OPTIONS_FILE_WITH_DNSSEC = textwrap.dedent("""\
203+ options {
204+ directory "/var/cache/bind";
205+ dnssec-validation auto;
206+ auth-nxdomain no; # conform to RFC1035
207+ listen-on-v6 { any; };
208+ };
209+""")
210+
211 OPTIONS_FILE_WITH_FORWARDERS = textwrap.dedent("""\
212 options {
213 directory "/var/cache/bind";
214@@ -103,6 +112,19 @@
215 Not(FileContains(
216 matcher=Contains('forwarders'))))
217
218+ def test_removes_existing_dnssec_validation_config(self):
219+ options_file = self.make_file(contents=OPTIONS_FILE_WITH_DNSSEC)
220+ call_command(
221+ "edit_named_options", config_path=options_file,
222+ stdout=self.stdout)
223+
224+ # Check that the file was re-written without forwarders (since
225+ # that's now in the included file).
226+ self.assertThat(
227+ options_file,
228+ Not(FileContains(
229+ matcher=Contains('dnssec-validation'))))
230+
231 def test_normal_operation(self):
232 options_file = self.make_file(contents=OPTIONS_FILE)
233 call_command(
234
235=== modified file 'src/provisioningserver/dns/actions.py'
236--- src/provisioningserver/dns/actions.py 2015-03-25 15:33:23 +0000
237+++ src/provisioningserver/dns/actions.py 2015-04-28 21:54:03 +0000
238@@ -119,7 +119,7 @@
239 dns_config.write_config(trusted_networks=trusted_networks)
240
241
242-def bind_write_options(upstream_dns):
243+def bind_write_options(upstream_dns, dnssec_validation):
244 """Write BIND options.
245
246 :param upstream_dns: A sequence of upstream DNS servers.
247@@ -129,7 +129,8 @@
248 assert not isinstance(upstream_dns, (bytes, unicode))
249 assert isinstance(upstream_dns, collections.Sequence)
250
251- set_up_options_conf(upstream_dns=upstream_dns)
252+ set_up_options_conf(
253+ upstream_dns=upstream_dns, dnssec_validation=dnssec_validation)
254
255
256 def bind_write_zones(zones):
257
258=== modified file 'src/provisioningserver/dns/config.py'
259--- src/provisioningserver/dns/config.py 2014-11-17 11:02:09 +0000
260+++ src/provisioningserver/dns/config.py 2015-04-28 21:54:03 +0000
261@@ -192,6 +192,7 @@
262 # specify it. If it's not set, the substitution will fail with the default
263 # template that uses this value.
264 kwargs.setdefault("upstream_dns")
265+ kwargs.setdefault("dnssec_validation", "auto")
266 try:
267 rendered = template.substitute(kwargs)
268 except NameError as error:
269
270=== modified file 'src/provisioningserver/dns/tests/test_actions.py'
271--- src/provisioningserver/dns/tests/test_actions.py 2015-03-25 15:33:23 +0000
272+++ src/provisioningserver/dns/tests/test_actions.py 2015-04-28 21:54:03 +0000
273@@ -251,7 +251,10 @@
274 factory.make_ipv4_address(),
275 factory.make_ipv4_address(),
276 ]
277- actions.bind_write_options(upstream_dns=upstream_dns)
278+ dnssec_validation = random.choice(["auto", "yes", "no"])
279+ expected_dnssec_validation = dnssec_validation
280+ actions.bind_write_options(
281+ upstream_dns=upstream_dns, dnssec_validation=dnssec_validation)
282 expected_options_file = join(
283 self.dns_conf_dir, MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME)
284 self.assertThat(expected_options_file, FileExists())
285@@ -261,11 +264,15 @@
286 %s;
287 };
288
289+ dnssec-validation %s;
290+
291 allow-query { any; };
292 allow-recursion { trusted; };
293 allow-query-cache { trusted; };
294 """)
295- expected_options_content %= tuple(upstream_dns)
296+ expected_options_content %= (
297+ tuple(upstream_dns) + (expected_dnssec_validation,))
298+
299 self.assertThat(
300 expected_options_file,
301 FileContains(expected_options_content))