Zones and Views
Introduction
The bind__*_zones variables (not to be confused with the
bind__*_generic_zones variables, see the next section) define the zones
which should be maintained via Ansible (and which are made available for use
in the bind__*_configuration variables using the autovalue feature, see
the corresponding Syntax).
The toplevel items in the bind__*_zones lists must be dicts containing
either name keys (for zones) or view keys (in case you are using
views, also known as split-horizon DNS, see RFC 6950). Each view
can, in turn, contain a number of zones, defined in its zones parameter.
Warning
Mixing view and name entries at the top level will result in an
error during the execution of the role. Either all zones need to be in
a view, or all of them need to be outside any view.
Generic Zones
The bind__*_generic_zones is similar to bind__*_zones, but define zones
which should be present in every view. They will automatically be added to
every view (if you are using views), or simply added at the end of the list of
zones (if you are not using views). That also means that you cannot define
view items in bind__*_generic_zones.
Other than that, the configuration syntax for bind__*_generic_zones is the
same as that of bind__*_zones, and the following sections are equally
applicable to both sets of variables.
Zone content
The role can be used to define the initial content of zones and to
automatically generate the corresponding zone files for BIND to use. This is
controlled using the content parameter. If you already have zone files at
hand, or plan to use some alternative source of zone files (including
hand-written zone files which could be transferred to the remote host running
BIND using e.g. the debops.resources role), the content parameter
should not be defined for a zone.
Note
As a minimum, BIND expects a valid zone to contain a NS record. Zones
lacking such a resource record will result in named logging an
error and not loading the zone. If you want to create an empty zone, you
should therefore set content to something like @ IN NS localhost..
If the nameserver is in the zone itself, it also needs at least one A
or AAAA record.
Dynamic zone updates
BIND supports dynamic zone updates if the update-policy or allow-update
options have been defined for a zone. By default, any updates to a zone file
are written by the named daemon to a separate journal file (by
default, the same path as the zone file, with an additional .jnl
extension).
The changes which are stored in the separate journal files can, and will,
be merged back into the original zone file (see, e.g. the sync command for
the rndc utility, but named will also automatically sync
changes at various points in time, e.g. during restarts).
Warning
This means that a zone file and its journal file can get out of sync if the zone file is changed from an outside tool, like Ansible/DebOps, while there are unmerged changes in the zone's journal file. That, in turn, may lead to BIND refusing to load the zone on restart.
Enabling DNSSEC means that BIND will also update the zone on its own, for example, to add signing records. In other words, there are many different ways in which a zone might be dynamic.
While the role attempts to minimise the risk of creating such inconsistencies, it can never be guaranteed in the presence of both dynamic and "static" zone changes. The recommended approach is therefore to always perform dynamic updated on dynamic zones, e.g. using nsupdate (which can make use of Keys to authenticate requests). For example (this example assumes that it is executed on the Ansible controller):
# nsupdate -k ansible/secret/bind/foobar/debops-key.key
> server foobar.example.com
> ttl 3600
> zone example.com
> delete oldhost.example.com A
> add newhost.example.com A 192.168.2.99
> send
> quit
See nsupdate(1) for further details and examples.
Further remarks on using views
Note that if you use views, some options may have to be moved from the toplevel
option block in /etc/bind/named.conf to the per-view equivalent.
For example, if you use the bind__blocked_domains feature together
with views, you may define something like this in the Ansible inventory (in
effect, moving the response-policy option and rpz.local zone down from
the global to the view level):
bind__configuration:
- name: 'options'
options:
- name: 'response-policy-blocked-domains'
state: 'absent'
bind__default_zones: []
bind__zones:
- view: 'internal-view'
options:
- name: 'response-policy-blocked-domains'
raw: 'response-policy { zone "rpz.local"; } break-dnssec yes;'
zones:
- name: 'rpz.local'
options:
- name: 'type'
value: 'master'
- name: 'file'
autovalue: 'zone_file_path'
content: ...
Examples
See bind__default_generic_zones for some simple examples.
A more complex example (without a view) could look something like this:
bind__zones:
- name: 'example.com'
comment: 'My main domain'
options:
- name: 'type'
value: 'master'
- name: 'allow-transfer'
options:
- name: 'allow-transfer-1'
raw: '192.168.1.2; 192.168.1.3;'
- name: 'update-policy'
options:
- name: 'update-policy-local-ddns'
raw: 'grant local-ddns zonesub any;'
- name: 'update-policy-debops'
raw: 'grant debops-key zonesub any;'
- name: 'dnssec-policy'
value: '"kskzsk-rollover"'
content:
Here's an example using multiple view entries, DNSSEC, and multiple keys to
differentiate between the views:
bind__keys:
- name: 'internal-key'
type: 'tsig'
algorithm: 'hmac-sha512'
- name: 'external-key'
type: 'tsig'
algorithm: 'hmac-sha512'
bind__configuration:
- name: 'acl-internal'
option: 'acl internal-acl'
comment: 'ACL matching internal users'
options:
- name: 'not-external-key'
raw: '!key external-key;'
- name: 'internal-key'
raw: 'key internal-key;'
- name: 'internal-ipv4'
raw: '192.168.1.0/24;'
- name: 'internal-ipv6'
raw: 'fd27:f00f:f00f::/48;'
- name: 'internal-localhost'
raw: 'localhost;'
- name: 'acl-external'
option: 'acl external-acl'
comment: 'ACL matching external users'
options:
- name: 'not-internal-key'
raw: '!key internal-key;'
- name: 'external-key'
raw: 'key external-key;'
- name: 'any'
raw: 'any;'
bind__zones:
- view: 'internal-view'
comment: 'Internal view'
options:
- name: 'match-clients'
options:
- name: 'internal-acl'
raw: 'internal-acl;'
- name: 'allow-recursion'
options:
- name: 'internal-acl'
raw: 'interal-acl;'
zones:
- name: 'example.com'
comment: 'My main domain'
options:
- name: 'type'
value: 'master'
- name: 'allow-transfer'
options:
- name: 'allow-transfer-1'
raw: '192.168.1.2; 192.168.1.3;'
- name: 'update-policy'
options:
- name: 'update-policy-local-ddns'
raw: 'grant local-ddns zonesub any;'
- name: 'update-policy-debops'
raw: 'grant debops-key zonesub any;'
- name: 'dnssec-policy'
value: '"kskzsk"'
content: ...
View syntax
Views are defined using a list of YAML dictionaries as toplevel items in the
bind__*_zones variables. The following keys are valid in a view dict:
viewRequired, string. Name of a given view. Multiple views with the same
vieware merged together.stateOptional, string. If not specified or
present, a given view will be present in the generated configuration. Otherwise, the view will not be included in the generated configuration.commentOptional, string. A comment for the view which will be included in the generated configuration for documentation purposes.
zonesOptional, list of YAML dictionaries defining zones belonging to the given view. See Zone syntax.
optionsOptional, list of YAML dictionaries defining options for the given view. The possible configuration options are listed here and the valid dict parameters are the same as for bind__*_configuration.
Zone syntax
Configuration sections are defined using a list of YAML dictionaries, each dictionary uses specific parameters:
nameRequired, string. Name of a given zone (with or without a trailing period), this must be a valid domain name (e.g.
example.com). Multiple configuration entries with the samenameare merged together.commentOptional, string. A comment for the zone which will be included in the generated configuration for documentation purposes.
stateOptional, string. If not specified or
present, a given zone will be present in the generated configuration. Otherwise, the zone will not be included in the generated configuration.dirOptional, string. The directory in which the zone file should be created. Defaults to
/var/lib/bind/<view>/<zone>/. Note that changing the default may clash with security policies defined by e.g.AppArmor. Has no effect unlesscontenthas been defined.ownerOptional, string. The UNIX user owning the zone file. Defaults to
root. Has no effect unlesscontenthas been defined.groupOptional, string. The UNIX group owning the zone file. Defaults to
bind. Has no effect unlesscontenthas been defined.modeOptional, octal mode string. The mode of the zone file. Defaults to
0775. Has no effect unlesscontenthas been defined.forceOptional, boolean. Whether an existing zone file should be overwritten if the
content(see below) has changed. Default:False. Changing the default value can have undesirable consequences if dynamic updates are also enabled for the zone (see Dynamic zone updates for details). Has no effect unlesscontenthas been defined.ttlOptional, string or integer. The default
TTLof the zone. Integers define a TTL in seconds, while a string can be used to define a TTL in a more human-readable format (e.g.1d,2m,1h).originOptional, string. This can be used to define the domain name which a given zone is for. If not defined (recommended), the
namewill be used as theoriginfor the zone.soa_primaryOptional, string. The primary server for this zone. Must be a valid domain name. If not defined,
bind__default_zone_soa_primaryis used.soa_emailOptional, string. The email address of the person responsible for the zone. If not defined,
bind__default_zone_soa_emailis used.soa_serialOptional, string or integer. The serial number of the zone (a number which changes every time the zone is modified, and which is automatically incremented by the named server in response to dynamic updates). If not defined,
bind__default_zone_soa_serialis used.soa_refresh,soa_retry,soa_expire,soa_neg_ttlOptional, string or integer. These values have the same format as the
ttlvalue and are used to complete the SOA record for the zone. If not defined, the defaults frombind__default_zone_soa_*are used.optionsOptional, list of YAML dictionaries defining options for the given zone. The format is the same as for the
optionsparameter for views (see the View syntax above). Valid configuration options can be found here and the valid dict parameters are the same as for bind__*_configuration.contentOptional, string or a list of strings/dicts. The resource records which should be present in the zone file in addition to the
SOArecord (which is always present). If this value is defined (even as an empty list), the zone file will be generated for apresentzone. If defined as a string or list of strings, the string(s) will be added as-is to the zone file.If defined as a list of dicts, the valid dict options are:
nameRequired, string. Name of a given resource record. Multiple resource records with the same
nameare merged together. Note that a resource record which doesn't end with a period will be interpreted by BIND as being a relative record and have the zone'soriginappended to it (so, assuming anoriginofexample.com.,test.example.comwill be interpreted astest.example.com.example.com.whiletestortest.example.com.will both refer totest.example.com.).stateOptional, string. If not specified or
present, the resource record will be present in the generated configuration. Ifcomment, the resource record will be present but commented out. Any other value means that the resource record will not be included in the generated zone file.commentOptional, string. A comment to include before the resource record in the zone file.
rawOptional, string. A string which will be included verbatim in the zone file. If this option is defined, the options below will be ignored.
ownerOptional, string. The owner (read: name) of the resource record. If not defined,
namewill be used as the owner/name of the resource record.ttlOptional, string or integer. The TTL of the resource record (defined in the same way as the zone
ttlabove).classOptional, string. The class of the resource record. It is quite unlikely that any other class than
IN(the default) is desired.typeRequired, string. The type of the resource record (e.g.
A,AAAA, orCNAME).value,rdataRequired, string. The data for the resource record (e.g.
192.168.2.1for a record of typeA).valueandrdataare synonyms.