Compare commits

..

45 commits
master ... main

Author SHA1 Message Date
3d3c2458e0
fix: gitignore
All checks were successful
Ansible Lint / build (push) Successful in 1m31s
2024-09-08 14:32:46 +02:00
777ccd89dc
fix: run kzonecheck with explicit origin argument
All checks were successful
Ansible Lint / build (push) Successful in 56s
Ansible Galaxy / deploy (push) Successful in 2m12s
2024-06-23 15:54:38 +02:00
c7d1cde8c3
feat: add knot_dnssec_policy_nsec3_salt_length to argument_specs.yml
All checks were successful
Ansible Lint / build (push) Successful in 1m25s
Ansible Galaxy / deploy (push) Successful in 1m57s
2024-06-05 01:19:06 +02:00
4fc51962e1
feat: add knot_dnssec_policy_nsec3_salt_length with default 0
All checks were successful
Ansible Lint / build (push) Successful in 1m30s
2024-06-05 01:15:25 +02:00
2d034ea22d
fix: semantic erros in argument_specs
All checks were successful
Ansible Lint / build (push) Successful in 52s
Ansible Galaxy / deploy (push) Successful in 2m10s
2024-05-05 18:07:59 +02:00
25f85bfc52
chore: release 0.4.1
All checks were successful
Ansible Galaxy / deploy (push) Successful in 1m29s
Ansible Lint / build (push) Successful in 1m28s
2024-05-05 13:16:48 +02:00
ffc1ed7fde
fix: update min_ansible to 2.15 due to deb822_repository 2024-05-05 13:16:22 +02:00
0d364f3359
fix: update requirements.yml in docs example 2024-05-05 13:15:51 +02:00
f568b38831
fix: only start knot after rendering the configuration 2024-05-05 13:13:58 +02:00
783dbdc986
fix: ci
All checks were successful
Ansible Lint / build (push) Successful in 52s
Ansible Galaxy / deploy (push) Successful in 2m10s
2024-04-14 22:46:51 +02:00
2ae220b3eb
feat: the great ansible-lint and documentation update
Some checks failed
Ansible Galaxy / deploy (push) Failing after 1m18s
Ansible Lint / build (push) Successful in 1m27s
2024-04-14 22:41:41 +02:00
s3lph
4298c8b656 fix(knot): set journal-content: all for zonefile-serial: difference-no-serial to work 2023-12-03 09:29:56 +01:00
s3lph
c51253c28c feat(knot): use zonefile-load: difference-no-serial policy for idempotency 2023-12-03 09:25:54 +01:00
s3lph
da51d2fbf1 fix: kzonecheck is now part of knot-dnssecutils 2023-07-16 17:53:19 +02:00
s3lph
dc2bd7c570 Add support for on-secondary signing 2023-04-05 00:16:19 +02:00
s3lph
3809b6c2c3 Add support for on-secondary signing 2023-04-05 00:14:57 +02:00
s3lph
a3545b1646 Add support for secondary-to-secondary replication 2023-04-05 00:10:52 +02:00
s3lph
b3a8be8303 Add support for secondary-to-secondary replication 2023-04-05 00:00:55 +02:00
s3lph
c337831383 Add support for secondary-to-secondary replication 2023-04-04 23:53:22 +02:00
s3lph
2398e99d65 Add support for secondary-to-secondary replication 2023-04-04 23:52:13 +02:00
s3lph
55ad1892a9 Add support for secondary-to-secondary replication 2023-04-04 23:50:08 +02:00
s3lph
88996a3700 Add support for on-secondary signing 2023-04-04 20:54:24 +02:00
s3lph
b1795f2e76 Add support for on-secondary signing 2023-04-04 20:46:54 +02:00
s3lph
ce27010642 Change default cds-cdnskey-publish to always 2022-06-14 21:17:14 +02:00
s3lph
e4e9e21e8c Make cds_cndskey_publish configurable per zone 2022-06-14 12:58:45 +02:00
s3lph
b9bb083f76 Make DNSSEC algorithms configurable per zone 2022-06-13 21:40:58 +02:00
s3lph
4fe9da8a6d Add option to install knot from the upstream cz.nic repository 2022-06-13 21:21:46 +02:00
s3lph
2abd8ccf02 Change default KSK/ZSK to ed25519 2022-06-04 00:33:07 +02:00
s3lph
35ed3e09f2 Apparently reloading knot is not enough with some config changes (e.g. listen-address), restart instead 2021-11-05 12:58:53 +01:00
s3lph
7ce38e0edf Fix loop in install.yml 2021-11-05 12:52:30 +01:00
s3lph
fbd4f1c5e7 Remove duplicate line from dnssec policies 2021-10-01 01:57:04 +02:00
s3lph
bf5b1d50fb Add dnssec policy propagation-delay option 2021-09-30 21:38:04 +02:00
s3lph
837832be9b Fix submission config error 2021-09-30 00:39:52 +02:00
s3lph
4680285605 Fix config file whitespaces 2021-09-30 00:37:26 +02:00
s3lph
a339290de7 Fix config file whitespaces 2021-09-30 00:36:27 +02:00
s3lph
83fc2ba1e8 Fix config file whitespaces 2021-09-30 00:35:41 +02:00
s3lph
0482dd69e3 Bugfix 3 2021-09-30 00:08:25 +02:00
s3lph
8b86782842 Bugfix 2 2021-09-29 23:57:40 +02:00
s3lph
a35e66049d Bugfix 2021-09-29 23:56:40 +02:00
s3lph
a14b0fb04d Bump version number 2021-09-29 23:48:52 +02:00
s3lph
6ca7fe89ab Bump version number 2021-09-29 23:27:14 +02:00
s3lph
6e3674e4ff Update documentation 2021-09-29 23:26:54 +02:00
s3lph
a9be647f84 Add support for configuring ZSK and KSK rollovers, including submission checks 2021-09-29 23:17:38 +02:00
s3lph
9402eee1c1 Prevent zone file from being overwritten on DNSSEC resigns 2021-09-29 22:45:26 +02:00
s3lph
b7f304a70e Fix collection version number 2021-07-19 02:52:07 +02:00
23 changed files with 642 additions and 114 deletions

