This guide provides Ansible playbooks and roles for automated dnsdist deployment on Linux servers.
# Install the role
ansible-galaxy install powerdns.dnsdist
# Create playbook
cat > site.yml << EOF
---
- hosts: dns_servers
roles:
- role: powerdns.dnsdist
EOF
# Run the playbook
ansible-playbook -i inventory.ini site.yml
# Clone or create playbook
git clone https://github.com/PowerDNS/dnsdist-ansible.git
cd dnsdist-ansible
# Run the playbook
ansible-playbook -i inventory.ini site.yml
# dnsdist_version: Version to install (default: latest)
dnsdist_version: "2.0.2"
# dnsdist_service_state: Service state
dnsdist_service_state: started
# dnsdist_service_enabled: Enable on boot
dnsdist_service_enabled: true
# dnsdist_repository: Use PowerDNS repository
dnsdist_use_powerdns_repo: true
# Listen addresses
dnsdist_listen:
- "0.0.0.0:53"
- ":::53"
# Backend servers
dnsdist_servers:
- address: "192.168.1.10:53"
- address: "192.168.1.11:53"
- address: "192.168.1.12:53"
# Load balancing policy
dnsdist_policy: "roundrobin" # roundrobin, firstAvailable, leastOutstanding, hashed
# Enable web interface
dnsdist_webserver_enabled: true
# Web interface address
dnsdist_webserver_address: "127.0.0.1:8083"
# Web interface password
dnsdist_webserver_password: "secure_password"
# ACL for web interface
dnsdist_webserver_acl:
- "127.0.0.0/8"
- "::1/128"
# Enable rate limiting
dnsdist_rate_limiting_enabled: true
# Global rate limit
dnsdist_rate_limit_qps: 10000
# Per-IP rate limit
dnsdist_rate_limit_ip_qps: 100
# Enable packet cache
dnsdist_cache_enabled: true
# Cache size (entries)
dnsdist_cache_size: 10000
# Cache TTL settings
dnsdist_cache_servfail_ttl: 60
dnsdist_cache_temp_failure_ttl: 60
# Enable DoT
dnsdist_dot_enabled: false
dnsdist_dot_port: 853
# Enable DoH
dnsdist_doh_enabled: false
dnsdist_doh_port: 443
dnsdist_doh_path: "/dns-query"
# TLS certificates
dnsdist_tls_cert: "/etc/dnsdist/certs/dnsdist.crt"
dnsdist_tls_key: "/etc/dnsdist/certs/dnsdist.key"
# Configuration format: lua (default) or yaml (v2.0+)
dnsdist_config_format: "lua"
# Configuration file path
dnsdist_config_file: "/etc/dnsdist/dnsdist.conf" # lua
# dnsdist_config_file: "/etc/dnsdist/dnsdist.yml" # yaml
---
- name: Deploy dnsdist
hosts: dns_servers
become: true
vars:
dnsdist_version: "2.0.2"
dnsdist_servers:
- address: "192.168.1.10:53"
- address: "192.168.1.11:53"
dnsdist_policy: "roundrobin"
dnsdist_webserver_enabled: true
dnsdist_webserver_password: "secure_password"
roles:
- role: powerdns.dnsdist
roles/dnsdist/tasks/main.yml:
---
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Add PowerDNS repository (Debian)
include_tasks: repository-debian.yml
when:
- ansible_os_family == "Debian"
- dnsdist_use_powerdns_repo
- name: Add PowerDNS repository (RedHat)
include_tasks: repository-redhat.yml
when:
- ansible_os_family == "RedHat"
- dnsdist_use_powerdns_repo
- name: Install dnsdist
package:
name: dnsdist
state: "{{ dnsdist_version | default('present') }}"
- name: Create configuration directory
file:
path: /etc/dnsdist
state: directory
mode: '0755'
- name: Create certificates directory
file:
path: /etc/dnsdist/certs
state: directory
mode: '0700'
- name: Deploy Lua configuration
template:
src: dnsdist.conf.j2
dest: /etc/dnsdist/dnsdist.conf
mode: '0644'
when: dnsdist_config_format == "lua"
notify: Restart dnsdist
- name: Deploy YAML configuration
template:
src: dnsdist.yml.j2
dest: /etc/dnsdist/dnsdist.yml
mode: '0644'
when: dnsdist_config_format == "yaml"
notify: Restart dnsdist
- name: Enable and start service
systemd:
name: dnsdist
enabled: "{{ dnsdist_service_enabled }}"
state: "{{ dnsdist_service_state }}"
- name: Configure firewall (firewalld)
firewalld:
service: dns
permanent: true
state: enabled
when: ansible_os_family == "RedHat"
- name: Configure firewall (UFW)
ufw:
rule: allow
port: "53"
proto: "{{ item }}"
loop:
- tcp
- udp
when: ansible_os_family == "Debian"
-- dnsdist Configuration
-- Managed by Ansible - DO NOT EDIT MANUALLY
-- Listen addresses
{% for listen in dnsdist_listen | default(['0.0.0.0:53', ':::53']) %}
addLocal("{{ listen }}")
{% endfor %}
-- Backend servers
{% for server in dnsdist_servers | default([]) %}
newServer{address="{{ server.address }}"{% if server.pool %}, pool="{{ server.pool }}"{% endif %}}
{% endfor %}
-- Load balancing policy
setServerPolicy({{ dnsdist_policy | default('roundrobin') }})
{% if dnsdist_webserver_enabled %}
-- Web interface
webserver("{{ dnsdist_webserver_address }}", "{{ dnsdist_webserver_password }}")
{% if dnsdist_webserver_acl %}
setACL({{% for acl in dnsdist_webserver_acl %}"{{ acl }}"{% if not loop.last %}, {% endif %}{% endfor %}})
{% endif %}
{% endif %}
{% if dnsdist_rate_limiting_enabled %}
-- Rate limiting
addAction(AllRule(), MaxQPSRule({{ dnsdist_rate_limit_qps | default(10000) }}))
{% endif %}
{% if dnsdist_cache_enabled %}
-- Packet cache
setPacketCacheSize({{ dnsdist_cache_size | default(10000) }})
setECSServFailTTL({{ dnsdist_cache_servfail_ttl | default(60) }})
setTempFailureTTL({{ dnsdist_cache_temp_failure_ttl | default(60) }})
{% endif %}
{% if dnsdist_dot_enabled %}
-- DNS over TLS
addTLSLocal("0.0.0.0:{{ dnsdist_dot_port | default(853) }}",
"{{ dnsdist_tls_cert }}",
"{{ dnsdist_tls_key }}")
{% endif %}
{% if dnsdist_doh_enabled %}
-- DNS over HTTPS
addDOHLocal("0.0.0.0:{{ dnsdist_doh_port | default(443) }}",
"{{ dnsdist_tls_cert }}",
"{{ dnsdist_tls_key }}",
"{{ dnsdist_doh_path | default('/dns-query') }}")
{% endif %}
[dns_servers]
dns1.example.com ansible_host=192.168.1.10
dns2.example.com ansible_host=192.168.1.11
[dns_servers:vars]
ansible_user=ansible
ansible_become=true
[primary_dns]
dns1.example.com
[backup_dns]
dns2.example.com
---
# Global dnsdist settings
dnsdist_version: "2.0.2"
dnsdist_webserver_enabled: true
dnsdist_cache_enabled: true
# Backend servers
dnsdist_servers:
- address: "192.168.1.10:53"
- address: "192.168.1.11:53"
- address: "192.168.1.12:53"
# Load balancing
dnsdist_policy: "roundrobin"
# Rate limiting
dnsdist_rate_limiting_enabled: true
dnsdist_rate_limit_qps: 10000
dnsdist_rate_limit_ip_qps: 100
# Cache
dnsdist_cache_size: 100000
---
# Primary DNS specific settings
dnsdist_listen:
- "0.0.0.0:53"
- ":::53"
# Enable DoT/DoH on primary
dnsdist_dot_enabled: true
dnsdist_doh_enabled: true
# TLS certificates
dnsdist_tls_cert: "/etc/dnsdist/certs/dns1.example.com.crt"
dnsdist_tls_key: "/etc/dnsdist/certs/dns1.example.com.key"
---
- name: Generate TLS certificates for dnsdist
hosts: dns_servers
become: true
vars:
cert_dir: "/etc/dnsdist/certs"
cert_cn: "dns.example.com"
tasks:
- name: Create certificate directory
file:
path: "{{ cert_dir }}"
state: directory
mode: '0700'
- name: Generate private key
openssl_privatekey:
path: "{{ cert_dir }}/dnsdist.key"
size: 2048
mode: '0600'
- name: Generate self-signed certificate
openssl_certificate:
path: "{{ cert_dir }}/dnsdist.crt"
privatekey_path: "{{ cert_dir }}/dnsdist.key"
provider: selfsigned
subject:
commonName: "{{ cert_cn }}"
mode: '0644'
- name: Set certificate permissions
file:
path: "{{ item }}"
owner: dnsdist
group: dnsdist
mode: '0644'
loop:
- "{{ cert_dir }}/dnsdist.crt"
when: item != cert_dir + '/dnsdist.key'
- name: Set key permissions
file:
path: "{{ cert_dir }}/dnsdist.key"
owner: dnsdist
group: dnsdist
mode: '0600'
---
- name: Deploy dnsdist with DoT/DoH
hosts: dns_servers
become: true
vars:
dnsdist_dot_enabled: true
dnsdist_doh_enabled: true
dnsdist_tls_cert: "/etc/dnsdist/certs/dnsdist.crt"
dnsdist_tls_key: "/etc/dnsdist/certs/dnsdist.key"
pre_tasks:
- name: Install OpenSSL
package:
name: openssl
state: present
roles:
- role: powerdns.dnsdist
tasks:
- name: Generate TLS certificates
include_tasks: generate-certs.yml
- name: Validate configuration
command: dnsdist --check-config
register: config_validation
changed_when: false
- name: Display validation result
debug:
var: config_validation.stdout
---
- name: Configure Prometheus scraping for dnsdist
hosts: monitoring_servers
become: true
vars:
prometheus_config: "/etc/prometheus/prometheus.yml"
tasks:
- name: Add dnsdist to Prometheus config
blockinfile:
path: "{{ prometheus_config }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK - dnsdist"
block: |
- job_name: 'dnsdist'
static_configs:
- targets:
{% for host in groups['dns_servers'] %}
- '{{ host }}:8083'
{% endfor %}
notify: Reload Prometheus
---
- name: Deploy Grafana dashboard for dnsdist
hosts: grafana_servers
become: true
vars:
grafana_url: "http://localhost:3000"
grafana_admin_user: "admin"
grafana_admin_password: "admin"
tasks:
- name: Import dnsdist dashboard
uri:
url: "{{ grafana_url }}/api/dashboards/db"
method: POST
user: "{{ grafana_admin_user }}"
password: "{{ grafana_admin_password }}"
headers:
Content-Type: application/json
body_format: json
body:
dashboard: "{{ lookup('file', 'dnsdist-dashboard.json') }}"
overwrite: true
---
- name: Deploy dnsdist to all DNS servers
hosts: dns_servers
become: true
gather_facts: true
pre_tasks:
- name: Verify Ansible version
assert:
that: ansible_version.full is version('2.9', '>=')
msg: "Ansible 2.9 or higher is required"
- name: Update package cache
package:
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
roles:
- role: powerdns.dnsdist
tags:
- dnsdist
- dns
post_tasks:
- name: Verify service is running
systemd:
name: dnsdist
register: service_status
- name: Display service status
debug:
var: service_status.status.ActiveState
- name: Test DNS resolution
command: dig @127.0.0.1 example.com +short
register: dns_test
changed_when: false
- name: Display DNS test result
debug:
var: dns_test.stdout
---
- name: Rolling update of dnsdist
hosts: dns_servers
become: true
serial: 1
max_fail_percentage: 0
tasks:
- name: Stop service
systemd:
name: dnsdist
state: stopped
- name: Upgrade package
package:
name: dnsdist
state: latest
- name: Start service
systemd:
name: dnsdist
state: started
- name: Wait for service
wait_for:
port: 53
delay: 5
timeout: 30
- name: Test DNS resolution
command: dig @127.0.0.1 example.com +short
register: dns_test
retries: 3
until: dns_test.rc == 0
changed_when: false
We develop tailored automation solutions for:
Let’s discuss your requirements: office@linux-server-admin.com | Contact