From f61640ec3922aabeb498dc9ac093961282d8f8ca Mon Sep 17 00:00:00 2001
From: lilly
Date: Thu, 30 Apr 2026 22:53:07 +0200
Subject: [PATCH] enable auth-dns role to actually configure useful zones
---
roles/auth-dns/defaults/main.yaml | 2 +
roles/auth-dns/handlers/main.yaml | 14 ++++-
roles/auth-dns/meta/argument_specs.yaml | 59 +++++++++++++++++++
roles/auth-dns/tasks/02-configure.yaml | 41 +++++++++++++
roles/auth-dns/templates/knot.conf.j2 | 55 +++++++++++++----
.../templates/netplan-disable-ra.yaml | 14 +++++
roles/auth-dns/templates/zone.j2 | 4 ++
7 files changed, 175 insertions(+), 14 deletions(-)
create mode 100644 roles/auth-dns/defaults/main.yaml
create mode 100644 roles/auth-dns/meta/argument_specs.yaml
create mode 100644 roles/auth-dns/templates/netplan-disable-ra.yaml
create mode 100644 roles/auth-dns/templates/zone.j2
diff --git a/roles/auth-dns/defaults/main.yaml b/roles/auth-dns/defaults/main.yaml
new file mode 100644
index 0000000..50a3ffb
--- /dev/null
+++ b/roles/auth-dns/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+knot__remotes: [ ]
diff --git a/roles/auth-dns/handlers/main.yaml b/roles/auth-dns/handlers/main.yaml
index 5ee0a5d..0d74c51 100644
--- a/roles/auth-dns/handlers/main.yaml
+++ b/roles/auth-dns/handlers/main.yaml
@@ -1,8 +1,18 @@
-- tags: [ 02-auth-dns ]
+- tags: [ auth-dns ]
name: restart knot
become: true
- notify: restart knot
ansible.builtin.systemd:
name: knot.service
state: restarted
+- name: reload knot zones
+ tags: [ auth-dns ]
+ become: true
+ changed_when: true
+ ansible.builtin.command: "knotc zone-reload"
+
+- name: netplan apply
+ tags: [ auth-dns ]
+ become: true
+ changed_when: true
+ ansible.builtin.command: "netplan apply"
diff --git a/roles/auth-dns/meta/argument_specs.yaml b/roles/auth-dns/meta/argument_specs.yaml
new file mode 100644
index 0000000..40a5823
--- /dev/null
+++ b/roles/auth-dns/meta/argument_specs.yaml
@@ -0,0 +1,59 @@
+---
+argument_specs:
+ main:
+ options:
+ knot__dnssec_key_id:
+ description: The id of the TSIG key which knot will use for zone transfer signing
+ type: str
+ required: true
+ knot__dnssec_key_secret:
+ description: The secret value of the TSIG key which knot will use for zone transfer signing
+ type: str
+ required: true
+ knot__remotes:
+ description:
+ - A list of definitions for remote nameservers that are used for different purposes
+ - See https://www.knot-dns.cz/docs/latest/html/reference.html#remote-section for details
+ type: list
+ elements: dict
+ required: false
+ options:
+ id:
+ type: str
+ required: true
+ address:
+ type: list
+ required: true
+ elements: str
+ knot__catalog_zones:
+ description: A list of catalog zones that will be served by knot
+ type: list
+ elements: dict
+ required: true
+ options:
+ domain:
+ type: str
+ required: true
+ notify_targets:
+ type: list
+ elements: str
+ required: false
+ knot__zones:
+ description: A list of user zones that will be served by knot
+ type: list
+ elements: dict
+ required: true
+ options:
+ domain:
+ type: str
+ required: true
+ notify_targets:
+ type: list
+ elements: str
+ required: false
+ catalog_member:
+ type: str
+ required: false
+ content:
+ type: str
+ required: true
diff --git a/roles/auth-dns/tasks/02-configure.yaml b/roles/auth-dns/tasks/02-configure.yaml
index 661864c..89116a4 100644
--- a/roles/auth-dns/tasks/02-configure.yaml
+++ b/roles/auth-dns/tasks/02-configure.yaml
@@ -1,3 +1,14 @@
+- tags: [ auth-dns ]
+ name: Ensure required directories exist
+ become: true
+ loop: [ "/etc/knot", "/etc/knot/zones" ]
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: directory
+ owner: knot
+ group: knot
+ mode: u=rwx,g=rx,o=
+
- tags: [ auth-dns ]
name: Deploy knot configuration file
become: true
@@ -9,3 +20,33 @@
group: knot
mode: u=rw,g=r,o=
+- name: Deploy configured zones
+ tags: [ auth-dns ]
+ become: true
+ notify: reload knot zones
+ loop: "{{ knot__zones }}"
+ loop_control:
+ label: "{{ item.domain }}"
+ vars:
+ zone_content: "{{ item.content }}"
+ ansible.builtin.template:
+ src: zone.j2
+ dest: "/etc/knot/zones/{{ item.domain }}zone"
+ owner: knot
+ group: knot
+ mode: u=rw,g=r
+
+# this seems weird but hear me out:
+# if we don't disable SLAAC, the node automatically gets an address based on IPv6 Router-Advertisements
+# this results in outgoing zone transfers failing because knot will prefer to use the dynamic address over the statically configured one.
+# so because we are configuring a DNS Nameserver where known IP-Addresses are actually important for ACL reasons, SLAAC is disabled
+- name: Disable IPv6 SLAAC
+ tags: [ auth-dns ]
+ become: true
+ notify: netplan apply
+ ansible.builtin.template:
+ src: "netplan-disable-ra.yaml"
+ dest: "/etc/netplan/10-disable-ra.yaml"
+ owner: root
+ group: root
+ mode: u=rw,g=,o=
diff --git a/roles/auth-dns/templates/knot.conf.j2 b/roles/auth-dns/templates/knot.conf.j2
index d0e5a5a..243c0b7 100644
--- a/roles/auth-dns/templates/knot.conf.j2
+++ b/roles/auth-dns/templates/knot.conf.j2
@@ -15,13 +15,21 @@ database:
storage: "/var/lib/knot"
key:
- - id: auth-dns.hamburg.ccc.de
+ - id: {{ knot__dnssec_key_id }}
algorithm: hmac-sha512
- secret: ""
+ secret: "{{ knot__dnssec_key_secret }}"
remote:
+ # static, external and public remote used for DNSSEC KSK checking
- id: quad9
address: "2620:fe::fe"
+ {% if knot__remotes -%}
+ # additional remotes used in the config
+ {% for i_remote in knot__remotes -%}
+ - id: "{{ i_remote.id }}"
+ address: [ {% for i_addr in i_remote.address %}"{{ i_addr}}"{% if not loop.last %},{% endif %} {% endfor %} ]
+ {% endfor %}
+ {% endif %}
# define how the presence of parent KSK keys is checked
# in this case, we just ask quad9 which is an open resolver
@@ -31,7 +39,7 @@ submission:
parent-delay: 1h
# define how dnssec signing is done
-# in this case we don't do anything special but teach knot how to check of KSK presence
+# in this case we don't do anything special but teach knot how to check for KSK presence
policy:
- id: default
ksk-submission: default
@@ -40,25 +48,48 @@ policy:
# define default settings that apply to all zones
template:
+ # template for general-purpose user zones
- id: default
storage: "/etc/knot/zones"
file: "%s.zone"
semantic-checks: on
zonefile-sync: -1
zonefile-load: difference-no-serial
+ serial-policy: dateserial
journal-content: all
- default-ttl: 60
- catalog-role: member
- catalog-zone: hamburg.ccc.de.catalog.
+ default-ttl: 7200
dnssec-signing: on
dnssec-policy: default
- {# notify: ["ns1.hanse.de", "ns.bsd.network."] #}
- - id: minimal
- {# notify: ["ns1.hanse.de", "ns.bsd.network."] #}
+ {# catalog-role: member #}
+ {# catalog-zone: hamburg.ccc.de.catalog. #}
+ # template for automatically created special zones
+ - id: catalog
+ catalog-role: generate
+ dnssec-signing: on
+ dnssec-policy: default
+
+
+# define zones on this server
+# See https://www.knot-dns.cz/docs/3.4/html/reference.html#zone-section
zone:
- {# - domain: onsite.eurofurence.catalog. #}
- {# template: minimal #}
- {# catalog-role: generate #}
+ # catalog zones
+ {% for i_zone in knot__catalog_zones -%}
+ - domain: "{{ i_zone.domain }}"
+ template: catalog
+ notify: [ {% for i_notif in i_zone.notify_targets | default([]) %}"{{ i_notif }}"{% if not loop.last %}, {% endif %}{% endfor %} ]
+ {% endfor %}
+
+ # normal zones
+ {% for i_zone in knot__zones -%}
+ - domain: "{{ i_zone.domain }}"
+ template: default
+ notify: [ {% for i_notif in i_zone.notify_targets | default([]) %}"{{ i_notif }}"{% if not loop.last %}, {% endif %}{% endfor %} ]
+ {% if i_zone.catalog_member | default(False) -%}
+ catalog-role: member
+ catalog-zone: "{{ i_zone.catalog_member }}"
+ {% endif %}
+ {% endfor %}
+
{# - domain: "onsite.eurofurence.org" #}
diff --git a/roles/auth-dns/templates/netplan-disable-ra.yaml b/roles/auth-dns/templates/netplan-disable-ra.yaml
new file mode 100644
index 0000000..505fba2
--- /dev/null
+++ b/roles/auth-dns/templates/netplan-disable-ra.yaml
@@ -0,0 +1,14 @@
+# {{ ansible_managed }}
+network:
+ ethernets:
+ {%- for i_iface_name in ansible_interfaces -%}
+ {%- if i_iface_name != "lo" -%}
+ {%- set i_iface = ansible_facts[i_iface_name] %}
+
+ {{ i_iface_name }}:
+ match:
+ macaddress: "{{ i_iface.macaddress }}"
+ accept-ra: false
+ {% endif %}
+ {% endfor %}
+
diff --git a/roles/auth-dns/templates/zone.j2 b/roles/auth-dns/templates/zone.j2
new file mode 100644
index 0000000..59edf5f
--- /dev/null
+++ b/roles/auth-dns/templates/zone.j2
@@ -0,0 +1,4 @@
+; {{ ansible_managed }}
+
+{{ zone_content }}
+