8
.ansible-lint Normal file
View file

@ -0,0 +1,8 @@
---
skip_list:
- meta-runtime[unsupported-version]
- galaxy[no-changelog]
- galaxy[version-incorrect]
- name[casing]
- var-naming[no-role-prefix]

View file

@ -0,0 +1,29 @@
---
name: Ansible Galaxy
on: # noqa yaml[truthy]
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: docker
steps:
- uses: actions/checkout@v4
- name: Set version in galaxy.yml
run: |
VERSION=${GITHUB_REF#refs/tags/v}
sed -re "s/^version:.*$/version: ${VERSION}/" -i galaxy.yml
- name: Upload collection to Ansible Galaxy
env:
GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }}
run: |
apt update; apt install --yes python3-pip
pip3 install --break-system-packages ansible
ansible-galaxy collection build
ansible-galaxy collection publish --api-key=${GALAXY_API_KEY} s3lph-nameserver*tar.gz

View file

@ -0,0 +1,17 @@
---
name: Ansible Lint
on: [push, pull_request] # noqa yaml[truthy]
jobs:
build:
runs-on: docker
steps:
- uses: actions/checkout@v4
- run: |
apt update; apt install --yes python3-pip
pip3 install --break-system-packages ansible-lint
ansible-lint

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
s3lph-nameserver*.tar.gz

View file

@ -1,7 +1,7 @@
--- ---
# Replace example.org with your zone name # Replace example_org/example.org with your zone name
knot_zone_example.org: knot_zone_example_org:
masters: masters:
- ns1.example.org - ns1.example.org
@ -10,9 +10,13 @@ knot_zone_example.org:
- ns3.example.org - ns3.example.org
updaters: updaters:
- foo.example.org - foo.example.org
parents:
- a.gtld-servers.net
# Replace example.org. with your zone name # Replace example.org. with your zone name
name: example.org. name: example.org.
# Enable automatic KSK rollover once a year
ksk_lifetime: 365d
# Configure the SOA record to your liking # Configure the SOA record to your liking
soa: soa:

View file

@ -0,0 +1,5 @@
---
knot_dns_addresses:
- "2001:503:a83e::2:30"
- "192.5.6.30"

View file

@ -19,6 +19,3 @@ knot_tsig_key:
secret: pZxgYlANxwWscfrZz4sdi6mQUlWFWlhUO/y7wjSJ6qdcXXGTaAxtwlaHWYYhJfTN secret: pZxgYlANxwWscfrZz4sdi6mQUlWFWlhUO/y7wjSJ6qdcXXGTaAxtwlaHWYYhJfTN
# Change other host specific options here # Change other host specific options here
# knot 2.7 in Debian stable doesn't know double-ds yet
knot_dnssec_policy_cds_publish: always

View file

@ -18,6 +18,3 @@ knot_tsig_key:
algorithm: hmac-sha384 algorithm: hmac-sha384
secret: poAeCzXByHLuuHjDfLceKmlUWFD+08p8QfV0ikXMBn0qTSJEXnBaDUupaG8aRS8M secret: poAeCzXByHLuuHjDfLceKmlUWFD+08p8QfV0ikXMBn0qTSJEXnBaDUupaG8aRS8M
# Change other host specific options here # Change other host specific options here
# knot 2.7 in Debian stable doesn't know double-ds yet
knot_dnssec_policy_cds_publish: always

View file

@ -11,3 +11,6 @@ ns2.example.org
ns3.example.org ns3.example.org
# TSIG update clients, also dummy host only # TSIG update clients, also dummy host only
foo.example.org foo.example.org
# Parents nameservers to check for publication of DS records upon KSK
# rollover
a.gtld-servers.net

