From 9c3e24af86d301b42947286f9add40e36a91eaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vi=C3=B1ar=20Ulriksen?= Date: Mon, 30 May 2022 18:16:32 -0300 Subject: [PATCH] WIP: addressing @andrespias review 2/6/22 11:28 --- README.md | 69 ++++++++++++++++-- defaults/main.yml | 27 +++++++ templates/bind/named.conf.local.j2 | 2 +- templates/bind/named.conf.options.j2 | 2 +- .../bind/named.conf.local.j2 | 70 +++++++++++++------ .../bind/named.conf.options.j2 | 14 +++- 6 files changed, 155 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 30eaf57..2102a10 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ zones_my_domains: 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 + master: 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 @@ -119,15 +119,15 @@ And similarly `zone_my_reverse_inaddr_arpa` and `zone_my_reverse_ip6_arpa` for I 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. -Configuration is based on a set of themplates, and the role can handle several set of templates. Presently two sets of templates are proposed: +Configuration is based on a set of templates, 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: +Templates' set is defined by variable `bind9_templates`. You should set it to the absolute path ot the relative one to the `templates/` directory of the role. For [strict authoritative NS config](templates/strict_authoritative/), define in your vartiables: ```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. +Several variables of the role define options and values in the set of templates you use. Note that the same role's variable of the role may have slightly different meanings, or no meaning at all, depending on the choosen set of templates. You can develop your own set of templates and set, for instance: ```yaml @@ -135,7 +135,66 @@ bind9_templates: "{{ playbook_dir }}/host_vars//templates/" ``` PRs with good bind9 configs templates are welcome! -## Role and templates varibles +### Role templates' variables + +#### `strict_authoritative` templates' set + +This template is designed to configure strict [Authoritative Name Servers](https://bind9.readthedocs.io/en/latest/chapter3.html#config-auth-samples), primaries or secondaries. Queries are refused except for the zones we are authoritative for: +* transfers are selectively set by zone and no recursion at all. When we answer, we give th same answer to the whole internet (for public internet zones, baroque configurations that restrict answers or, worse, give different answers to different clients, such as with views, are bad ideas that break the internet, considering DNS is a core part of it). +* customization proposed does it best to guess hosts that must be allowed zone transfer (secundaries and also-notify elements) and tries to implement all kind of sets of primaries or secudaries, visible or hideden. + +Therefore, when you set `bind9_templates: strict_authoritative/`, in option's BIND configuration `options` section, this templates always set: +``` +recursion no; +allow-query { none; }; +allow-transfer { none; }; +allow-recursion { none; }; +``` + +Default configuration values are set with `bind9_` role variables, that can be overwrite with specific values for each zone in the `.` field in the corresponding element of `bind9_zones_static` or `bind9_zones_static`. + +Depending on thee parameter considered, the templates implement the default value either in the `options` section of BIND config files (`notify`, `also-notify`), either picking default values and set them in the zone's configuration section (`allow-query`, `allow-transfer`), and can handle all sort of particular cases for some zones. + +The role does it best to also include secundaries NS IPs and also-notify IPs either default one or specific values overwritten for the zone in the zone's `allow-transfer` configuration directive. + + zone, which doesn't handle masters lists, but ACLs. If you use masters list in this variable, you MUST also overwrite the default definition of `bind9_also_allow_transfer` hereafter. + +`bind9_masters` and `bind9_slaves` should be enough for standard internet zones and if you have the same set of NS authoritative servers for your zones. Use `bind9_also_notify` if you have some hidden NS servers, but only put there a list of IPs if you want to take advantage of all the benefits of the role in setting appropiate values wherever needed. If you have some specific configuration of some zones, `bind9_masters_extra` can help you for different sets of masters. `bind9_acl` and specific values for zones listed in `bind9_zones_static` and `bind9_zones_dynamic` will help you to set all kind of transfers, notifications and even to restric query for eventual private zones. + +Explicitely, ou can use the following variables to configure your NS server and its zones: +* `bind9_masters`: default master NS servers for zones we are secondary for. +```yaml +bind9_masters: + my_primariy: + - IPv4_1 + - IPv6_1 + my_fault_back_primary: + - IPv4_1 + - IPv6_1 +``` +With these lists the template builds the [primaries' lists](https://bind9.readthedocs.io/en/latest/reference.html?highlight=primaries%20list#primaries-statement-grammar) to be used by default as masters for slave zones, when these are not specifically set for the zone. + +`bind9_masters_extra` is a similar structure that also sets primaries' lists, but are not default values for zones. `bind9_masters` and `bind9_masters_extra` can be used anywhere such lists are valid for bind, i.e. to set masters and also-notify zone's lists. + +* `bind9_acl` has a similar structure with kewords and list of IPs but, in BIND configuration, it builds global [Access Control lists](https://bind9.readthedocs.io/en/latest/chapter6.html#access-control-lists), to be used in other appropriate parameters, particularly in the `allow-xxx` zone by zone. + +* `bind9_slaves` defines the default slave NS servers for zones we are master for. It's a list of IPs, or eventually ACLs defined with previous variable, and it's used to set the hosts that are allowed to transfer the zone. + +The YAML structure of ther variables allows to overcome and unify BIND's management of two kind of lists: masters and acl. The templates take advantage of this characteristic for default configuration + +* `bind9_notify` can take the values `explicit`, `yes`, `no`, or any other form of boolean values. It defines the defaut behavior for notification, wich is set in [`options` section](templates/strict_authoritative/bind/named.conf.options.j2#L41). `bind9_notify_explicit` is a legacy boolean variable, false by default, which sets `bind9_notify`'s value to `explicit` when `bind9_notify` is not defined. Letting `bind9_notify` undefined leads to BIND's default behavior, i.e. `notify: yes`. + +* `bind9_also_notify` is a list that defines the global `also-noitfy`. As such, it can contain IPs or masters list, that you can set with the variables `bind9_masters` and `bind9_masters_extra` hereabove. However, the role does it best to also include these IPs in the `allow-transfer` directive of the zone, which doesn't handle masters lists, but ACLs. If you use masters list in this variable, you MUST also overwrite the default definition of `bind9_also_allow_transfer` hereafter. + +* `bind9_also_allow_transfer` is a variable that can contain a list of IPs and ACL names that you can define with + +Zone by zone, in `bind9_zones_static` as well as in `bind9_zones_dynamic` list elements, you can define the following zone configuration parameters: + +* `.allow-query`: a list of IPs and/or acls to restrict the clients that can ask for the zone. Default value is `any`. (breaks the internet: use with care, only for private zones) +* `.notify`: `explicit`, `yes`, `no`, or any other form of boolean values. Sets notification behavior for the zone, +* `.slaves`: can be defined for zones we are master for. It will overwrite `bind9_slaves` for the zone, and the hosts it contains will be allowed for transfer, except if the parameter `.allow_transfer` hereafter is defined. + +https://bind9.readthedocs.io/en/latest/reference.html?highlight=also-notify#zone-options See `defaults/main.yml` for a list of role variables and some doc. diff --git a/defaults/main.yml b/defaults/main.yml index db62cec..344e0f7 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -34,6 +34,8 @@ bind9_hidden_master: no # Necessary to keep traffic between nameservers in private network. bind9_notify_explicit: no +bind9_notify: '{{ "explicit" if bind9_notify_explicit else undef }}' + # Default zone type bind9_zone_type: master @@ -80,6 +82,31 @@ bind9_rndc_algorithm: hmac-md5 # Let's progressively rename this variable with bind's preferred terminology: # bind9_secondaries: "{{ bind9_slaves }}" +# bind9_acl: +# undefined by default, this variable allows to define a set of several access control lists (ACL) +# with the same format as `bind9_masters`, and use it in slaves. allow-query or allow-transfer definitions + +# bind9_also_notify: +# undefined by default, a list of hosts or of masters lists to be defined as global `notify-also` list in configuration. + +# bind9_also_allow_transfer: +bind9_also_allow_transfer: '{{ bind9_also_notify if bind9_also_notify is defined else undef }}' +# defaults to bind9_also_notify, but this definitio must be overwritten if this vairable contains masters names. +# As far as bind9_also_notify is just a list of IPs, default values of the role take advantage of the similar strucuture +# in YAML for lasters lists and ACLs, and the can by default allow transfer, zone by zone, to slaves and also notify IPS + +# bind9_also_allow_transfer +bind9_also_allow_transfer: '{{ bind9_also_notify if bind9_also_notify is defined else undef }}' +# for primary zones, except if `allow_transfer` is explicitely defined for the zone, by default an `allow-transfer` list +# will be set, iincluding slave NS of the host and either the list also_allow_transfer defined for the zone, either this +# default list. +# If `bind9_also_notify` is defined by default `bind9_also_allow_transfer` has the same values. +# But be carefull: in BIND9 configuration, `also-notify` may include `masters` lists but not `acl` ones, while +# `allow-transfer` may include `acl` lists but not `masters` ones. In YAML role's variables structures are identical, but +# if they appear in BIND configuration list inclusions it will fail. +# Practically: if you use `masters` lists (defined with `bind9_masters`or `bind9_masters_extra` variables of this role), +# yo must re-define separately `bind9_also_allow_transfer`, probably defining an ACL with same values than master lists. + # Enable BIND's XML statistics-channels (for monitoring purposes) bind9_statistics_enabled: False diff --git a/templates/bind/named.conf.local.j2 b/templates/bind/named.conf.local.j2 index 1e7fb55..d5ecb61 100644 --- a/templates/bind/named.conf.local.j2 +++ b/templates/bind/named.conf.local.j2 @@ -12,7 +12,7 @@ statistics-channels { {% endif %} {% if bind9_masters|default() %} -// masters for zones and alñlow-notify +// masters for zones and allow-notify {% for master in bind9_masters %} masters "{{ master.name }}" { {% for addr in master.addresses %} diff --git a/templates/bind/named.conf.options.j2 b/templates/bind/named.conf.options.j2 index 1f2df03..a0a33b3 100644 --- a/templates/bind/named.conf.options.j2 +++ b/templates/bind/named.conf.options.j2 @@ -35,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() %}none{% else %}our_networks{% endif %}; }; + allow-query { {% if bind9_hidden_master|default() %}our_neighbors{% elif bind9_authoritative|default() %}any{% else %}our_networks{% endif %}; }; {% if bind9_authoritative|default() %} allow-transfer { our_neighbors; }; diff --git a/templates/strict_authoritative/bind/named.conf.local.j2 b/templates/strict_authoritative/bind/named.conf.local.j2 index cf5388d..5f3a393 100644 --- a/templates/strict_authoritative/bind/named.conf.local.j2 +++ b/templates/strict_authoritative/bind/named.conf.local.j2 @@ -12,7 +12,7 @@ statistics-channels { inet 127.0.0.1 port 8053 allow { 127.0.0.1; }; }; {% endif %} -{% if bind9_masters|default() %} +{% if bind9_masters is defined %} {% for master in bind9_masters %} masters "{{ master.name }}" { {% for addr in master.addresses %} @@ -21,7 +21,7 @@ masters "{{ master.name }}" { }; {% endfor %} {% endif %} -{% if bind9_masters_extra|default() %} +{% if bind9_masters_extra is defined %} {% for master in bind9_masters_extra %} masters "{{ master.name }}" { {% for addr in master.addresses %} @@ -43,8 +43,9 @@ acl "{{ acl_item.name }}" { {% endif %} // The following zones are managed by this DNS Server // -{% for zone in (bind9_zones_static + bind9_zones_dynamic)|sort(attribute='name') %} -{% set zone_type = zone.type|default(bind9_zone_type|default('master')) %} +{% for zone in ( bind9_zones_static + bind9_zones_dynamic ) | sort( attribute='name' ) %} +{% set zone_type = zone.type | default( bind9_zone_type | default( 'master' ) ) %} +{# ############################### Zone is master ####################################### #} zone "{{ zone.name }}" { type {{ zone_type }}; {% if zone_type == 'master' %} @@ -59,30 +60,34 @@ zone "{{ zone.name }}" { any; }; {% endif %} -{% set zone_secondaries = zone.secondaries | default( bind9_secondaries | default( [] ) ) %} -{% set zone_allow_transfer = zone.allow_transfer | default( zone_secondaries + zone.also_allow_transfer | default( bind9_also_allow_transfer | default( [] ) ) ) %} -{% if zone_allow_transfer %} - - // allow transfer from secundaries and extra also_allow_transfer hosts - allow-transfer { -{% for allow_transfer_item in zone_allow_transfer %} - {{ allow_transfer_item }}; -{% endfor %} - }; -{% endif %} -{% if bind9_notify_explicit %} +{% if zone.notify is defined %} +{% if zone.notify == 'explicit' %} notify explicit; -{% else %} - notify {{ zone.notify | default(true) | ternary ('yes','no') }}; +{% else %} + notify {{ zone.notify | ternary ('yes','no') }}; +{% endif %} {% endif %} -{% set zone_also_notify = zone.also_notify | default( bind9_also_notify | default() ) %} -{% if zone_also_notify %} +{% set zone_also_notify = zone.also_notify | default( bind9_also_notify | default( [] ) ) %} +{% if zone.also_notify is defined and zone.also_notify | length() > 0 %} also-notify { -{% for also_notify_item in zone_also_notify %} +{% for also_notify_item in zone.also_notify | list %} {{ also_notify_item }}; {% endfor %} }; {% endif %} +{% set zone_slaves = zone.slaves | default( bind9_slaves | default( [] ) ) %} +{% set zone_also_allow_transfer = zone.also_allow_transfer | default( bind9_also_allow_transfer | default( [] ) ) %} + +{% set zone_allow_transfer = zone.allow_transfer | default( zone_slaves + zone_also_allow_transfer ) %} +{% if zone_allow_transfer | bool %} + + // allow transfer from secundaries and other hosts + allow-transfer { +{% for allow_transfer_item in zone_allow_transfer %} + {{ allow_transfer_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; @@ -92,6 +97,7 @@ zone "{{ zone.name }}" { grant {{ zone.name }}_ddns_update {{ zone.update_policy_grant }}; }; {% endif %} +{# ############################### Zone is slave ####################################### #} {% elif zone_type == 'slave' %} file "/var/lib/bind/db.{{ zone.name }}"; {% if zone.masters | default() or bind9_masters | default() %} @@ -106,8 +112,30 @@ zone "{{ zone.name }}" { {{ master.name }}; {% endfor %} {% endif %} + }; {% endif %} + allow-query { +{% if zone.allow_query is defined and zone.allow_query | length() > 0 %} +{% for allow_query_item in zone.allow_query | list %} + {{ allow_query_item }}; +{% endfor %} }; +{% else %} + any; + }; +{% endif %} +{% set zone_slaves = zone.slaves | default( bind9_slaves | default( [] ) ) %} +{% set zone_allow_transfer = zone.allow_transfer | default( zone_slaves + zone.also_allow_transfer | default( bind9_also_allow_transfer | default( [] ) ) ) %} +{% if zone_allow_transfer %} + + // allow transfer from secundaries and extra also_allow_transfer hosts + allow-transfer { +{% for allow_transfer_item in zone_allow_transfer %} + {{ allow_transfer_item }}; +{% endfor %} + }; +{% endif %} +{# ############################### Zone is forward ####################################### #} {% elif zone_type == 'forward' %} forwarders { {% for fwd in zone.forwarders %} diff --git a/templates/strict_authoritative/bind/named.conf.options.j2 b/templates/strict_authoritative/bind/named.conf.options.j2 index 9472a17..aa8c0f5 100644 --- a/templates/strict_authoritative/bind/named.conf.options.j2 +++ b/templates/strict_authoritative/bind/named.conf.options.j2 @@ -37,10 +37,22 @@ options { allow-query { none; }; allow-transfer { none; }; allow-recursion { none; }; -{% if bind9_notify_explicit|default() %} + +{% if bind9_notify is defined %} +{% if bind9_notify == 'explicit' %} // Notify only nameservers from also-notify, not from NS RRs in zones notify explicit; +{% else %} + notify {{ bind9_notify | ternary ('yes','no') }}; +{% endif %} +{% endif %} +{% if bind9_also_notify is defined and bind9_also_notify | length() > 0 %} + also-notify { +{% for notify_item in bind9_also_notify | list %} + notify_item; +{% endfor %} + }; {% endif %} {% if bind9_dnssec|default() %}