# Ansible role BIND [![Actions Status](https://github.com/bertvv/ansible-role-bind/workflows/CI/badge.svg)](https://github.com/bertvv/ansible-role-bind/actions) An Ansible role for setting up ISC BIND as an **authoritative-only** DNS server for multiple domains. Specifically, the responsibilities of this role are to: - install BIND - set up the main configuration file (primary/secondary/forwarder server) - set up forward and reverse lookup zone files This role supports multiple forward and reverse zones, including for IPv6. Although enabling recursion is supported (albeit *strongly* discouraged), consider using another role if you want to set up a caching or forwarding name server. If you like/use this role, please consider giving it a star and rating it on the role's [Ansible Galaxy page](https://galaxy.ansible.com/bertvv/bind). Thanks! See the [change log](CHANGELOG.md) for notable changes between versions. **WARNING:** If you've been using this role since **before v5.0.0**, please check the change log for important information on breaking changes. Old playbooks will fail if you upgrade to v5.0.0. ## Supported platforms This role can be used on several platforms, see [meta/main.yml](meta/main.yml) for an updated list. We strive to set up automated tests for each supported platform (see [.ci.yml](.github/workflows/ci.yml)), but this is not always possible. A few remarks on supported roles that are not included in automated tests - **Arch Linux** and **FreeBSD** should work, but at this time, it's not possible to test the role on these distros, since no suitable Docker images are available. - **CentOS 6** should work, but idempotence tests fail even if BIND is installed successfully and acceptance tests succeed. ## Requirements The packages `python-netaddr` (required for the [`ipaddr`](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters_ipaddr.html) filter) and `dnspython` should be installed on the management node ## Role Variables | Variable | Default | Comments (type) | | :-------------------------- | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `bind_acls` | `[]` | A list of ACL definitions, which are mappings with keys `name:` and `match_list:`. See below for an example. | | `bind_allow_query` | `['localhost']` | A list of hosts that are allowed to query this DNS server. Set to ['any'] to allow all hosts | | `bind_allow_recursion` | `['any']` | Similar to `bind_allow_query`, this option applies to recursive queries. | | `bind_check_names` | `[]` | Check host names for compliance with RFC 952 and RFC 1123 and take the defined action (e.g. `warn`, `ignore`, `fail`). | | `bind_dns_keys` | `[]` | A list of binding keys, which are mappings with keys `name:` `algorithm:` and `secret:`. See below for an example. | | `bind_dns64` | `false` | If `true`, support for [DNS64](https://www.oreilly.com/library/view/dns-and-bind/9781449308025/ch04.html) is enabled | | `bind_dns64_clients` | `['any']` | A list of clients which the DNS64 function applies to (can be any ACL) | | `bind_dnssec_enable` | `true` | If `true`, DNSSEC is enabled | | `bind_dnssec_validation` | `true` | If `true`, DNSSEC validation is enabled | | `bind_extra_include_files` | `[]` | A list of custom config files to be included from the main config file | | `bind_forward_only` | `false` | If `true`, BIND is set up as a caching name server | | `bind_forwarders` | `[]` | A list of name servers to forward DNS requests to. | | `bind_listen_ipv4` | `['127.0.0.1']` | A list of the IPv4 address of the network interface(s) to listen on. Set to ['any'] to listen on all interfaces. | | `bind_listen_ipv4_port` | `[53]` | A list of port numbers to listen on for IPv4 addresses. | | `bind_listen_ipv6` | `['::1']` | A list of the IPv6 address of the network interface(s) to listen on | | `bind_listen_ipv6_port` | `[53]` | A list of port numbers to listen on for IPv6 addresses. | | `bind_log` | `data/named.run` | Path to the log file | | `bind_other_logs` | - | A list of logging channels to configure, with a separate mapping for each zone, with relevant details | | `bind_query_log` | - | A mapping with keyss `file:` (e.g. `data/query.log`), `versions:`, `size:`. When defined, this will enable the query log | | `bind_recursion` | `false` | Determines whether requests for which the DNS server is not authoritative should be forwarded†. | | `bind_rrset_order` | `random` | Defines order for DNS round robin (either `random` or `cyclic`) | | `bind_statistics_channels` | `false` | If `true`, BIND is configured with a `statistics-channels` clause (currently only supports listening on a single interface) | | `bind_statistics_allow` | `['127.0.0.1']` | A list of hosts that can access the server statistics | | `bind_statistics_host` | `127.0.0.1` | IP address of the network interface that the statistics service should listen on | | `bind_statistics_port` | 8053 | Network port that the statistics service should listen on | | `bind_zone_dir` | - | When defined, sets a custom absolute path to the server directory (for zone files, etc.) instead of the default. | | `bind_key_mapping` | [] | `Primary: Keyname` - mapping of TSIG keys to use for a specific primary | | `bind_zones` | n/a | A list of mappings with zone definitions. See below this table for examples | | `- allow_update` | `['none']` | A list of hosts that are allowed to dynamically update this DNS zone. | | `- also_notify` | - | A list of servers that will receive a notification when the primary zone file is reloaded. | | `- create_forward_zones` | - | When initialized and set to `false`, creation of forward zones will be skipped (resulting in a reverse only zone) | | `- create_reverse_zones` | - | When initialized and set to `false`, creation of reverse zones will be skipped (resulting in a forward only zone) | | `- delegate` | `[]` | Zone delegation. | | `- forwarders` | - | List of forwarders for for the forward type zone | | `- hostmaster_email` | `hostmaster` | The e-mail address of the system administrator for the zone | | `- hosts` | `[]` | Host definitions. | | `- ipv6_networks` | `[]` | A list of the IPv6 networks that are part of the domain, in CIDR notation (e.g. 2001:db8::/48) | | `- mail_servers` | `[]` | A list of mappings (with keys `name:` and `preference:`) specifying the mail servers for this domain. | | `- name_servers` | `[ansible_hostname]` | A list of the DNS servers for this domain. | | `- name` | `example.com` | The domain name | | `- naptr` | `[]` | A list of mappings with keys `name:`, `order:`, `pref:`, `flags:`, `service:`, `regex:` and `replacement:` specifying NAPTR records. | | `- networks` | `['10.0.2']` | A list of the networks that are part of the domain | | `- other_name_servers` | `[]` | A list of the DNS servers outside of this domain. | | `- primaries` | - | A list of primary DNS servers for this zone. | | `- services` | `[]` | A list of services to be advertised by SRV records | | `- text` | `[]` | A list of mappings with keys `name:` and `text:`, specifying TXT records. `text:` can be a list or string. | | `- caa` | `[]` | A list of mappings with keys `name:` and `text:`, specifying CAA records. `text:` can be a list or string. | | `- type` | - | Optional zone type. If not specified, autodetection will be used. Possible values include `primary`, `secondary` or `forward` | | `bind_zone_file_mode` | 0640 | The file permissions for the main config file (named.conf) | | `bind_zone_minimum_ttl` | `1D` | Minimum TTL field in the SOA record. | | `bind_zone_time_to_expire` | `1W` | Time to expire field in the SOA record. | | `bind_zone_time_to_refresh` | `1D` | Time to refresh field in the SOA record. | | `bind_zone_time_to_retry` | `1H` | Time to retry field in the SOA record. | | `bind_zone_ttl` | `1W` | Time to Live field in the SOA record. | | `bind_python_version` | - | The python version that should be used for ansible. Depends on Distro, either `2` or `3`. Defaults to the OS standard | † Best practice for an authoritative name server is to leave recursion turned off. However, [for some cases](http://www.zytrax.com/books/dns/ch7/queries.html#allow-query-cache) it may be necessary to have recursion turned on. ### Minimal variables for a working zone In order to set up an authoritative name server that is available to clients, you should at least define the following variables: | Variable | Primary | Secondary | Forward | | :----------------- | :-----: | :-------: | :-----: | | `bind_allow_query` | V | V | V | | `bind_listen_ipv4` | V | V | V | | `bind_zones` | V | V | V | | `- hosts` | V | -- | -- | | `- name_servers` | V | -- | -- | | `- name` | V | V | -- | | `- networks` | V | V | V | | `- primaries` | V | V | -- | | `- forwarders` | -- | -- | V | ### Domain definitions ```Yaml bind_zones: # Example of a primary zone (hosts: and name_servers: ares defined) - name: mydomain.com # Domain name create_reverse_zones: false # Skip creation of reverse zones primaries: - 192.0.2.1 # Primary server(s) for this zone name_servers: - pub01.mydomain.com. - pub02.mydomain.com. hosts: - name: pub01 ip: 192.0.2.1 ipv6: 2001:db8::1 aliases: - ns1 - name: pub02 ip: 192.0.2.2 ipv6: 2001:db8::2 aliases: - ns2 - name: '@' # Enables "http://mydomain.com/" ip: - 192.0.2.3 # Multiple IP addresses for a single host - 192.0.2.4 # results in DNS round robin sshfp: # Secure shell fingerprint - "3 1 1262006f9a45bb36b1aa14f45f354b694b77d7c3" - "3 2 e5921564252fe10d2dbafeb243733ed8b1d165b8fa6d5a0e29198e5793f0623b" ipv6: - 2001:db8::2 - 2001:db8::3 aliases: - www - name: priv01 # This IP is in another subnet, will result in ip: 10.0.0.1 # multiple reverse zones - name: mydomain.net. aliases: - name: sub01 type: DNAME # Example of a DNAME alias record networks: - '192.0.2' - '10' - '172.16' delegate: - zone: foo dns: 192.0.2.1 services: - name: _ldap._tcp weight: 100 port: 88 target: dc001 naptr: # Name Authority Pointer record, used for IP - name: "sip" # telephony order: 100 pref: 10 flags: "S" service: "SIP+D2T" regex: "!^.*$!sip:customer-service@example.com!" replacement: "_sip._tcp.example.com." # Minimal example of a secondary zone - name: acme.com primaries: - 172.17.0.2 networks: - "172.17" # Minimal example of a forward zone - name: acme.com forwarders: - 172.17.0.2 networks: - "172.17" ``` ### Hosts Host names that this DNS server should resolve can be specified in `bind_zones.hosts` as a list of mappings with keys `name:`, `ip:`, `aliases:` and `sshfp:`. Aliases can be CNAME (default) or DNAME records. To allow to surf to `http://example.com/`, set the host name of your web server to `'@'` (must be quoted!). In BIND syntax, `@` indicates the domain name itself. If you want to specify multiple IP addresses for a host, add entries to `bind_zones.hosts` with the same name (e.g. `priv01` in the code snippet). This results in multiple A/AAAA records for that host and allows [DNS round robin](http://www.zytrax.com/books/dns/ch9/rr.html), a simple load balancing technique. The order in which the IP addresses are returned can be configured with role variable `bind_rrset_order`. ### Networks As you can see, not all hosts are in the same subnet. This role will generate suitable reverse lookup zones for each subnet. All subnets should be specified in `bind_zones.networks`, though, or the host will not get a PTR record for reverse lookup. Remark that only the network part should be specified here! When specifying a class B IP address (e.g. "172.16") in a variable file, it must be quoted. Otherwise, the Yaml parser will interpret it as a float. Based on the idea and examples detailed at for the gdnsd package, the zone files are fully idempotent, and thus only get updated if "real" content changes. ### Zone types and Zone type auto-detection Zone `type` is an optional zone parameter that defines if the zone type should be of `primary`, `secondary` or `forward` type. When `type` parameter is omitted, zone type will be autodetected based on the intersection of host IP addresses and `primaries` record when configuring primary or secondary zone. When `primaries` is not defined and `forwarders` is defined, the zone type will be set to `forward`. Zone auto-detection functionality is especially useful when deploying multi-site DNS infrastructure. It is convenient to have a "shared" `bind_zones` definitions in a single group inventory file for all dns servers ( ex. `group_vars\dns.yml`). Such an approach allows to switch between primary and secondary server(s) roles by updating `primaries` record only and rerunning the playbook. Zone type auto-detection can be tested with "shared_inventory" molecule scenario by running: `molecule test --scenario-name shared_inventory` --- **NOTE** - bind doesn't support automated [multi-master configuration](https://kb.isc.org/docs/managing-manual-multi-master) and `primaries` list should have a single entry only. - When `primaries` record is updated to switch primary to secondary server roles, zones will be wiped out and recreated from template as we yet to support dynamic updates for existing zones. --- Zone types can be also defined explicitly in per host inventory to skip autodetection: ```Yaml # Primary Server bind_zones: - name: mydomain.com type: primary primaries: - 192.0.2.1 ... # Secondary Server bind_zones: - name: mydomain.com type: secondary primaries: - 192.0.2.1 ... # Forwarder Server bind_zones: - name: anotherdomain.com type: forward forwarders: - 192.0.3.1 ``` ### Zone delegation To delegate a zone to a DNS server, it is sufficient to create a `NS` record (under delegate) which is the equivalent of: ```text foo IN NS 192.0.2.1 ``` ### Service records Service (SRV) records can be added with the services. This should be a list of mappings with mandatory keys `name:` (service name), `target:` (host providing the service), `port:` (TCP/UDP port of the service) and optional keys `priority:` (default = 0) and `weight:` (default = 0). ### ACLs ACLs can be defined like this: ```Yaml bind_acls: - name: acl1 match_list: - 192.0.2.0/24 - 10.0.0.0/8 ``` The names of the ACLs will be added to the `allow-transfer` clause in global options. ### Binding Keys Binding keys can be defined like this: ```Yaml bind_dns_keys: - name: primary_key algorithm: hmac-sha256 secret: "azertyAZERTY123456" bind_extra_include_files: - "{{ bind_auth_file }}" ``` **tip**: Extra include file must be set as an ansible variable because file is OS dependant This will be set in a file *"{{ bind_auth_file }}* (e.g. /etc/bind/auth_transfer.conf for Debian) which have to be added in the list variable **bind_extra_include_files** ### Using TSIG for zone transfer (XFR) authorization To authorize the transfer of zone between primary & secondary servers based on a TSIG key, set the mapping in the variable `bind_key_mapping`: ```Yaml bind_key_mapping: primary_ip: TSIG-keyname ``` Each primary can only have one key (per view). A check will be performed to ensure the key is actually present in the `bind_dns_keys` mapping. This will add a server statement for the `a` in `bind_auth_file` on a secondary server containing the specified key. ## Dependencies No dependencies. ## Example Playbooks See the test playbooks and inventory for an elaborate example that showcases most features. ### Standard Inventory - Variables common between all servers defined in [all.yml](molecule/default/group_vars/all.yml) - `bind_zone` variable defined on per host basis ([primary](molecule/default/host_vars/ns1.yml), [secondary](molecule/default/host_vars/ns2.yml) and [forwarder](molecule/default/host_vars/ns3.yml)) ```console ❯ tree --dirsfirst molecule/default molecule/default ├── group_vars │   └── all.yml ├── host_vars │   ├── ns1.yml # Primary │   ├── ns2.yml # Secondary │   └── ns3.yml # Forwarder ├── converge.yml ... ``` ### Shared Inventory Variables common between primary and secondary servers defined in [all.yml](molecule/shared_inventory/group_vars/all.yml) ```console ❯ tree --dirsfirst molecule/shared_inventory molecule/shared_inventory ├── group_vars │   └── all.yml ├── converge.yml ... ``` ---