View file

@ -1,5 +1,6 @@
--- ---
- hosts: nameserver - name: Install and configure Knot
hosts: nameserver
roles: roles:
- s3lph.nameserver.knot - s3lph.nameserver.knot

View file

@ -1,5 +1,3 @@
--- ---
collections: collections:
- name: https://gitlab.com/s3lph/ansible-collection-nameserver - name: s3lph.nameserver
type: git
version: master

View file

@ -1,4 +1,3 @@
### REQUIRED
# The namespace of the collection. This can be a company/brand/organization or product namespace under which all # The namespace of the collection. This can be a company/brand/organization or product namespace under which all
# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
# underscores or numbers and cannot contain consecutive underscores # underscores or numbers and cannot contain consecutive underscores
@ -8,7 +7,7 @@ namespace: s3lph
name: nameserver name: nameserver
# The version of the collection. Must be compatible with semantic versioning # The version of the collection. Must be compatible with semantic versioning
version: 0.2 version: "0.4.4"
# The path to the Markdown (.md) readme file. This path is relative to the root of the collection # The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md readme: README.md
@ -16,8 +15,7 @@ readme: README.md
# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url) # A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
# @nicks:irc/im.site#channel' # @nicks:irc/im.site#channel'
authors: authors:
- s3lph <account-gitlab-ideynizv@kernelpanic.lol> - s3lph <s3lph@kabelsalat.ch>
### OPTIONAL but strongly recommended ### OPTIONAL but strongly recommended
# A short summary description of the collection # A short summary description of the collection
@ -26,15 +24,12 @@ description: Authoritative nameserver setup using knot
# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
license: license:
- MIT - MIT
# The path to the license file for the collection. This path is relative to the root of the collection. This key is
# mutually exclusive with 'license'
license_file: ''
# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
# requirements as 'namespace' and 'name' # requirements as 'namespace' and 'name'
tags: tags:
- application
- dns - dns
- knot - knot
- nameserver - nameserver
@ -47,20 +42,19 @@ tags:
dependencies: {} dependencies: {}
# The URL of the originating SCM repository # The URL of the originating SCM repository
repository: https://gitlab.com/s3lph/ansible-collection-nameserver repository: https://git.kabelsalat.ch/s3lph/ansible-collection-nameserver
# The URL to any online docs # The URL to any online docs
documentation: https://gitlab.com/s3lph/ansible-collection-nameserver documentation: https://git.kabelsalat.ch/s3lph/ansible-collection-nameserver
# The URL to the homepage of the collection/project # The URL to the homepage of the collection/project
homepage: https://gitlab.com/s3lph/ansible-collection-nameserver homepage: https://git.kabelsalat.ch/s3lph/ansible-collection-nameserver
# The URL to the collection issue tracker # The URL to the collection issue tracker
issues: https://gitlab.com/s3lph/ansible-collection-nameserver/-/issues issues: https://git.kabelsalat.ch/s3lph/ansible-collection-nameserver/issues
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build # A list of file glob-like patterns used to filter any files or directories that should not be included in the build
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This # artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
# and '.git' are always filtered # and '.git' are always filtered
build_ignore: [] build_ignore: []

52
meta/runtime.yml Normal file
View file

@ -0,0 +1,52 @@
---
# Collections must specify a minimum required ansible version to upload
# to galaxy
requires_ansible: '>=2.15'
# Content that Ansible needs to load from another location or that has
# been deprecated/removed
# plugin_routing:
# action:
# redirected_plugin_name:
# redirect: ns.col.new_location
# deprecated_plugin_name:
# deprecation:
# removal_version: "4.0.0"
# warning_text: |
# See the porting guide on how to update your playbook to
# use ns.col.another_plugin instead.
# removed_plugin_name:
# tombstone:
# removal_version: "2.0.0"
# warning_text: |
# See the porting guide on how to update your playbook to
# use ns.col.another_plugin instead.
# become:
# cache:
# callback:
# cliconf:
# connection:
# doc_fragments:
# filter:
# httpapi:
# inventory:
# lookup:
# module_utils:
# modules:
# netconf:
# shell:
# strategy:
# terminal:
# test:
# vars:
# Python import statements that Ansible needs to load from another location
# import_redirection:
# ansible_collections.ns.col.plugins.module_utils.old_location:
# redirect: ansible_collections.ns.col.plugins.module_utils.new_location
# Groups of actions/modules that take a common set of options
# action_groups:
# group_name:
# - module1
# - module2

5
roles/knot/README.md Normal file
View file

@ -0,0 +1,5 @@
# Role s3lph.nameserver.knot
Documentation in `meta/argument_specs.yml`.
A usage example can be found in the `docs` folder of the collection.

View file

