An Ansible role create by the folks behind PowerDNS to set up dnsdist.
An ansible-core 2.16 or newer installation. Enterprise Linux 8 targets must be managed with ansible-core 2.16: their system Python is 3.6, which the modules of ansible-core 2.20 cannot run.
None.
Available variables are listed below, along with default values (see defaults/main.yml):
dnsdist_install_repo: ""By default, dnsdist is installed from the software repositories configured on the target hosts.
# Install dnsdist from the master branch
- hosts: dnsdist
roles:
- { role: PowerDNS.dnsdist,
dnsdist_install_repo: "{{ dnsdist_powerdns_repo_master }}"
# Install dnsdist 1.3.x
- hosts: dnsdist
roles:
- { role: PowerDNS.dnsdist,
dnsdist_install_repo: "{{ dnsdist_powerdns_repo_13 }}"The examples above, show how to install DNSdist from the official PowerDNS repositories
(see the complete list of pre-defined repos in vars/main.yml).
- hosts: all
vars:
dnsdist_install_repo:
name: "dnsdist" # the repository name
apt_repo_origin: "example.com" # used to pin dnsdist to the provided repository
apt_version: "dnsdist-19" # deb822 suites suffix (appended to release codename)
gpg_key_url: "http://example.com/MYREPOGPGPUBKEY.asc" # repository public GPG key
yum_repo_baseurl: "http://example.com/centos/$basearch/$releasever/dnsdist"
yum_debug_symbols_repo_baseurl: "http://example.com/centos/$basearch/$releasever/dnsdist/debug"
roles:
- { role: PowerDNS.dnsdist }It is also possible to install dnsdist from custom repositories as demonstrated in the example above.
dnsdist_install_epel: trueBy default, install EPEL to satisfy some DNSdist dependencies like lidsodium.
To skip the installation of EPEL set the dnsdist_install_epel variable to false.
dnsdist_package_name: "{{ default_dnsdist_package_name }}"The name of the dnsdist package: "dnsdist" on both RHEL and Debian derivates distributions.
dnsdist_package_version: ""Optionally, allow to set a specific version of the dnsdist package to be installed.
dnsdist_package_state: "present"The desired state of the dnsdist packages. Use "present" (default) to install, "latest" to upgrade, or "absent" to uninstall.
dnsdist_install_debug_symbols_package: falseInstall dnsdist debug symbols package.
dnsdist_debug_symbols_package_name: "{{ default_dnsdist_debug_symbols_package_name }}"The name of the dnsdist debug symbols package to be installed when dnsdist_install_debug_symbols_package is true.
dnsdist_additional_packages: []List of additional packages to install, list support version pining for each of the packages.
dnsdist_acls: []Configures the dnsdist ACLS (netmasks).
dnsdist_locals: ['127.0.0.1:5300']Configure dnsdist's listen addresses.
dnsdist_servers:
- '127.0.0.1'
- "{ address='127.0.0.1:5300', source='127.0.0.1@lo', order=1 }"The list of IP addresses of the downstream DNS servers dnsdist should be send traffic to OR of Lua tables that the newServer function ( https://dnsdist.org/reference/config.html#newServer ) can parse.
dnsdist_carbonserver: ""The IP address of the Carbon server that should receive dnsdist metrics.
dnsdist_setkey: ""Encryption key for the dnsdist's TCP control socket. If it is empty, a random key will be generated. If a key is already present in the configuration file, it will be kept.
dnsdist_generatekey: "{{ (dnsdist_setkey | length == 0) | bool }}"Whether to auto-generate a control socket encryption key. Defaults to true when dnsdist_setkey is empty.
dnsdist_controlsocket: "127.0.0.1"The listen IP address of the dnsdist's TCP control socket.
dnsdist_webserver_address: ""The listen IP address of the built-in webserver, empty thus disable by default.
dnsdist_webserver_password: ""The authentication credentials for the built-in webserver. Must be set when dnsdist_webserver_address is set.
dnsdist_webserver_apikey: ""The authentication credentials for the built-in API.
dnsdist_webserver_acl: ""Since 1.5.0, only connections from 127.0.0.1 and ::1 are allowed by default. See https://dnsdist.org/guides/webserver.html for more information.
dnsdist_config: ""Additional dnsdist configuration to be injected verbatim in the configuration file. Lua format only.
dnsdist_config_format: "lua"Format of the generated configuration file, lua or yaml. On dnsdist 2.0 the
yaml format also needs a unit override, because only 2.1 and newer look for the
file. See Configuration format.
dnsdist_config_yaml: {}The configuration in YAML format, used when dnsdist_config_format is yaml.
The dict is passed to dnsdist as given, so every setting is reachable without the
role knowing about it. See https://dnsdist.org/reference/yaml-settings.html and
the dnsdist.yml-dist file the packages ship. See
Configuration format.
dnsdist_config_yaml_string_keys:
- api_key
- key
- name
- password
- pool
- pools
- secretSettings whose value must stay a string even when it looks like a number or a boolean. See Configuration format.
dnsdist_config_remove_stale_yaml: trueWhether a lua run removes the YAML configuration file that an earlier yaml
run of this role wrote. On dnsdist 2.1 and newer a daemon started without an
explicit --config prefers the .yml, so without this a switch back to Lua would
leave the old YAML file in charge and the Lua configuration ignored. On 2.0 the
.conf is read either way, so the removal changes nothing there - it still runs,
because a leftover that starts being read the day the host is upgraded to 2.1 is
worse than removing a file the daemon currently ignores. Only the .yml is ever
removed, and only while the format is lua.
The path removed is the .yml sibling of dnsdist_config_location, never the
platform default, so an invocation that manages dnsdist-a.conf removes
dnsdist-a.yml and cannot touch the configuration of another instance.
dnsdist_config_files: {}Additional dnsdist configuration files to be placed in the configuration directory.
dnsdist_config_owner: ""
dnsdist_config_group: ""User and Group that own the configuration file. When empty, version-specific defaults are used.
dnsdist_service_overrides: {}Dict with overrides for the service (systemd only).
This can be used to change any systemd settings in the [Service] category.
dnsdist_unit_overrides: {}Dict with overrides for the service unit (systemd only).
This can be used to change any systemd settings in the [Unit] category.
dnsdist_environment_overrides: {}Dict with overrides for the service environments (systemd only).
This can be used to change any environment variables in systemd settings in the [Service] category.
The three dicts are written to override.conf, override-unit.conf and
override-environment.conf in /etc/systemd/system/<service name>.service.d/. Emptying one of
them removes its own file again and restarts the service; the other two files and any drop-in an
operator added next to them are left alone.
dnsdist_service_name: "dnsdist"Name of the managed service. Set it to dnsdist@<instance> to manage an instance of the templated
systemd unit shipped by the dnsdist packages, and set dnsdist_config_location to the matching
/etc/dnsdist/dnsdist-<instance>.conf. See Handlers.
dnsdist_config_location: >-
{{ (dnsdist_config_format == 'yaml')
| ternary(default_dnsdist_config_location_yaml, default_dnsdist_config_location) }}Location of the configuration file, /etc/dnsdist/dnsdist.conf on Linux and
/usr/local/etc/dnsdist.conf on FreeBSD; with dnsdist_config_format set to
yaml, the same path with a .yml extension. Setting it by hand is supported,
but the extension has to match the format or the role fails the run. The additional files
from dnsdist_config_files are written next to it.
The role writes a Lua configuration by default. Set dnsdist_config_format to
yaml to write the YAML configuration that dnsdist 2.0 and newer accept. dnsdist
2.1 and newer then read that file on their own; a 2.0 has to be pointed at it with
--config, see Which file the daemon reads.
- hosts: all
roles:
- role: PowerDNS.dnsdist
dnsdist_config_format: yaml
dnsdist_config_yaml:
binds:
- listen_address: "127.0.0.1:5300"
protocol: Do53
backends:
- address: "192.0.2.1:53"
protocol: Do53
acl:
- 127.0.0.0/8
console:
listen_address: "127.0.0.1:5900"
webserver:
listen_addresses:
- "127.0.0.1:8083"
password: "{{ vault_dnsdist_webserver_password }}"dnsdist_config_yaml is submitted as given. Nothing in it is interpreted,
reordered or renamed, so a setting the role has never heard of works as soon as
the installed dnsdist supports it. Values that arrive as strings - from an
inventory file, from --extra-vars, or from a vault-encrypted variable - are
converted to a real number or boolean, because the YAML parser of dnsdist is
strictly typed and rejects a quoted number where it wants a real one.
That conversion follows the shape of the value, since a template cannot know
the type a setting declares. So a value that must stay a string even though it
looks like a number - a password of 12345678, a backend named no - is
protected by the key list:
dnsdist_config_yaml_string_keys:
- api_key
- key
- name
- password
- pool
- pools
- secretAny setting whose key appears here is emitted as a string untouched, at any depth
and inside lists, so an entry under pools stays a string too. Add a key to the
list if one of your values is being rewritten.
The two formats are separate inputs and nothing is translated between them.
In yaml the configuration comes from dnsdist_config_yaml alone, and
dnsdist_locals, dnsdist_servers, dnsdist_acls, dnsdist_controlsocket,
dnsdist_webserver_address, dnsdist_webserver_password,
dnsdist_webserver_apikey, dnsdist_webserver_acl, dnsdist_carbonserver,
dnsdist_tlslocals and dnsdist_config are all ignored. Their YAML equivalents
are binds, backends, acl, console, webserver and carbon.
The console encryption key is the one exception, because it is generated on the
first converge and has to survive every later one. dnsdist_setkey and
dnsdist_generatekey therefore apply in both formats: in yaml the key is read
back from and written to console.key, and only when the configuration declares a
console section without a key of its own. With no console section there is
nowhere to put a key, so none is generated.
The format and the file name have to agree, and the role fails the run when they
do not: dnsdist picks its parser from the extension, so a YAML document in a
.conf file is read as Lua. The check exists because that mistake is otherwise
silent in the worst way - the configuration check passes, and the daemon fails at
start.
Because the parser follows the extension, yaml also moves the configuration
file to dnsdist.yml, and dnsdist_config_location follows unless it is set
explicitly. Whether the daemon then finds that file on its own depends on the
release.
Auto-detection of dnsdist.yml starts with dnsdist 2.1. Given both
dnsdist.conf and dnsdist.yml, a 2.1 started without an explicit --config
reads the .yml one. The packaged dnsdist.service passes no --config, so on
2.1 and newer switching a single instance over needs no unit override.
On dnsdist 2.0 it does. A 2.0 reads /etc/dnsdist/dnsdist.conf and never
looks at the .yml, so a 2.0 host left to the packaged unit keeps running the
packaged Lua configuration and ignores the YAML this role wrote - without an
error, because both files are valid and the role's configuration check passes
against the name it chooses. The daemon has to be pointed at the file:
- hosts: all
roles:
- role: PowerDNS.dnsdist
dnsdist_config_format: yaml
dnsdist_service_overrides:
ExecStartPre: /usr/bin/dnsdist --check-config --config /etc/dnsdist/dnsdist.yml
ExecStart: /usr/bin/dnsdist --supervised --disable-syslog --config /etc/dnsdist/dnsdist.yml
dnsdist_config_yaml:
binds:
- listen_address: "127.0.0.1:5300"
protocol: Do53Keep --supervised and --disable-syslog: the unit is Type=notify, and the
packaged ExecStart carries both for that reason.
The templated dnsdist@.service needs the same overrides on every release,
2.1 included: its ExecStart and ExecStartPre name
/etc/dnsdist/dnsdist-<instance>.conf outright, and an explicit path has no
.yml fallback, so an instance in YAML mode needs both overridden:
- hosts: all
roles:
- role: PowerDNS.dnsdist
dnsdist_service_name: dnsdist@a
dnsdist_config_format: yaml
dnsdist_config_location: /etc/dnsdist/dnsdist-a.yml
dnsdist_service_overrides:
ExecStartPre: /usr/bin/dnsdist --check-config --config /etc/dnsdist/dnsdist-a.yml
ExecStart: /usr/bin/dnsdist --supervised --disable-syslog --config /etc/dnsdist/dnsdist-a.yml
dnsdist_config_yaml:
binds:
- listen_address: "127.0.0.1:5300"
protocol: Do53The role clears the packaged ExecStart and ExecStartPre before setting its
own, so the drop-in replaces them rather than appending to them.
dnsdist_service_state: "started"
dnsdist_service_enabled: true
dnsdist_service_masked: falseAllow to specify the desired state of the DNSdist service. E.g. This allows to install and configure DNSdist without automatically starting the service. Masking is a systemd concept and is ignored on hosts without systemd.
dnsdist_disable_handlers: falseDisable automated service restart on configuration changes.
dnsdist_flush_handlers: falseRun the notified handlers at the end of the role instead of at the end of the play. See Handlers.
dnsdist_tlslocals: []Configures DNS over TLS listeners. The entries are copied verbatim entry-by-entry.
dnsdist_force_reinstall: falseForce reinstall of dnsdist packages by performing a removal prior to the package installation steps. Intended for usage where a downgrade of dnsdist needs to be performed.
Tags for --tags / --skip-tags:
repository: repo and GPG key setup, APT pinning, removal of stale versioned repo files.install: package installation and removal.config(aliasconfigure): config files, systemd overrides, control socket key.service: service state.always: OS variable import.
Repository tasks are also tagged install. The control socket key tasks are tagged install,
configure and config, so the key exists before the config is rendered.
Contributors: tags belong on the tasks inside install.yml, configure.yml and repo-*.yml, not
only on the include_tasks in tasks/main.yml. A dynamic include_tasks does not pass its tags
to included tasks, so a narrow --tags run would execute the include and skip its body, silently,
with rc=0.
Supported only on a host where this role already ran successfully.
Converged host: --check reports real drift only. Read-only probes such as the key lookup carry
check_mode: false so they still run and register results; they change nothing. Without that,
every run reports the config as changed with the existing setKey(...) removed.
Fresh host: --check is expected to fail. It installs neither the repository, python3-debian
nor the package, so deb822_repository fails and the config cannot be validated without the
dnsdist binary.
dnsdist_package_state:present,latest,absent, ...dnsdist_force_reinstall: remove before install, for downgrades.dnsdist_service_state(started,stopped,restarted,reloaded),dnsdist_service_enabled,dnsdist_service_masked(systemd hosts only).
dnsdist_package_state: absent removes the packages, but the config and service tasks still run,
so a full run fails on the service task (Could not find the requested service dnsdist). Remove
via the install path only:
ansible-playbook site.yml -e dnsdist_package_state=absent --tags installWorks with or without the config file present. That run also removes
/etc/systemd/system/<service name>.service.d/ and reloads systemd, so a later reinstall
does not inherit the drop-ins of the previous installation.
Handlers run at the end of the play, and Ansible shares them between invocations of the same role.
A role parameter read inside a handler resolves to the value of the last invocation, so with more
than one invocation in a play the restart targets the wrong service or is collapsed into a single
run. Set dnsdist_flush_handlers: true to run meta: flush_handlers as the last task of the role,
which restarts the dnsdist_service_name of that invocation.
Every instance needs its own service name and configuration file; the templated systemd unit reads
/etc/dnsdist/dnsdist-<instance>.conf:
- hosts: dnsdist
tasks:
- name: Instance a
ansible.builtin.include_role:
name: PowerDNS.dnsdist
vars:
dnsdist_service_name: dnsdist@a
dnsdist_config_location: /etc/dnsdist/dnsdist-a.conf
dnsdist_locals: ['127.0.0.1:5301']
dnsdist_controlsocket: '127.0.0.1:5401'
dnsdist_flush_handlers: true
- name: Instance b
ansible.builtin.include_role:
name: PowerDNS.dnsdist
vars:
dnsdist_service_name: dnsdist@b
dnsdist_config_location: /etc/dnsdist/dnsdist-b.conf
dnsdist_locals: ['127.0.0.1:5302']
dnsdist_controlsocket: '127.0.0.1:5402'
dnsdist_flush_handlers: trueThe dnsdist_locals and dnsdist_controlsocket of every instance have to differ, otherwise the
second instance cannot bind its addresses.
The systemd overrides of an instance are written to /etc/systemd/system/<service name>.service.d/.
meta: flush_handlers is play-wide: it also runs handlers that earlier roles in the same play
notified. dnsdist_disable_handlers: true skips the restart handlers entirely.
dnsdist_flush_handlers defaults to false, which is correct for a single invocation and wrong
for more than one: without it the pending restarts of every instance run once, at the end of the
play, against the service name of the last invocation.
On systemd hosts the restart handler reloads the units in the same task, so a restart never runs
against a unit systemd has not read. The reload happens even when dnsdist_service_state: stopped
keeps the service down, so the next manual start uses the drop-ins this run wrote.
Ansible does not filter handlers by tag, so the restart handlers read ansible_skip_tags
themselves: under --skip-tags service the service task is skipped and the handler restarts
nothing, while the systemd units of that run are still reloaded. --tags config is unaffected and
still restarts. dnsdist_disable_handlers: true remains the way to apply configuration without
restarting in a run that is not tag-filtered.
Deploy dnsdist in front of Quad9 and enable the web monitoring interface
- hosts: dnsdist
roles:
- { role: PowerDNS.dnsdist,
dnsdist_servers: ['9.9.9.9'],
dnsdist_webserver_address: "{{ ansible_default_ipv4['address'] }}:8083",
dnsdist_webserver_password: 'geheim' }A detailed changelog of all the changes applied to the role is available here.
Tests are performed by Molecule.
$ pip install tox
$ toxSee molecule/README.md for the test layout, how to run a single leg, and what to change when a new release or operating system has to be covered.
MIT