Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

Feature issue5 - new set of templates, strict authoritative NS #6

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 90 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ Features:
* automatic zone DNSSEC configuration
* support to send DNSKEY/DS formatted output over XMPP
* Support for hidden primary and authoritative secondary configuration
* Basic support for dynamic creation of zone files from variables
* Support for so called "static" zones, i.e. zones defined uploading their raw .db bind file
* Validity check of zone files with named-checkzone
* Basic support for so called "dynamic" zones, i.e. defined from variables yaml variables sets

## Basic usage - master and slave server with static zones and forwarder
## Basic server configuration

* place your zone file in ansible directory (not in role directory): files/bind/zones/db.example.com
* set vars for your master server:
Lest's start by a simple but complete configuration:

### Master server

```
* set vars for your master server, for instance in `host_vars/master_name/vars/XX_bind.yml`, here with an example.com static zones and forwarder:
```yaml
bind9_authoritative: yes
bind9_zones_static:
- { name: example.com , type=master }
Expand All @@ -38,10 +41,13 @@ bind9_our_neighbors:
- slave_ip_2
- slave_ip_3
```
* set vars for your slave server:
* Place your BIND zone file in ansible directory (not in role directory): `files/bind/zones/db.example.com` The role will check the validity of this file.

### Slave servers

```
* set vars for your slave servers:

```yaml
bind9_zones_static:
- { name: example.com, type: slave }
bind9_forward: yes
Expand All @@ -52,18 +58,90 @@ bind9_masters:
- { name: master_name, addresses: [master_ip] }
bind9_recursor: our_network
```
* deploy role to your servers!

## Static zones and Dynamic zones

* deploy role to your servers
In previous example, zones' ressource records are defined by a classic BIND9 zone file, which validity is checked, but that you have to maintain. These are the so called "static zones", raw defined by a `db.<zone_name>` file.

So called "dynamic" zones' files are built form ansible variables. Theire ressource records are defined through YAML ansible structure `bind9_zones_dynamic` which is parsed by [`bind/zones/db.template.j2`](templates/bind/zones/db.template.j2) template.
As there can be several zones, and zones definitions can be long, zones vars are worthly defined in a different vars' file, for instance `host_vars/master_name/vars/YY_zones.yml`, and `bind9_zones_dynamic` can be splited in several variables, that can bie defined in specific files. In `YY_zones.yml` we may have:
```yaml
bind9_zones_dynamic: >
{{ zones_my_domains
| union ( zone_my_reverse_inaddr_arpa )
| union ( zone_my_reverse_ip6_arpa ) }}

## Dependencies
# bind9_zone_static: zone files copied from `files/bind/zones/`

For the XMPP notification feature, `python-xmpp` needs to be installed.
bind9_zones_static:
- name: static_dom.org
type: master
- name: static_dom2.org
type: master
- name: static_dom3.org
type: slave
```
And in other vars files:
```yaml
zones_my_domains:
# This is the variables set for my domain
- name: dyn_domain.org
type: master
default_ttl: 600
serial: 2022050501
refresh: 1D
retry: 2H
expire: 1000H
# NS and other pre-formatted records values must be given as full qualified domain names, with or without final dot, but not relative to the zone
primary: ns1.dyn_domain.org # Optional, if you don't define it, firs NS is taken
admin: postmaster.dyn_domain.org
ns_records:
- ns1.dyn_domain.org
- ns2.dyn_domain.org
# RR values are either relative to the zone, either with a final dot when outside.
rrs:
- {label: "@", type: MX, rdata: 10 mail}
- {label: webmail, type: CNAME, rdata: mail}
- {label: "@", type: A, rdata: 8.8.8.221}
- {label: "@", type: AAAA, rdata: 2001:db8:6a::95}
- {label: www, type: CNAME, rdata: webserver.dyn_domain.org.}
- {label: mail, type: A, rdata: 8.8.8.222}
- {label: mail, type: AAAA, rdata: 2001:db8:6a::22}
- {label: webserver, ttl: 86400, type: A, rdata: 8.8.8.223}
- {label: webserver, ttl: 86400, type: AAAA, rdata: 2001:db8:6a::23}
```