@ -1,11 +1,15 @@
--- ---
knot_repository_install: false
knot_repository_url: https://deb.knot-dns.cz/knot/
knot_repository_distribution: "{{ ansible_facts.distribution_release }}"
knot_server_rundir: /run/knot knot_server_rundir: /run/knot
knot_server_user: knot knot_server_user: knot
knot_server_group: knot knot_server_group: knot
knot_server_identity: "{{ ansible_hostname }}" knot_server_identity: "{{ ansible_facts.hostname }}"
knot_server_nsid: "{{ ansible_hostname }}" knot_server_nsid: "{{ ansible_facts.hostname }}"
knot_server_version: "{{ ansible_hostname }}" knot_server_version: "{{ ansible_facts.hostname }}"
knot_server_listen: knot_server_listen:
- "::@53" - "::@53"
- "0.0.0.0@53" - "0.0.0.0@53"
@ -19,9 +23,18 @@ knot_zone_replica_storage_path: /var/lib/knot/replica
knot_zone_semantic_checks: 'on' knot_zone_semantic_checks: 'on'
knot_zone_dnssec_signing: 'on' knot_zone_dnssec_signing: 'on'
knot_dnssec_policy_algorithm: ecdsap384sha384 knot_dnssec_policy_algorithm: ed25519
knot_dnssec_policy_nsec3: 'on' knot_dnssec_policy_nsec3: 'on'
knot_dnssec_policy_ksk_shared: 'on' # Use of a NSEC3 salt is discouraged by RFC 9276, section 3.1
knot_dnssec_policy_ksk_size: 384 knot_dnssec_policy_nsec3_salt_length: 0
knot_dnssec_policy_zsk_size: 384 knot_dnssec_policy_ksk_shared: 'off'
knot_dnssec_policy_cds_publish: 'double-ds' knot_dnssec_policy_ksk_size: 256
knot_dnssec_policy_zsk_size: 256
knot_dnssec_policy_zsk_lifetime: 30d
knot_dnssec_policy_ksk_lifetime: 0
# double-ds breaks algorithm rollovers: https://gitlab.nic.cz/knot/knot-dns/-/issues/804
knot_dnssec_policy_cds_publish: 'always'
knot_dnssec_policy_propagation_delay: 1h
knot_dnssec_submission_check_interval: 1h
knot_dnssec_submission_timeout: 0

View file

@ -1,6 +1,11 @@
--- ---
- name: reload knot - name: reload knot
service: ansible.builtin.service:
name: knot name: knot
state: reloaded state: reloaded
- name: restart knot
ansible.builtin.service:
name: knot
state: restarted

View file

