Skip to content

Commit df04690

Browse files
authored
Merge pull request #118 from crux-capacitor/master
Add support for multiple views serving the same zone but different record sets.
2 parents 522b369 + 8eec38c commit df04690

File tree

5 files changed

+162
-38
lines changed

5 files changed

+162
-38
lines changed

README.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ declared.
8686
`bind:available_zones:<zone>:file` should point to an existing zone file
8787
that will be **sourced** by the formula.
8888

89+
Using Views
90+
-----------
91+
92+
Using views introduces some restrictions by the BIND server in that once you have views defined, ALL of your zones have to be served via a view. You cannot have any zones defined outside of a view.
93+
94+
If you want multiple views to serve the same zone but with different record sets, follow the example in pillar-with-views.example to set this up. The key to this is the 'file' argument in the view configuration that allows you to set the view's configured_zone to a zone that you define underneath 'available_zones'. Without specifying this 'file' argument, your views cannot serve the same zone; they will instead serve a zone that matches the name of the view.
95+
8996
External zone files
9097
-------------------
9198

bind/config.sls

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,19 +203,24 @@ bind_rndc_client_config:
203203
{%- set views = {False: salt['pillar.get']('bind', {})} %}{# process non-view zones in the same loop #}
204204
{%- do views.update(salt['pillar.get']('bind:configured_views', {})) %}
205205
{%- for view, view_data in views|dictsort %}
206-
{%- set dash_view = '-' + view if view else '' %}
207-
{% for zone, zone_data in view_data.get('configured_zones', {})|dictsort -%}
208-
{%- set file = salt['pillar.get']("bind:available_zones:" + zone + ":file", false) %}
209-
{%- set zone_records = salt['pillar.get']('bind:available_zones:' + zone + ':records', {}) %}
210-
{%- if salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse') %}
211-
{%- do generate_reverse(zone_records, salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse:net'), salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse:for_zones'), salt['pillar.get']('bind:available_zones', {})) %}
212-
{%- endif %}
206+
{%- set dash_view = '-' + view if view else '' %}
207+
{%- for zone, zone_data in view_data.get('configured_zones', {})|dictsort -%}
208+
{%- if 'file' in zone_data %}
209+
{%- set file = zone_data.file %}
210+
{%- set zone = file|replace(".txt", "") %}
211+
{%- else %}
212+
{%- set file = salt['pillar.get']("bind:available_zones:" + zone + ":file", false) %}
213+
{%- endif %}
214+
{%- set zone_records = salt['pillar.get']('bind:available_zones:' + zone + ':records', {}) %}
215+
{%- if salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse') %}
216+
{%- do generate_reverse(zone_records, salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse:net'), salt['pillar.get']('bind:available_zones:' + zone + ':generate_reverse:for_zones'), salt['pillar.get']('bind:available_zones', {})) %}
217+
{%- endif %}
213218
{# If we define RRs in pillar, we use the internal template to generate the zone file
214219
otherwise, we fallback to the old behaviour and use the declared file
215220
#}
216-
{%- set zone_source = 'salt://bind/files/zone.jinja' if zone_records != {} else 'salt://' ~ map.zones_source_dir ~ '/' ~ file %}
217-
{%- set serial_auto = salt['pillar.get']('bind:available_zones:' + zone + ':soa:serial', '') == 'auto' %}
218-
{% if file and zone_data['type'] == 'master' -%}
221+
{%- set zone_source = 'salt://bind/files/zone.jinja' if zone_records != {} else 'salt://' ~ map.zones_source_dir ~ '/' ~ file %}
222+
{%- set serial_auto = salt['pillar.get']('bind:available_zones:' + zone + ':soa:serial', '') == 'auto' %}
223+
{% if file and zone_data['type'] == 'master' -%}
219224
zones{{ dash_view }}-{{ zone }}{{ '.include' if serial_auto else ''}}:
220225
file.managed:
221226
- name: {{ zones_directory }}/{{ file }}{{ '.include' if serial_auto else ''}}

bind/files/named.conf.local.jinja

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@
77
// organization
88
//include "/etc/bind/zones.rfc1918";
99

10+
{% for name, data in salt['pillar.get']('bind:configured_acls', {})|dictsort %}
11+
acl {{ name }} {
12+
{%- for d in data %}
13+
{{ d }};
14+
{%- endfor %}
15+
};
16+
{%- endfor %}
17+
18+
{%- for name, data in salt['pillar.get']('bind:configured_masters', {})|dictsort %}
19+
masters {{ name }} {
20+
{%- for d in data %}
21+
{{ d }};
22+
{%- endfor %}
23+
};
24+
{%- endfor %}
25+
1026
{%- macro zone(key, args, file, masters) %}
1127
zone "{{ key }}" {
1228
type {{ args['type'] }};
@@ -92,23 +108,26 @@ include "{{ map.default_zones_config }}";
92108
{{ zone(key, args, file, masters) }}
93109
{% endfor %}
94110

95-
{% for view, view_data in salt['pillar.get']('bind:configured_views', {})|dictsort %}
111+
{%- for view, view_data in salt['pillar.get']('bind:configured_views', {})|dictsort %}
96112

97113
view {{ view }} {
98114
{%- if view == 'default' %}
99115
include "{{ map.default_zones_config }}";
100116
{%- endif %}
101-
102-
match-clients {
117+
match-clients {
103118
{%- for acl in view_data.get('match_clients', {}) %}
104-
{{ acl }};
119+
{{ acl }};
105120
{%- endfor %}
106-
};
121+
};
107122

108-
{% for key, args in view_data.get('configured_zones', {})|dictsort -%}
109-
{%- set file = salt['pillar.get']("bind:available_zones:" + key + ":file") %}
123+
{%- for key, args in view_data.get('configured_zones', {})|dictsort -%}
124+
{%- if 'file' in args %}
125+
{%- set file = args.file %}
126+
{%- else %}
127+
{%- set file = salt['pillar.get']("bind:available_zones:" + key + ":file") %}
128+
{%- endif %}
110129
{%- set masters = salt['pillar.get']("bind:available_zones:" + key + ":masters") %}
111-
{{ zone(key, args, file, masters) }}
130+
{{ zone(key, args, file, masters) }}
112131
{%- endfor %}
113132
};
114133
{%- endfor %}
@@ -163,20 +182,3 @@ statistics-channels {
163182
{%- endfor %}
164183
};
165184
{%- endif %}
166-
167-
168-
{%- for name, data in salt['pillar.get']('bind:configured_acls', {})|dictsort %}
169-
acl {{ name }} {
170-
{%- for d in data %}
171-
{{ d }};
172-
{%- endfor %}
173-
};
174-
{%- endfor %}
175-
176-
{%- for name, data in salt['pillar.get']('bind:configured_masters', {})|dictsort %}
177-
masters {{ name }} {
178-
{%- for d in data %}
179-
{{ d }};
180-
{%- endfor %}
181-
};
182-
{%- endfor %}

pillar-with-views.example

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
bind:
2+
configured_acls: # We have an internal ACL restricted to our
3+
internal: # private IP range.
4+
- 10.0.0.0/8 # In this case, an ACL for external isn't needed
5+
# as that view will be matched by 'any'.
6+
7+
# Notice that there is no 'configured_zones' at this indentation level.
8+
# That is because when you are using views, the bind service forces all zones to be served via a view.
9+
#
10+
# Also note - any other zones defined in any other conf files will either need to be commented out, or
11+
# also served via a view using a file include. If you have other zones being served outside of a view, bind will
12+
# fail to start and give you an error message indicating this. You will likely find these externally-defined zones
13+
# in /etc/named.conf and /etc/named.conf.local
14+
15+
configured_views:
16+
external: # A view called 'external' to match anything except the 'internal' ACL.
17+
match_clients:
18+
- any # This will match anything, including the public internet.
19+
configured_zones:
20+
mydomain.com: # Notice that this value matches on both views.
21+
type: master
22+
file: external.mydomain.com.txt # Specify the file to be used, which must match the file
23+
recursion: yes # name of the zone below under available_zones.
24+
# This filename also must match the corresponding zone name
25+
# without the .txt extension (and be sure to use .txt as the extension).
26+
notify: False
27+
dnssec: False
28+
29+
internal: # The 'internal' view that is restricted to the 'internal' ACL.
30+
match_clients:
31+
- internal # This will match only our ACL named 'internal'.
32+
configured_zones:
33+
mydomain.com: # Same as above - both views will serve the same zone.
34+
type: master
35+
file: internal.mydomain.com.txt # Different file - matches the internal zone below.
36+
# Again, this filename must match the corresponding zone name
37+
# without the .txt extension (and be sure to use .txt as the extension).
38+
recursion: yes
39+
notify: False
40+
dnssec: False
41+
42+
available_zones:
43+
external.mydomain.com: # Beginning of the 'external' zone definition.
44+
file: external.mydomain.com.txt # The file in which to save this zone's record set - matches the file
45+
# specified in the 'external' view.
46+
47+
soa: # Declare the SOA RRs for the zone
48+
ns: ns1.external.mydomain.com # Required
49+
contact: [email protected] # Required
50+
serial: auto # Alternatively, autoupdate serial on each change
51+
class: IN # Optional. Default: IN
52+
refresh: 8600 # Optional. Default: 12h
53+
retry: 900 # Optional. Default: 15m
54+
expiry: 86000 # Optional. Default: 2w
55+
nxdomain: 500 # Optional. Default: 1m
56+
ttl: 8600 # Optional. Not set by default
57+
records: # Records for the zone, grouped by type
58+
A:
59+
portal: 50.60.70.80
60+
gateway: 50.60.70.81
61+
directory: 50.60.70.82
62+
ns1: 50.60.70.83
63+
www: 50.60.70.84
64+
NS:
65+
'@':
66+
- ns1
67+
CNAME:
68+
login: portal.mydomain.com.
69+
dashboard: www.mydomain.com.
70+
71+
internal.mydomain.com: # Beginning of the 'internal' zone definition.
72+
file: internal.mydomain.com.txt # The file in which to save this zone's record set - matches the file
73+
# specified in the 'internal' view.
74+
75+
soa: # Declare the SOA RRs for the zone
76+
ns: ns1.mydomain.com # Required
77+
contact: [email protected] # Required
78+
serial: auto # Alternatively, autoupdate serial on each change
79+
class: IN # Optional. Default: IN
80+
refresh: 8600 # Optional. Default: 12h
81+
retry: 900 # Optional. Default: 15m
82+
expiry: 86000 # Optional. Default: 2w
83+
nxdomain: 500 # Optional. Default: 1m
84+
ttl: 8600 # Optional. Not set by default
85+
records: # Records for the zone, grouped by type
86+
A:
87+
portal: 10.0.0.10 # Here we serve all private IPs as opposed to the public IPs
88+
gateway: 10.0.0.11 # in the external zone.
89+
directory: 10.0.0.12
90+
ns1: 10.0.0.13
91+
www: 10.0.0.14
92+
NS:
93+
'@':
94+
- ns1
95+
CNAME:
96+
login: portal.mydomain.com.
97+
dashboard: www.mydomain.com.

pillar.example

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ bind:
231231
notify: False # Don't notify any NS RRs of any changes to zone
232232
also-notify: # Do notify these IP addresses (pointless as
233233
- 1.1.1.1 # notify has been set to no)
234-
- 2.2.2.2
234+
- 2.2.2.2 # If using views, do not define configured_zones
235+
# at this indentation level - define it using the sub-key
236+
# of your view under configured_views.
235237

236238
sub.domain2.com: # Domain zone with DNSSEC
237239
type: master # We're the master of this zone
@@ -279,14 +281,25 @@ bind:
279281
configured_zones: # Zones that our view is applicable to
280282
my.zone: # We've defined a new zone in here
281283
type: master
282-
notify: False
284+
file: example.com.txt # Optional: specify the zone file to be used for this view,
285+
# otherwise it will default to the file matching the name of the zone that you
286+
# specify here (which must match a zone under 'available_zones'.
287+
# The file name must match what you have entered for 'file' in the zone under
288+
# 'available_zones'.
289+
# This allows you to define multiple views that serve the same zone, but
290+
# serve a different record set in each.
291+
# If doing this, you need to configure the zones and their record sets
292+
# underneath the 'available_zones' section.
293+
notify: False
283294
update_policy: # A given update policy
284295
- "grant core_dhcp name dns_entry_allowed_to_update. ANY"
285296

286297
configured_acls: # And now for some ACLs
287298
my_net: # Our ACL's name
288299
- 127.0.0.0/8 # And the applicable IP addresses
289-
- 10.20.0.0/16
300+
- 10.20.0.0/16 # If using views, you need to create an ACL per view to differentiate
301+
# who accesses the view, and then specify the appropriate ACL name under
302+
# the 'match_clients' sub-key of your view.
290303

291304
### Define zone records in pillar ###
292305
bind:

0 commit comments

Comments
 (0)