And similarly `zone_my_reverse_inaddr_arpa` and `zone_my_reverse_ip6_arpa` for IP reverse DNS resolution. Note that we adopted for generic NS records the terminology defined in [RFC 1034, Section 3.6](https://datatracker.ietf.org/doc/html/rfc1034#section-3.6)


## Configurable templates' set

## Role varibles
Basically the role builds bind9 configuration, i.e. `/etc/bind/named.conf,*` files, as well aa zone definition files, whicha are placed in `/etc/bind/zones/` directory.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typos: 'aa zone' 'whicha'.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. README still needs work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mark this unresolved 'cause these typos still here.


See `defaults/main.yml` for a list of role variables.
Configuration is based on a set of themplates, and the role can handle several set of templates. Presently two sets of templates are proposed:
* the default one, a general purpose set of templates that has evolved with the role,
* a strict authoritative NS templates' set, that denies by default any query, recursion or transfer, and only allows queries from any and transfers from slaves for zones the server is authoritative on.

Templates' set is defined by variable `bind9_templates`. For [strict authoritative NS config](templates/strict_authoritative/), you should set:
```yaml
bind9_templates: strict_authoritative/
```
Note that the same variable of the role may have different meanings, or no meaning at all, depending on the choosen set of templates.
Copy link
Member

@andrespias andrespias May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this section it is necessary to specifically clarify that if you choose your own set of templates or strict_authoritative, bind9_authoritative has no meaning at all.


En esta sección, merece especificamente aclarar que si elegimos nuestro propio conjunto de templates o las strict_authortitave, la variable bind9_authoritative queda sin efecto.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely right. The new set of templates is not yet documented. I only documented the minimum to understand how could work a version of the role with several sets of templates.


You can develop your own set of templates and set, for instance:
```yaml
bind9_templates: "{{ playbook_dir }}/host_vars/<my_host>/templates/"
```
PRs with good bind9 configs templates are welcome!

## Role and templates varibles

See `defaults/main.yml` for a list of role variables and some doc.

## Dependencies

For the XMPP notification feature, `python-xmpp` needs to be installed.

Testing & Development
---------------------
Expand Down
34 changes: 29 additions & 5 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ bind9_group: bind
bind9_ipv6: yes

# Run bind as a DNS recursor?
# variable used only by default templates, not strict_authoritative
bind9_recursor: no

# Run bind as authoritative nameserver?
# variable used only by default templates, not strict_authoritative
bind9_authoritative: no

# run bind with forwarding?
Expand All @@ -35,12 +37,17 @@ bind9_notify_explicit: no
# Default zone type
bind9_zone_type: master

## //!\\ Several of the following variables have different meanings or (no meaning at all) depending on the templates' set you use
## See here after bind9_template variable.

# Permitted hosts/networks for recursion (when configured as recursor)
# variable used only by default templates, not strict_authoritative
bind9_our_networks:
- localhost
- localnets

# Permitted hosts/networks for zone transfers
# variable used only by default templates, not strict_authoritative
bind9_our_neighbors:
- localhost
- localnets
Expand All @@ -49,21 +56,29 @@ bind9_our_neighbors:
bind9_rndc_algorithm: hmac-md5
# bind9_rndc_key:

# Global primaries for all zones (if configured as secondary)
# Global primaries for all zones (if configured as secondary), default masters if not defined in the zone
# bind9_masters:
# - name: ns-primary
# addresses:
# - 1.2.3.4
# For BIND 9.17.3 (not yet in debian): https://downloads.isc.org/isc/bind9/9.17.3/doc/arm/html/notes.html#feature-changes
# Let's progressively rename this variable with bind's preferred terminology:
# bind9_primaries: "{{ bind9_masters }}"

# Primaries for particular zones (if configured as secondary)
# Primaries for particular zones (if configured as secondary), that can also be used in also-notify directives
# bind9_masters_extra:
# - name: ns-primary
# - name: "ns-primary"
# addresses:
# - 1.2.3.4
# Let's progressively rename this variable with bind's preferred terminology:
# bind9_primaries_extra: "{{ bind9_masters_extra }}"

# Global secondaries for all zones (if configured as primary)
# Global secondaries for all zones (if configured as primary), default slaves if not specifically defined for the zone
# bind9_slaves:
# - 1.2.3.4
#
# Let's progressively rename this variable with bind's preferred terminology:
# bind9_secondaries: "{{ bind9_slaves }}"

# Enable BIND's XML statistics-channels (for monitoring purposes)
bind9_statistics_enabled: False
Expand Down Expand Up @@ -113,7 +128,16 @@ bind9_packages:

# Directory for bind9 files templates
bind9_templates: ""
# The default value takes templates form the {{ role_path }}/templates/ directory of the role
# The role can handle different sets of templates for bind and zones configuration.
# It presently proposes two sets of templates:
# * the defaults one, "", wich is a general purpose configuration set, that has evolved with the role.
# It's files live in {{ role_path }}/templates/ directory
# * a second new set for a strict authoritative bind NS server: `strict_authoritative` It accepts DNS queries only for zones it is authoritative for.
# allow-transfer for secunday NS servers, notify-also for hidden slaves, and even allow-query and notify can be defined zone by zone.
# It's files live in {{ role_path }}/templates/strict_authoritative/ directory
# Note that several default variables `bind9_*` have different meanings than with default templates' set.
# bind9_templates nust be set as a relative or absolute directory, including it's trailing "/":
# bind9_templates: strict_authoritative/
# You can set your own templates, for example with:
# bind9_templates: "{{ playbook_dir }}/host_vars/<my_host>/templates/"

Expand Down
66 changes: 48 additions & 18 deletions templates/bind/named.conf.local.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";
{% if bind9_statistics_enabled %}
statistics-channels {
inet 127.0.0.1 port 8053 allow { 127.0.0.1; };
};

{% endif %}
{% if bind9_masters|default() %}
// masters for zones and alñlow-notify
ulvida marked this conversation as resolved.
Show resolved Hide resolved
{% for master in bind9_masters %}
masters {{ master.name }} {
masters "{{ master.name }}" {
{% for addr in master.addresses %}
{{ addr }};
{% endfor %}
Expand All @@ -17,18 +23,23 @@ masters {{ master.name }} {
{% endif %}
{% if bind9_masters_extra|default() %}
{% for master in bind9_masters_extra %}
masters {{ master.name }} {
masters "{{ master.name }}" {
{% for addr in master.addresses %}
{{ addr }};
{% endfor %}
};
{% endfor %}
{% endif %}
{% if bind9_acl is defined %}
ulvida marked this conversation as resolved.
Show resolved Hide resolved

{% if bind9_statistics_enabled %}
statistics-channels {
inet 127.0.0.1 port 8053 allow { 127.0.0.1; };
// Custom acls
{% for acl_item in bind9_acl %}
acl "{{ acl_item.name }}" {
{% for item_address in acl_item.addresses %}
{{ item_address }};
{% endfor %}
};
{% endfor %}
{% endif %}

// The following zones are managed by this DNS Server //
Expand All @@ -38,44 +49,63 @@ zone "{{ zone.name }}" {
type {{ zone_type }};
{% if zone_type == 'master' %}
file "/etc/bind/zones/db.{{ zone.name }}";
{% if bind9_notify_explicit|default() %}
{% if zone.allow_query is defined %}
allow-query {
{% for allow_query_item in zone.allow_query %}
{{ allow_query_item }};
{% endfor %}
};
{% endif %}
{% if zone.allow_transfer is defined %}
allow-transfer {
{% for allow_transfer_item in zone.allow_transfer %}
{{ allow_transfer_item }};
{% endfor %}
};
{% endif %}
{% if bind9_notify_explicit %}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bind9_notify_explicit is a boolean that sets a forced value, not a default value as many other global variables. It must be documented for the default template. I don't understand the reason of this behavior, so in other templates we may change it, to manage a default value and not a forced value, with a bind9_notify, eventually distinguishing masters and slaves.

notify explicit;
{% elif zone.notify|default(true) %}
notify yes;
{% elif zone.notify | default(true) %}
notify {{ zone.notify | default(true) | ternary ('yes','no') }};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the order filters are applied: What about if zone.notify is set to explicit? I guess the value will fall to yes. the ternary should apply only if something clearly true or false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, before proposing the PR I will bring back the default templates to their previous content.

{% endif %}
{% if (bind9_dnssec|default() or zone.dnssec|default()) and zone.dnssec|default(bind9_dnssec_zones_default_enabled) %}
{% if zone.also_notify is defined %}
also-notify {
{% for also_notify_item in zone.also_notify %}
{{ also_notify_item }};
{% endfor %}
};
{% endif %}
{% if (bind9_dnssec or zone.dnssec | default() ) and zone.dnssec | default( bind9_dnssec_zones_default_enabled ) %}
auto-dnssec maintain;
inline-signing yes;
{% endif %}
{% if zone.update_policy_grant|default() %}
{% if zone.update_policy_grant | default() %}
update-policy {
grant {{ zone.name }}_ddns_update {{ zone.update_policy_grant }};
};
{% endif %}
{% elif zone_type == 'slave' %}
file "/var/lib/bind/db.{{ zone.name }}";
{% if zone.masters|default() or bind9_masters|default() %}
{% if zone.masters | default() or bind9_masters | default() %}
notify no;
masters {
{% if zone.masters|default() %}
{% if zone.masters | default() %}
{% for master in zone.masters %}
{{ master }};
{% endfor %}
{% elif bind9_masters|default() %}
{% elif bind9_masters | default() %}
{% for master in bind9_masters %}
{{ master.name }};
{% endfor %}
{% endif %}
};
{% endif %}
{% else %}
{% if zone_type == 'forward' %}
{% elif zone_type == 'forward' %}
forwarders {
{% for fwd in zone.forwarders %}
{% for fwd in zone.forwarders %}
{{ fwd }};
{% endfor %}
{% endfor %}
};
{% endif %}
{% endif %}
};
{% endfor %}
15 changes: 9 additions & 6 deletions templates/bind/named.conf.options.j2
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// BIND 9 Configuration - generated by systemli.bind9 ansible role
// DO NOT edit, change your ansible config and re-run your ansible playbooks

options {
directory "/var/cache/bind";

Expand All @@ -12,9 +15,9 @@ options {

{% if bind9_forward|default() %}
forwarders {
{% for forwarder in bind9_forward_servers %}
{% for forwarder in bind9_forward_servers %}
{{ forwarder }};
{% endfor %}
{% endfor %}
};
{% endif %}

Expand All @@ -32,7 +35,7 @@ options {
recursion {{ bind9_recursor|default()|ternary('yes', 'no') }};
allow-recursion { {{ bind9_recursor|default()|ternary('our_networks', 'none') }}; };

allow-query { {% if bind9_hidden_master|default() %}our_neighbors{% elif bind9_authoritative|default() %}any{% else %}our_networks{% endif %}; };
allow-query { {% if bind9_hidden_master|default() %}our_neighbors{% elif bind9_authoritative|default() %}none{% else %}our_networks{% endif %}; };
andrespias marked this conversation as resolved.
Show resolved Hide resolved
{% if bind9_authoritative|default() %}

allow-transfer { our_neighbors; };
Expand Down Expand Up @@ -89,8 +92,8 @@ acl our_neighbors {
{% endfor %}
{% endif %}
};

{% if bind9_named_logging %}

logging {
channel bind_log {
file "{{ bind9_log_path }}/bind.log" versions {{ bind9_log_versions }} size {{ bind9_log_size }};
Expand All @@ -99,8 +102,8 @@ logging {
print-severity yes;
print-time yes;
};
{% for category in bind9_log_categories %}
{% for category in bind9_log_categories %}
category {{ category.name }} { {{ category.destination }}; };
{% endfor %}
{% endfor %}
};
{% endif %}
Loading