@ -0,0 +1,365 @@
---
argument_specs:
main:
version_added: "0.0.1"
short_description: Install and configure Knot.
description:
- Install and configure the L(Knot,https://knot.readthedocs.io/en/latest/index.html) authoritative namesever.
- Zones are configured through YAML hostvars, rather than simply deploying zone files.
- "Execution of this role can be limited using the following tags:"
- "C(role::knot:install): Install knot from distribution packages or upstream repositories."
- "C(role::knot:config): Render the Knot configuration file."
- "C(role::knot:zones): Render the zone files for which Knot is authoritative."
- "C(role::knot): Apply all of the above."
author: s3lph
options:
knot_repository_install:
description:
- If true, install knot from the L(upstream repositories,https://deb.knot-dns.cz/knot/).
- If false, install knot from the default system repositories.
type: bool
default: false
knot_repository_url:
description: URL of the upstream repository.
type: str
default: https://deb.knot-dns.cz/knot/
knot_repository_distribution:
description:
- Suite name (distribution codename) of the upstream repository.
- Defaults to the value of C(ansible_facts.distribution_release).
type: str
knot_server_rundir:
description: Runtime directory where Knot should e.g. write its control socket to.
type: str
default: /run/knot
knot_server_user:
description: The user to run knot as.
type: str
default: knot
knot_server_group:
description: The group to run knot as.
type: str
default: knot
knot_server_identity:
description:
- Response to a C(id.server. CH TXT) or C(hostname.bind. CH TXT) query.
- Set to an empty value to disable.
- Defaults to C(ansible_facts.hostname).
type: str
knot_server_nsid:
description:
- The RFC 5001 NSID to include in responses when requested by the resolver.
- Set to an empty value to disable.
- Defaults to C(ansible_facts.hostname).
type: str
knot_server_version:
description:
- Response to a C(version.server. CH TXT) or C(version.bind. CH TXT) query.
- Set to an empty value to disable.
- Defaults to C(ansible_facts.hostname).
type: str
knot_server_listen:
description:
- The list of interfaces to listen on.
- Host and port are separated with an C(@) sign.
type: list
elements: str
default: ["::@53", "0.0.0.0@53"]
knot_dns_addresses:
description:
- Addresses under which the nameserver is reachable externally.
- Used for zone replication and notification and for KSK submission checks.
- Required to be set for all members of a replicated setup.
type: list
elements: str
default: []
knot_log_targets:
description:
- Logging configuration of Knot.
- Every entry is a dict consisting of at least a C(target) key.
- >-
Details on logging configuration can be found in the
U(upstream documentation,https://knot.readthedocs.io/en/latest/reference.html#log-section)
type: list
elements: dict
default:
- target: syslog
level: info
knot_zone_master_storage_path:
description: Location from where Knot should read zonefiles for which it is the primary server.
type: str
default: /var/lib/knot/master
knot_zone_replica_storage_path:
description: Location where Knot should store replicated zonefiles.
type: str
default: /var/lib/knot/replica
knot_zone_semantic_checks:
description: If set to C(on), Knot will perform additional zonefile checks.
type: str
default: 'on'
knot_zone_dnssec_signing:
description:
- If set to C(on), Knot will automatically configure DNSSEC zone signing.
- If set to C(off), Knot will not sign zones automatically.
type: str
default: 'on'
knot_dnssec_policy_algorithm:
description: The DNSSEC signing algorithm to use.
type: str
default: ed25519
knot_dnssec_policy_nsec3:
description:
- If set to C(on), C(NSEC3) is used instead of C(NSEC).
- If set to C(off), C(NSEC) is used, which allows full zone enumeration.
type: str
default: 'on'
knot_dnssec_policy_nsec3_salt_length:
description:
- Length of the NSEC3 salt field.
- >-
Use of a NSEC3 salt is discouraged by
U(RFC 9276,https://datatracker.ietf.org/doc/html/rfc9276#section-3.1).
type: int
default: 0
knot_dnssec_policy_ksk_size:
description:
- Size (in bits) of the KSK.
- >-
Permitted values in combination with O(knot_dnssec_policy_algorithm) are documented in the
U(upstream documentation,https://knot.readthedocs.io/en/latest/reference.html#ksk-size).
type: int
default: 256
knot_dnssec_policy_zsk_size:
description:
- Size (in bits) of the KSK.
- >-
Permitted values in combination with O(knot_dnssec_policy_algorithm) are documented in the
U(upstream documentation,https://knot.readthedocs.io/en/latest/reference.html#ksk-size).
type: int
default: 256
knot_dnssec_policy_zsk_lifetime:
description: Time after which the ZSK should be rotated automatically.
type: str
default: 30d
knot_dnssec_policy_ksk_lifetime:
description: Time after which the KSK should be rotated automatically.
type: str
default: "0"
knot_dnssec_policy_cds_publish:
description:
- If and when to publish C(CDS) and C(CDNSKEY) records.
- >-
Supported values and their meaning are documented in the
U(upstream documentation,https://knot.readthedocs.io/en/latest/reference.html#cds-cdnskey-publish)
- Do not use the C(double-ds) policy when performing automated KSK rollovers. It will break the chain of trust.
type: str
default: 'always'
knot_dnssec_policy_propagation_delay:
description: Additional time to wait before continuing with each step of a key rollover.
type: str
default: 1h
knot_dnssec_submission_check_interval:
description: How often during a KSK rollover Knot should check submission nameservers for the new DS RRSet.
type: str
default: 1h
knot_dnssec_submission_timeout:
description:
- >-
Time after which a KSK submission to the parent nameserver should automatically be considered successful,
even if the new DS RRSet has not been found on the submussion nameserver.
type: str
default: "0"
knot_tsig_key:
description:
- The TSIG key used by this host for zone transfers or updates.
- >-
This shared key will be configured automatically for all zones involving this host in
O(replicas) or O(updaters).
type: dict
default: null
options:
name:
description:
- The name of the key.
- Should be a FQDN including the trailing C(.), e.g. C(tsig.hostname.example.org.).
type: str
required: true
algorithm:
description: The key algorithm, e.g. C(hmac-sha384).
type: str
required: true
secret:
description:
- The shared secret of this key.
- Generate a new key with e.g. C(keymgr -t tsig.foo.example.org. hmac-sha384).
- This is a secret. Protect it e.g. with C(ansible-vault)!
type: str
required: true
knot_zone_*:
description:
- Zone configurations, one top-level dict per zone.
- >-
Recomendation: You can use an arbitrary string after C(knot_zone_), but we recommend to use the zone name
with C(.) replaced by (_).
- "Recommendation: Keep one file per zone in C(group_vars/nameservers/zones/zone_<zonename>.yml)."
type: dict
required: false
options:
name:
description: Fully qualified name of the zone, including the trailing C(.).
type: str
required: true
soa:
description: Contains the values required to synthesize the C(SOA) record of the zone apex.
type: dict
required: true
options:
class:
description: Class for the records in this zone, usually C(IN).
type: str
required: true
primary:
description:
- FQDN of the autoritative nameserver of the zone.
- Also known as C(MNAME).
type: str
required: true
rname:
description:
- Email address of the administrator.
- The C(@) must be replaced with a C(.).
- C(.) in the localpart must be escaled as C(\.).
type: str
required: true
refresh:
description:
- How often (in seconds) replicas should query the primary for SOA changes.
type: int
required: true
retry:
description:
- How long (in seconds) replicas should wait to retry a failed zone transfer.
type: int
required: true
expire:
description:
- How long after the last update (in seconds) replicas should stop serving a zone.
type: int
required: true
ttl:
description:
- Default TTL (in seconds) for all entries in the zone, and TTL of the SOA record.
type: int
required: true
min_ttl:
description:
- TTL (in seconds) of negative responses.
type: int
required: true
records:
description:
- All the records in this zone go here.
type: list
elements: dict
options:
name:
description:
- Name of the record.
- Records without a trailing C(.) are relative to the zone apex.
- Use C(@) to refer to the zone apex itself.
type: str
required: true
ttl:
description:
- TTL of this record.
- If omitted, defaults to the zone TTL set in the O(soa) section.
type: int
class:
description:
- Class of this record.
- If omitted, defaults to the class set in the O(soa) section.
type: str
type:
description: Type of the record, e.g. C(AAAA), C(A) or C(CNAME).
type: str
required: true
value:
description:
- Value of the record.
- >-
Length restrictions for TXT records apply. Subdivide them with single-quoted double quotes
e.g. C('"first part of the txt record " "second part of the txt record"')
type: str
required: true
masters:
description:
- Hostnames of servers which should act as a primary for this zone.
- Zonefile will be deployed to hosts whose C(inventory_hostname) is contained in this list.
type: list
default: []
replicas:
description:
- Hostnames of servers which should at as a replica for this zone.
- >-
Hosts whose C(inventory_hostname) is contained in this list are automatically configured
to replicate this zone.
updaters:
description:
- Hostnames of servers which should be permitted to submit TSIG zone updates for this zone.
- >-
The O(knot_tsig_key)s of hosts whose C(inventory_hostname) is contained in this list are configured as
permitted TSIG updaters for this zone.
- The inventory entry for this host can be a dummy. Its hostvars are only used to fetch the key.
parents:
description:
- Hostnames of servers which should be checked for KSK submissions.
- >-
The O(knot_dns_addresses) of hosts whose C(inventory_hostname) is contained in this list are configured
as KSK submission servers, which are regularly checked to verify the upstream DS RRSet.
- The inventory entry for this host can be a dummy. Its hostvars are only used to hold its IPs.
algorithm:
description: Zone-specific override for O(knot_dnssec_policy_algorithm).
type: str
ksk_size:
description: Zone-specific override for O(knot_dnssec_policy_ksk_size).
type: str
zsk_size:
description: Zone-specific override for O(knot_dnssec_policy_zsk_size).
type: str
ksk_lifetime:
description: Zone-specific override for O(knot_dnssec_policy_ksk_lifetime).
type: str
zsk_lifetime:
description: Zone-specific override for O(knot_dnssec_policy_zsk_lifetime).
type: str
propagation_delay:
description: Zone-specific override for O(knot_dnssec_policy_propagation_delay).
type: str
cds_cdnskey_publish:
description: Zone-specific override for O(knot_dnssec_policy_cds_publish).
type: str
sign_on_secondary:
description:
- Whether Knot should sign this zone even if it is not the primary nameserver.
- Useful if Knot is used with a hidden primary that does not support DNSSEC.
type: bool
default: false
replicate:
description:
- >-
Note: This option is used for more complex replication hierarchies. Chances are you want to use
O(replicas) instead.
- Configure further replication to other nameservers even if the server is already a replica itself.
- This works in addition to the replication configured through O(masters)/O(replicas).
- Takes a dict where each upstream is mapped to a list of downstreams.
type: dict
default: {}

View file

@ -1,12 +1,60 @@
--- ---
- name: render knot master config - name: Render knot master config
template: ansible.builtin.template:
src: etc/knot/knot.conf.j2 src: etc/knot/knot.conf.j2
dest: /etc/knot/knot.conf dest: /etc/knot/knot.conf
owner: knot owner: knot
group: knot group: knot
mode: 0640 mode: "0640"
vars: vars:
zones: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | map(attribute='value') | list }}" zones: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | map(attribute='value') | list }}"
notify: restart knot
- name: Create knot zone directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: knot
group: knot
mode: "0750"
loop:
- "{{ knot_zone_master_storage_path }}"
- "{{ knot_zone_replica_storage_path }}"
- name: Make sure all zones have a name
ansible.builtin.assert:
that:
- "'name' in item.value"
- "item.value.name | type_debug == 'str'"
fail_msg: "{{ item.key }} does not have a name"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | list }}"
- name: Make sure all zones have at least one master defined
ansible.builtin.assert:
that:
- "'masters' in item.value"
- "item.value.masters | type_debug == 'list'"
- "item.value.masters | length > 0"
fail_msg: "{{ item.key }} does not have a zone master"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | list }}"
- name: Render knot zone files
ansible.builtin.template:
src: var/lib/knot/master/zone.j2
dest: "{{ knot_zone_master_storage_path }}/{{ item.name }}zone"
owner: knot
group: knot
mode: "0640"
validate: "/usr/bin/kzonecheck -o {{ item.name }} -v %s"
vars:
zone: "{{ item }}"
when: "inventory_hostname in item['masters']"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | map(attribute='value') | list }}"
notify: reload knot notify: reload knot
- name: Start and enable knot
ansible.builtin.service:
name: knot
state: started
enabled: true

