This guide provides Ansible playbooks and roles for automated Knot Resolver deployment on Linux servers.
# Clone or download the playbook
git clone https://github.com/your-org/knot-resolver-ansible.git
cd knot-resolver-ansible
# Install requirements
ansible-galaxy install -r requirements.yml
# Run the playbook
ansible-playbook -i inventory.ini site.yml
ansible-playbook -i server1, knot-resolver.yml \
-e "knot_resolver_version=6.1.0" \
-e "knot_resolver_forwarders=['1.0.0.1', '8.8.8.8']"
# requirements.yml
---
collections:
- name: community.general
version: ">=7.0.0"
- name: ansible.posix
version: ">=1.5.0"
# knot_resolver_version: Version to install (default: latest v6)
knot_resolver_version: "6.1.0"
# knot_resolver_service_state: Service state
knot_resolver_service_state: started
# knot_resolver_service_enabled: Enable on boot
knot_resolver_service_enabled: true
# knot_resolver_repository: Repository to use
knot_resolver_repository: "https://repo.knot-resolver.cz/debian/"
# Listen interfaces
knot_resolver_interfaces:
- "0.0.0.0:53"
- ":::53"
# Upstream forwarders
knot_resolver_forwarders:
- address: "1.0.0.1"
- address: "8.8.8.8"
- address: "9.9.9.9"
# Access control (ACL)
knot_resolver_access_control:
- network: "10.0.0.0/8"
action: "allow"
- network: "192.168.0.0/16"
action: "allow"
# Cache size in bytes
knot_resolver_cache_size: 524288000 # 500 MB
# Cache TTL settings
knot_resolver_cache_max_ttl: 604800 # 1 week
knot_resolver_cache_min_ttl: 60 # 1 minute
# Enable DNSSEC validation
knot_resolver_dnssec_enable: true
# DNSSEC trust anchor file (auto-managed if not specified)
knot_resolver_dnssec_trust_anchor: ""
# Enable rate limiting
knot_resolver_rate_limit_enable: true
# Queries per second per IP
knot_resolver_rate_limit: 100
# Log level: crit, err, warning, notice, info, debug
knot_resolver_log_level: "notice"
# Log to syslog
knot_resolver_log_syslog: true
# Enable Prometheus metrics
knot_resolver_monitoring_enable: true
# Metrics port
knot_resolver_metrics_port: 8453
# Enable DoT
knot_resolver_dot_enable: false
knot_resolver_dot_port: 853
# Enable DoH
knot_resolver_doh_enable: false
knot_resolver_doh_port: 443
knot_resolver_doh_path: "/dns-query"
# TLS certificates
knot_resolver_tls_cert: "/etc/knot-resolver/certs/tls.crt"
knot_resolver_tls_key: "/etc/knot-resolver/certs/tls.key"
---
- name: Deploy Knot Resolver
hosts: dns_servers
become: true
vars:
knot_resolver_version: "6.1.0"
knot_resolver_forwarders:
- address: "1.0.0.1"
- address: "8.8.8.8"
knot_resolver_dnssec_enable: true
knot_resolver_monitoring_enable: true
roles:
- role: knot-resolver
---
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Add Knot Resolver repository (Debian)
include_tasks: repository-debian.yml
when: ansible_os_family == "Debian"
- name: Add Knot Resolver repository (RedHat)
include_tasks: repository-redhat.yml
when: ansible_os_family == "RedHat"
- name: Install Knot Resolver
package:
name: knot-resolver
state: present
- name: Create configuration directory
file:
path: /etc/knot-resolver
state: directory
mode: '0755'
- name: Create cache directory
file:
path: /var/cache/knot-resolver
state: directory
mode: '0755'
owner: knot-resolver
group: knot-resolver
- name: Deploy configuration
template:
src: config.yaml.j2
dest: /etc/knot-resolver/config.yaml
mode: '0644'
notify: Restart Knot Resolver
- name: Enable and start service
systemd:
name: knot-resolver
enabled: "{{ knot_resolver_service_enabled }}"
state: "{{ knot_resolver_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"
# Knot Resolver Configuration
# Managed by Ansible - DO NOT EDIT MANUALLY
# Server configuration
server:
interfaces:
{% for interface in knot_resolver_interfaces | default(['0.0.0.0:53', ':::53']) %}
- "{{ interface }}"
{% endfor %}
# DNSSEC validation
dnssec:
enable: {{ knot_resolver_dnssec_enable | default(true) | lower }}
# Upstream forwarders
forward:
{% for forwarder in knot_resolver_forwarders | default([]) %}
- address: "{{ forwarder.address | default(forwarder) }}"
{% endfor %}
# Cache configuration
cache:
max_size: {{ knot_resolver_cache_size | default(524288000) }}
{% if knot_resolver_cache_max_ttl is defined %}
max_ttl: {{ knot_resolver_cache_max_ttl }}
{% endif %}
{% if knot_resolver_cache_min_ttl is defined %}
min_ttl: {{ knot_resolver_cache_min_ttl }}
{% endif %}
# Access control
{% if knot_resolver_access_control is defined %}
policy:
access_control:
{% for acl in knot_resolver_access_control %}
- network: "{{ acl.network }}"
action: "{{ acl.action }}"
{% endfor %}
{% endif %}
# Rate limiting
{% if knot_resolver_rate_limit_enable | default(false) %}
rate_limiting:
enable: true
limit: {{ knot_resolver_rate_limit | default(100) }}
{% endif %}
# Logging
logging:
level: "{{ knot_resolver_log_level | default('notice') }}"
# Monitoring
{% if knot_resolver_monitoring_enable | default(false) %}
monitoring:
metrics: true
port: {{ knot_resolver_metrics_port | default(8453) }}
{% endif %}
# DNS over TLS
{% if knot_resolver_dot_enable | default(false) %}
tls:
enable: true
listen:
- address: "0.0.0.0:{{ knot_resolver_dot_port | default(853) }}"
cert: "{{ knot_resolver_tls_cert }}"
key: "{{ knot_resolver_tls_key }}"
{% endif %}
# DNS over HTTPS
{% if knot_resolver_doh_enable | default(false) %}
http:
enable: true
listen:
- address: "0.0.0.0:{{ knot_resolver_doh_port | default(443) }}"
cert: "{{ knot_resolver_tls_cert }}"
key: "{{ knot_resolver_tls_key }}"
path: "{{ knot_resolver_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 DNS settings
knot_resolver_version: "6.1.0"
knot_resolver_dnssec_enable: true
knot_resolver_monitoring_enable: true
# Upstream forwarders
knot_resolver_forwarders:
- address: "1.0.0.1"
- address: "8.8.8.8"
- address: "9.9.9.9"
# Cache settings
knot_resolver_cache_size: 1073741824 # 1 GB
# Rate limiting
knot_resolver_rate_limit_enable: true
knot_resolver_rate_limit: 100
# Logging
knot_resolver_log_level: "notice"
---
# Primary DNS specific settings
knot_resolver_interfaces:
- "0.0.0.0:53"
- ":::53"
# Enable DoT/DoH on primary
knot_resolver_dot_enable: true
knot_resolver_doh_enable: true
# TLS certificates
knot_resolver_tls_cert: "/etc/knot-resolver/certs/dns1.example.com.crt"
knot_resolver_tls_key: "/etc/knot-resolver/certs/dns1.example.com.key"
---
- name: Generate TLS certificates for Knot Resolver
hosts: dns_servers
become: true
vars:
cert_dir: "/etc/knot-resolver/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 }}/tls.key"
size: 2048
mode: '0600'
- name: Generate self-signed certificate
openssl_certificate:
path: "{{ cert_dir }}/tls.crt"
privatekey_path: "{{ cert_dir }}/tls.key"
provider: selfsigned
subject:
commonName: "{{ cert_cn }}"
mode: '0644'
- name: Deploy certificates to Knot Resolver
copy:
src: "{{ cert_dir }}/{{ item }}"
dest: "{{ cert_dir }}/{{ item }}"
mode: '0644'
loop:
- tls.crt
- tls.key
notify: Restart Knot Resolver
---
- name: Deploy Knot Resolver with DoT/DoH
hosts: dns_servers
become: true
vars:
knot_resolver_dot_enable: true
knot_resolver_doh_enable: true
knot_resolver_tls_cert: "/etc/knot-resolver/certs/tls.crt"
knot_resolver_tls_key: "/etc/knot-resolver/certs/tls.key"
pre_tasks:
- name: Install OpenSSL
package:
name: openssl
state: present
roles:
- role: knot-resolver
tasks:
- name: Generate TLS certificates
include_tasks: generate-certs.yml
- name: Validate configuration
command: kresctl validate
register: config_validation
changed_when: false
- name: Display validation result
debug:
var: config_validation.stdout
---
- name: Configure Prometheus scraping for Knot Resolver
hosts: monitoring_servers
become: true
vars:
prometheus_config: "/etc/prometheus/prometheus.yml"
tasks:
- name: Add Knot Resolver to Prometheus config
blockinfile:
path: "{{ prometheus_config }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK - Knot Resolver"
block: |
- job_name: 'knot-resolver'
static_configs:
- targets:
{% for host in groups['dns_servers'] %}
- '{{ host }}:8453'
{% endfor %}
notify: Reload Prometheus
---
- name: Deploy Grafana dashboard for Knot Resolver
hosts: grafana_servers
become: true
vars:
grafana_url: "http://localhost:3000"
grafana_admin_user: "admin"
grafana_admin_password: "admin"
tasks:
- name: Import Knot Resolver 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', 'knot-resolver-dashboard.json') }}"
overwrite: true
---
- name: Deploy Knot Resolver 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.12', '>=')
msg: "Ansible 2.12 or higher is required"
- name: Update package cache
package:
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
roles:
- role: knot-resolver
tags:
- knot-resolver
- dns
post_tasks:
- name: Verify service is running
systemd:
name: knot-resolver
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 Knot Resolver
hosts: dns_servers
become: true
serial: 1
max_fail_percentage: 0
tasks:
- name: Stop service
systemd:
name: knot-resolver
state: stopped
- name: Upgrade package
package:
name: knot-resolver
state: latest
- name: Start service
systemd:
name: knot-resolver
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
---
- name: Validate Knot Resolver deployment
hosts: dns_servers
become: false
gather_facts: false
tasks:
- name: Check service status
command: kresctl status
register: service_status
changed_when: false
- name: Display service status
debug:
var: service_status.stdout
- name: Validate configuration
command: kresctl validate
register: config_validation
changed_when: false
failed_when: config_validation.rc != 0
- name: Test DNS resolution
command: dig @127.0.0.1 example.com +short
register: dns_test
changed_when: false
- name: Display DNS test
debug:
var: dns_test.stdout
- name: Test DNSSEC validation
command: dig @127.0.0.1 dnssec-failed.org +short
register: dnssec_test
changed_when: false
- name: Check DNSSEC (should return SERVFAIL)
assert:
that:
- "'SERVFAIL' in dnssec_test.stderr or dnssec_test.rc != 0"
fail_msg: "DNSSEC validation may not be working"
success_msg: "DNSSEC validation is working"
---
# handlers/main.yml
- name: Restart Knot Resolver
systemd:
name: knot-resolver
state: restarted
when: knot_resolver_service_state == 'started'
- name: Reload Knot Resolver
systemd:
name: knot-resolver
state: reloaded
when: knot_resolver_service_state == 'started'
- name: Validate configuration
command: kresctl validate
register: config_validation
changed_when: false
- name: Check service status
systemd:
name: knot-resolver
register: service_check
---
- name: Debug Knot Resolver issues
hosts: "{{ target | default('dns_servers') }}"
become: true
tasks:
- name: Check service status
systemd:
name: knot-resolver
register: service_status
- name: Display service details
debug:
var: service_status
- name: Get recent logs
command: journalctl -u knot-resolver -n 50 --no-pager
register: recent_logs
changed_when: false
- name: Display recent logs
debug:
var: recent_logs.stdout
- name: Check configuration file
stat:
path: /etc/knot-resolver/config.yaml
register: config_file
- name: Display config file info
debug:
var: config_file.stat
- name: Validate configuration
command: kresctl validate
register: validation
changed_when: false
failed_when: false
- name: Display validation result
debug:
var: validation
Beyond this playbook, we offer:
Contact our automation team: office@linux-server-admin.com | Contact Page