View file

@ -1,15 +1,18 @@
--- ---
- name: install dependencies - name: Install knot repository
package: ansible.builtin.deb822_repository:
name: knot name: knot
state: present types: deb
loop: uris: "{{ knot_repository_url }}"
- knot suites: "{{ knot_repository_distribution }}"
- knot-dnsutils components: main
signed_by: https://deb.knot-dns.cz/apt.gpg
when: knot_repository_install
- name: start and enable knot - name: Install dependencies
service: ansible.builtin.package:
name: knot name:
state: started - knot
enabled: yes - knot-dnsutils
- knot-dnssecutils

View file

@ -1,19 +1,14 @@
--- ---
- name: install knot - name: Install knot
import_tasks: install.yml ansible.builtin.import_tasks: install.yml
tags: tags:
- "role::knot" - "role::knot"
- "role::knot:install" - "role::knot:install"
- name: render zonefiles - name: Configure knot
import_tasks: zones.yml ansible.builtin.import_tasks: config.yml
tags:
- "role::knot"
- "role::knot:zones"
- name: configure knot
import_tasks: config.yml
tags: tags:
- "role::knot" - "role::knot"
- "role::knot:config" - "role::knot:config"
- "role::knot:zones" # Backwards compat; this used to be a separate file

View file

@ -1,43 +0,0 @@
---
- name: create knot zone directories
file:
path: "{{ item }}"
state: directory
owner: knot
group: knot
mode: 0750
loop:
- "{{ knot_zone_master_storage_path }}"
- "{{ knot_zone_replica_storage_path }}"
- name: make sure all zones have a name
assert:
that:
- "'name' in item.value"
- "item.value.name | type_debug == 'str'"
fail_msg: "{{ item.key }} does not have a name"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | list }}"
- name: make sure all zones have at least one master defined
assert:
that:
- "'masters' in item.value"
- "item.value.masters | type_debug == 'list'"
- "item.value.masters | length > 0"
fail_msg: "{{ item.key }} does not have a zone master"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | list }}"
- name: render knot zone files
template:
src: var/lib/knot/master/zone.j2
dest: "{{ knot_zone_master_storage_path }}/{{ item.name }}zone"
owner: knot
group: knot
mode: 0640
validate: /usr/bin/kzonecheck -v %s
vars:
zone: "{{ item }}"
when: "inventory_hostname in item['masters']"
loop: "{{ hostvars[inventory_hostname] | dict2items | selectattr('key', 'match', '^knot_zone_.+$') | map(attribute='value') | list }}"
notify: reload knot

View file

@ -35,7 +35,7 @@ key:
remote: remote:
{% for remote in ( (zones | map(attribute='replicas') ) + (zones | map(attribute='masters') ) ) | flatten | unique %} {% for remote in ( (zones | map(attribute='replicas') ) + (zones | map(attribute='masters') ) + (zones | map(attribute='parents') | select('defined') ) ) | flatten | unique %}
- id: remote-{{ remote }} - id: remote-{{ remote }}
{% if knot_tsig_key is defined and 'knot_tsig_key' in hostvars[remote] %} {% if knot_tsig_key is defined and 'knot_tsig_key' in hostvars[remote] %}
@ -48,7 +48,7 @@ remote:
{% endfor %} {% endfor %}
acl: acl:
{% for remote in zones | map(attribute='replicas') | flatten | unique %} {% for remote in ( zones | map(attribute='replicas') ) | flatten | unique %}
- id: acl-xfr-{{ remote }} - id: acl-xfr-{{ remote }}
action: transfer action: transfer
@ -56,7 +56,7 @@ acl:
key: {{ hostvars[remote].knot_tsig_key.name }} key: {{ hostvars[remote].knot_tsig_key.name }}
{% endif %} {% endif %}
{% for address in hostvars[remote].knot_dns_addresses %} {% for address in hostvars[remote].knot_dns_addresses %}
address: "{{ address }}" address: "{{ address.split('@')[0] }}"
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
@ -68,7 +68,7 @@ acl:
key: {{ hostvars[remote].knot_tsig_key.name }} key: {{ hostvars[remote].knot_tsig_key.name }}
{% endif %} {% endif %}
{% for address in hostvars[remote].knot_dns_addresses %} {% for address in hostvars[remote].knot_dns_addresses %}
address: "{{ address }}" address: "{{ address.split('@')[0] }}"
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
@ -84,17 +84,36 @@ acl:
# MASTER ZONES # MASTER ZONES
# #
submission:
{% for zone in zones %}
{% if inventory_hostname in zone.masters or (inventory_hostname in zone.replicas and zone.sign_on_secondary | default(false)) %}
- id: submission-{{ zone.name }}
check-interval: {{ knot_dnssec_submission_check_interval }}
timeout: {{ knot_dnssec_submission_timeout }}
{% if zone.parents is defined and zone.parents | length > 0 %}
parent:{% for parent in zone.parents %} remote-{{ parent }}{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
policy: policy:
{% for zone in zones %} {% for zone in zones %}
{% if inventory_hostname in zone.masters %} {% if inventory_hostname in zone.masters or (inventory_hostname in zone.replicas and zone.sign_on_secondary | default(false)) %}
- id: dnssec-{{ zone.name }} - id: dnssec-{{ zone.name }}
algorithm: {{ knot_dnssec_policy_algorithm }} algorithm: {{ zone.algorithm | default(knot_dnssec_policy_algorithm) }}
nsec3: {{ knot_dnssec_policy_nsec3 }} nsec3: {{ zone.nsec3 | default(knot_dnssec_policy_nsec3) }}
ksk-size: {{ knot_dnssec_policy_ksk_size }} nsec3-salt-length: {{ zone.nsec3_salt_length | default(knot_dnssec_policy_nsec3_salt_length) }}
zsk-size: {{ knot_dnssec_policy_zsk_size }} ksk-size: {{ zone.ksk_size | default(knot_dnssec_policy_ksk_size) }}
zsk-size: {{ zone.zsk_size | default(knot_dnssec_policy_zsk_size) }}
zsk-lifetime: {{ zone.zsk_lifetime | default(knot_dnssec_policy_zsk_lifetime) }}
ksk-lifetime: {{ zone.ksk_lifetime | default(knot_dnssec_policy_ksk_lifetime) }}
ksk-submission: submission-{{ zone.name }}
ksk-shared: {{ knot_dnssec_policy_ksk_shared }} ksk-shared: {{ knot_dnssec_policy_ksk_shared }}
cds-cdnskey-publish: {{ knot_dnssec_policy_cds_publish }} propagation-delay: {{ zone.propagation_delay | default(knot_dnssec_policy_propagation_delay) }}
cds-cdnskey-publish: {{ zone.cds_cdnskey_publish | default(knot_dnssec_policy_cds_publish) }}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@ -106,7 +125,9 @@ zone:
storage: {{ knot_zone_master_storage_path }} storage: {{ knot_zone_master_storage_path }}
semantic-checks: {{ knot_zone_semantic_checks }} semantic-checks: {{ knot_zone_semantic_checks }}
serial-policy: unixtime serial-policy: unixtime
zonefile-load: difference zonefile-load: difference-no-serial
zonefile-sync: -1
journal-content: all
dnssec-signing: {{ knot_zone_dnssec_signing }} dnssec-signing: {{ knot_zone_dnssec_signing }}
dnssec-policy: dnssec-{{ zone.name }} dnssec-policy: dnssec-{{ zone.name }}
{% for replica in zone.replicas %} {% for replica in zone.replicas %}
@ -138,9 +159,19 @@ zone:
{% for master in zone.masters %} {% for master in zone.masters %}
acl: acl-notify-{{ master }} acl: acl-notify-{{ master }}
{% endfor %} {% endfor %}
{% for xfer in (zone.replicate | default({})).get(inventory_hostname, []) %}
acl: acl-xfr-{{ xfer }}
{% endfor %}
{% for master in zone.masters %} {% for master in zone.masters %}
master: remote-{{ master }} master: remote-{{ master }}
{% endfor %} {% endfor %}
{% for notify in (zone.replicate | default({})).get(inventory_hostname, []) %}
notify: remote-{{ notify }}
{% endfor %}
{% if zone.sign_on_secondary | default(false) %}
dnssec-signing: {{ knot_zone_dnssec_signing }}
dnssec-policy: dnssec-{{ zone.name }}
{% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View file

@ -3,7 +3,7 @@
$TTL {{ zone.soa.ttl }} $TTL {{ zone.soa.ttl }}
@ {{ zone.soa.class }} SOA {{ zone.soa.primary }}. {{ zone.soa.rname }}. ( @ {{ zone.soa.class }} SOA {{ zone.soa.primary }}. {{ zone.soa.rname }}. (
{{ ansible_date_time.epoch }} ; serial 0 ; serial (ignored by knot, uses unixtime instead)
{{ zone.soa.refresh }} ; refresh {{ zone.soa.refresh }} ; refresh
{{ zone.soa.retry }} ; retry {{ zone.soa.retry }} ; retry
{{ zone.soa.expire }} ; expire {{ zone.soa.expire }} ; expire