Standalone BIND DNS Deployment (Headless)
1. Overview
Deploy standalone BIND DNS server on Rocky 9 using cloud images for fully headless deployment. This is the enterprise pattern - DNS as a dedicated, separated service.
2. Phase 1: Download Cloud Image
If not already downloaded:
cd /var/lib/libvirt/images
sudo curl -LO https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
Verify download (~620MB):
ls -lh Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
-rw-r--r-- 1 root root 619M Feb 15 08:00 Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
2.1. Verify Checksum (Security)
|
Always verify checksums - HTTPS only proves server identity, not file integrity. If Rocky’s servers were compromised, you’d download malware over valid HTTPS. Checksums prove the file matches what Rocky published. |
Download the checksum file:
sudo curl -LO https://dl.rockylinux.org/pub/rocky/9/images/x86_64/CHECKSUM
Extract and verify (Rocky uses BSD-format checksums):
# Get expected hash from CHECKSUM file
grep "GenericCloud-Base.latest.x86_64.qcow2" CHECKSUM
# Rocky-9-GenericCloud-Base.latest.x86_64.qcow2: 648806400 bytes SHA256 (Rocky-9-GenericCloud-Base.latest.x86_64.qcow2) = 15d81d3434b298142b2fdd8fb54aef2662684db5c082cc191c3c79762ed6360c
# Verify hash matches (converts BSD → GNU format)
EXPECTED=$(grep "GenericCloud-Base.latest.x86_64.qcow2)" CHECKSUM | awk '{print $4}')
echo "$EXPECTED Rocky-9-GenericCloud-Base.latest.x86_64.qcow2" | sha256sum -c
Rocky-9-GenericCloud-Base.latest.x86_64.qcow2: OK
|
If verification fails, DO NOT USE THE IMAGE. Re-download or investigate. |
3. Phase 2: Create cloud-init Configuration
3.3. user-data
cat > user-data << 'EOF'
#cloud-config
hostname: bind-01
fqdn: bind-01.inside.domusdigitalis.dev
manage_etc_hosts: true
users:
- name: evanusmodestus
groups: wheel
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false
plain_text_passwd: REPLACE_WITH_GOPASS_PASSWORD
ssh_authorized_keys:
# YubiKey primary
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIFHfsGSAFAkqwYj6EGS9sA2MROjs28zM6LJds3gagsCkAAAACHNzaDpkMDAw evanusmodestus@d000-yubikey
# YubiKey secondary
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEBZ+kus4aTHzQt1zNnEnGxJs+Lf56vrCdcyvqLhpp9hAAAACHNzaDpkMDAw evanusmodestus@d000-secondary
runcmd:
# Configure static IP (interface is eth0 on Rocky cloud images)
- nmcli connection delete 'Wired connection 1' 2>/dev/null || true
- nmcli con add type ethernet ifname eth0 con-name mgmt ipv4.addresses {bind-ip}/24 ipv4.gateway {vyos-vip} ipv4.dns {vyos-vip} ipv4.method manual
- nmcli con up mgmt
- dnf install -y bind bind-utils
final_message: "Cloud-init completed. System ready for BIND configuration."
EOF
|
Password: Generate with |
|
Interface Name: Rocky 9 cloud images use |
4. Phase 3: Create VM Disk
# Copy base image
sudo cp /var/lib/libvirt/images/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 \
/mnt/onboard-ssd/vms/bind-01.qcow2
# Resize to 20GB (DNS doesn't need much)
sudo qemu-img resize /mnt/onboard-ssd/vms/bind-01.qcow2 20G
5. Phase 4: Create VM
sudo virt-install \
--name bind-01 \
--memory 2048 \
--vcpus 2 \
--disk path=/mnt/onboard-ssd/vms/bind-01.qcow2,format=qcow2 \
--disk path=/mnt/onboard-ssd/vms/bind-01-cloud-init.iso,device=cdrom \
--network bridge=virbr0,model=virtio \
--os-variant rocky9 \
--graphics none \
--console pty,target_type=serial \
--import \
--noautoconsole
6. Phase 5: Verify VM Boot
# Check status
sudo virsh list --all | grep bind
# View console
sudo virsh console bind-01
Press Enter, login with evanusmodestus.
Exit console: Ctrl+]
7. Phase 6: DNS & DHCP Reservation
7.2. VyOS DHCP Reservation
VyOS DHCP is configured via CLI. SSH to vyos-01 and add static mapping:
configure
set service dhcp-server shared-network-name MGMT subnet 10.50.1.0/24 static-mapping bind-01 ip-address 10.50.1.90
set service dhcp-server shared-network-name MGMT subnet 10.50.1.0/24 static-mapping bind-01 mac-address <MAC-FROM-ABOVE>
commit
save
8. Phase 7: Workstation Setup (Prerequisites)
Before configuring BIND, set up your workstation for SSH access.
8.1. Password Management (gopass)
Generate and store password for cloud-init:
gopass generate -s v2/DOMUS/servers/bind-01/evanusmodestus 24
gopass show v2/DOMUS/servers/bind-01/evanusmodestus
Copy to clipboard when needed:
gopass show -c v2/DOMUS/servers/bind-01/evanusmodestus
8.2. Workstation SSH Config
Add to ~/.ssh/config on your workstation:
cat >> ~/.ssh/config << 'EOF'
# ═══════════════════════════════════════════════════════════════════════════════════════
# HOME INFRASTRUCTURE - DNS & DIRECTORY SERVICES
# ═══════════════════════════════════════════════════════════════════════════════════════
Host bind-01
HostName 10.50.1.90
User evanusmodestus
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_secondary
IdentityFile ~/.ssh/id_ed25519_d000
PasswordAuthentication yes
PreferredAuthentications publickey,password
Host bind-02
HostName 10.50.1.91
User evanusmodestus
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_secondary
IdentityFile ~/.ssh/id_ed25519_d000
PasswordAuthentication yes
PreferredAuthentications publickey,password
Host ipa-01
HostName 10.50.1.100
User evanusmodestus
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_secondary
IdentityFile ~/.ssh/id_ed25519_d000
PasswordAuthentication yes
PreferredAuthentications publickey,password
Host ipa-02
HostName 10.50.1.101
User evanusmodestus
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000
IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_secondary
IdentityFile ~/.ssh/id_ed25519_d000
PasswordAuthentication yes
PreferredAuthentications publickey,password
EOF
8.3. SSH Connection Test
ssh bind-01
Enter passphrase for key '/home/evanusmodestus/.ssh/id_ed25519_sk_rk_d000': Confirm user presence for key ED25519-SK SHA256:UuOTOPAPIYYAG9sSW37hmAAHmCfKJuQ2eaQq6JSHILQ User presence confirmed Activate the web console with: systemctl enable --now cockpit.socket Last login: Sun Feb 15 06:54:26 2026 [evanusmodestus@bind-01 ~]$
9. Phase 8: Configure BIND
All commands in this phase run on bind-01 via SSH.
9.1. named.conf
sudo tee /etc/named.conf << 'EOF'
//
// BIND Configuration for inside.domusdigitalis.dev
//
options {
listen-on port 53 { 127.0.0.1; 10.50.1.90; };
listen-on-v6 { none; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
// Allow queries from management network
allow-query { localhost; 10.50.1.0/24; };
// Forward external queries (upstream resolvers)
forwarders {
8.8.8.8; // Google
1.1.1.1; // Cloudflare
};
recursion yes;
dnssec-validation auto;
// Security hardening
version "not disclosed";
allow-transfer { none; };
rate-limit {
responses-per-second 10;
window 5;
};
managed-keys-directory "/var/named/dynamic";
geoip-directory "/usr/share/GeoIP";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
};
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
};
// Root hints
zone "." IN {
type hint;
file "named.ca";
};
// Localhost zones
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
// inside.domusdigitalis.dev zone
zone "inside.domusdigitalis.dev" IN {
type master;
file "inside.domusdigitalis.dev.zone";
allow-update { none; };
};
// Reverse zone for 10.50.1.x
zone "1.50.10.in-addr.arpa" IN {
type master;
file "10.50.1.rev";
allow-update { none; };
};
EOF
9.2. Forward Zone File
sudo tee /var/named/inside.domusdigitalis.dev.zone << 'EOF'
$TTL 86400
@ IN SOA bind-01.inside.domusdigitalis.dev. admin.inside.domusdigitalis.dev. (
2026031101 ; Serial (YYYYMMDDNN)
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
; Name servers
@ IN NS bind-01.inside.domusdigitalis.dev.
; Gateway - VyOS VRRP (.1-3)
vyos-vip IN A 10.50.1.1
vyos-01 IN A 10.50.1.2
vyos-02 IN A 10.50.1.3
; Network Devices (.10-19)
3560cx-01 IN A 10.50.1.10
9300-01 IN A 10.50.1.11
; Identity Services (.20-29)
ise-01 IN A 10.50.1.20
ise-02 IN A 10.50.1.21
; iPSK Manager (.30-39)
ipsk-mgr-01 IN A 10.50.1.30
ipsk-mgr-02 IN A 10.50.1.31
; Wireless Controllers (.40-49)
9800-wlc-01 IN A 10.50.1.40
wlc-01 IN A 10.50.1.40
9800-wlc-02 IN A 10.50.1.41
wlc-02 IN A 10.50.1.41
; Windows Servers (.50-59)
home-dc01 IN A 10.50.1.50
home-dc02 IN A 10.50.1.51
; PKI Services - Vault HA Cluster (.60-69)
vault-01 IN A 10.50.1.60
vault-02 IN A 10.50.1.61
vault-03 IN A 10.50.1.62
; Storage/Git (.70-79)
nas-01 IN A 10.50.1.70
nas-02 IN A 10.50.1.71
gitea-01 IN A 10.50.1.70
; IdP/SSO (.80-89)
keycloak-01 IN A 10.50.1.80
keycloak-02 IN A 10.50.1.81
; DNS Services (.90-99)
bind-01 IN A 10.50.1.90
bind-02 IN A 10.50.1.91
; LDAP/Directory (.100-109)
ipa-01 IN A 10.50.1.100
ipa-02 IN A 10.50.1.101
; KVM Hypervisors (.110-119)
kvm-01 IN A 10.50.1.110
kvm-02 IN A 10.50.1.111
; Kubernetes (.120-129)
k3s-master-01 IN A 10.50.1.120
k3s-master-02 IN A 10.50.1.121
k3s-master-03 IN A 10.50.1.122
; Ingress & Monitoring (.130-139)
traefik-vip IN A 10.50.1.130
wazuh-indexer IN A 10.50.1.131
wazuh-manager IN A 10.50.1.134
grafana IN A 10.50.1.132
prometheus IN A 10.50.1.133
alertmanager IN A 10.50.1.135
; IPMI/BMC (.200-209)
ipmi-01 IN A 10.50.1.200
ipmi-02 IN A 10.50.1.201
; ============================================================================
; ACTIVE DIRECTORY SRV RECORDS (CRITICAL for Kerberos/LDAP)
; ============================================================================
; These records are REQUIRED for AD domain join, Kerberos authentication,
; and LDAP lookups. Windows DC auto-registers these via dynamic DNS.
; When migrating DNS to BIND, you MUST add these manually!
;
; Without these records:
; - kinit fails: "Cannot find KDC for realm"
; - Domain join fails
; - AD group lookups fail (affects 802.1X authorization)
; ============================================================================
; Kerberos KDC
_kerberos._tcp IN SRV 0 100 88 home-dc01
_kerberos._udp IN SRV 0 100 88 home-dc01
; Kerberos password change
_kpasswd._tcp IN SRV 0 100 464 home-dc01
_kpasswd._udp IN SRV 0 100 464 home-dc01
; LDAP
_ldap._tcp IN SRV 0 100 389 home-dc01
; Global Catalog (for forest-wide searches)
_gc._tcp IN SRV 0 100 3268 home-dc01
; Domain Controller Locator (Microsoft-specific)
_ldap._tcp.dc._msdcs IN SRV 0 100 389 home-dc01
_kerberos._tcp.dc._msdcs IN SRV 0 100 88 home-dc01
_ldap._tcp.gc._msdcs IN SRV 0 100 3268 home-dc01
; Aliases
ise IN CNAME ise-01
keycloak IN CNAME keycloak-01
ipsk IN CNAME ipsk-mgr-01
dc IN CNAME home-dc01
ipa IN CNAME ipa-01
dns IN CNAME bind-01
vault IN CNAME vault-01
nas IN CNAME nas-01
gitea IN CNAME gitea-01
wlc IN CNAME 9800-wlc-01
kvm IN CNAME kvm-01
EOF
Windows DC DNS Migration Warning
When migrating DNS from Windows Server (AD-integrated DNS) to BIND, the AD SRV records above are NOT automatically created. Windows DCs register these records dynamically via secure DNS updates.
If you skip these records:
-
kinitfails with "Cannot find KDC for realm DOMAIN.COM" -
Workstations cannot join the domain
-
ISE AD group authorization fails (802.1X policies don’t match)
-
LDAP lookups fail
Always verify after migration:
dig SRV _kerberos._tcp.inside.domusdigitalis.dev +short
dig SRV _ldap._tcp.inside.domusdigitalis.dev +short
9.3. Reverse Zone File
sudo tee /var/named/10.50.1.rev << 'EOF'
$TTL 86400
@ IN SOA bind-01.inside.domusdigitalis.dev. admin.inside.domusdigitalis.dev. (
2026031101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
@ IN NS bind-01.inside.domusdigitalis.dev.
; Gateway - VyOS VRRP (.1-3)
1 IN PTR vyos-vip.inside.domusdigitalis.dev.
2 IN PTR vyos-01.inside.domusdigitalis.dev.
3 IN PTR vyos-02.inside.domusdigitalis.dev.
; Network Devices (.10-19)
10 IN PTR 3560cx-01.inside.domusdigitalis.dev.
11 IN PTR 9300-01.inside.domusdigitalis.dev.
; Identity Services (.20-29)
20 IN PTR ise-01.inside.domusdigitalis.dev.
21 IN PTR ise-02.inside.domusdigitalis.dev.
; iPSK Manager (.30-39)
30 IN PTR ipsk-mgr-01.inside.domusdigitalis.dev.
31 IN PTR ipsk-mgr-02.inside.domusdigitalis.dev.
; Wireless Controllers (.40-49)
40 IN PTR 9800-wlc-01.inside.domusdigitalis.dev.
41 IN PTR 9800-wlc-02.inside.domusdigitalis.dev.
; Windows Servers (.50-59)
50 IN PTR home-dc01.inside.domusdigitalis.dev.
51 IN PTR home-dc02.inside.domusdigitalis.dev.
; PKI Services - Vault HA Cluster (.60-69)
60 IN PTR vault-01.inside.domusdigitalis.dev.
61 IN PTR vault-02.inside.domusdigitalis.dev.
62 IN PTR vault-03.inside.domusdigitalis.dev.
; Storage/Git (.70-79)
70 IN PTR nas-01.inside.domusdigitalis.dev.
71 IN PTR nas-02.inside.domusdigitalis.dev.
; IdP/SSO (.80-89)
80 IN PTR keycloak-01.inside.domusdigitalis.dev.
81 IN PTR keycloak-02.inside.domusdigitalis.dev.
; DNS Services (.90-99)
90 IN PTR bind-01.inside.domusdigitalis.dev.
91 IN PTR bind-02.inside.domusdigitalis.dev.
; LDAP/Directory (.100-109)
100 IN PTR ipa-01.inside.domusdigitalis.dev.
101 IN PTR ipa-02.inside.domusdigitalis.dev.
; KVM Hypervisors (.110-119)
110 IN PTR kvm-01.inside.domusdigitalis.dev.
111 IN PTR kvm-02.inside.domusdigitalis.dev.
; Kubernetes (.120-129)
120 IN PTR k3s-master-01.inside.domusdigitalis.dev.
121 IN PTR k3s-master-02.inside.domusdigitalis.dev.
122 IN PTR k3s-master-03.inside.domusdigitalis.dev.
; Ingress & Monitoring (.130-139)
130 IN PTR traefik-vip.inside.domusdigitalis.dev.
131 IN PTR wazuh-indexer.inside.domusdigitalis.dev.
132 IN PTR grafana.inside.domusdigitalis.dev.
133 IN PTR prometheus.inside.domusdigitalis.dev.
134 IN PTR wazuh-manager.inside.domusdigitalis.dev.
135 IN PTR alertmanager.inside.domusdigitalis.dev.
; IPMI/BMC (.200-209)
200 IN PTR ipmi-01.inside.domusdigitalis.dev.
201 IN PTR ipmi-02.inside.domusdigitalis.dev.
EOF
9.4. Set Permissions
sudo chown root:named /var/named/inside.domusdigitalis.dev.zone /var/named/10.50.1.rev
sudo chmod 640 /var/named/inside.domusdigitalis.dev.zone /var/named/10.50.1.rev
ls -la /var/named/*.zone /var/named/*.rev
-rw-r-----. 1 root named 1850 Feb 15 12:00 /var/named/inside.domusdigitalis.dev.zone -rw-r-----. 1 root named 1200 Feb 15 12:00 /var/named/10.50.1.rev
9.5. Validate Configuration
sudo named-checkconf /etc/named.conf
(no output = success)
sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zone
zone inside.domusdigitalis.dev/IN: loaded serial 2026021401 OK
sudo named-checkzone 1.50.10.in-addr.arpa /var/named/10.50.1.rev
zone 1.50.10.in-addr.arpa/IN: loaded serial 2026021401 OK
10. Phase 9: Start BIND
sudo systemctl enable --now named
Created symlink /etc/systemd/system/multi-user.target.wants/named.service → /usr/lib/systemd/system/named.service.
sudo systemctl status named
● named.service - Berkeley Internet Name Domain (DNS)
Loaded: loaded (/usr/lib/systemd/system/named.service; enabled; preset: disabled)
Active: active (running) since Sat 2026-02-15 ...
Main PID: 12345 (named)
Tasks: 5 (limit: 23081)
Memory: 20.0M
CPU: 50ms
CGroup: /system.slice/named.service
└─12345 /usr/sbin/named -u named -c /etc/named.conf
10.1. Configure Firewall
Rocky 9 GenericCloud images don’t include firewalld. Install and enable it first:
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
(no output = already enabled by package installation)
sudo firewall-cmd --permanent --add-service=dns
success
sudo firewall-cmd --reload
success
sudo firewall-cmd --list-services
cockpit dhcpv6-client dns ssh
11. Phase 10: Test DNS
11.1. From bind-01 itself
dig @localhost ise-01.inside.domusdigitalis.dev +short
10.50.1.20
dig @localhost ise-01.inside.domusdigitalis.dev
; <<>> DiG 9.16.23-RH <<>> @localhost ise-01.inside.domusdigitalis.dev ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: ... (good) ;; QUESTION SECTION: ;ise-01.inside.domusdigitalis.dev. IN A ;; ANSWER SECTION: ise-01.inside.domusdigitalis.dev. 86400 IN A 10.50.1.20 ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sat Feb 15 12:00:00 UTC 2026 ;; MSG SIZE rcvd: 89
11.2. Reverse Lookup (PTR)
dig @localhost -x 10.50.1.20 +short
ise-01.inside.domusdigitalis.dev.
dig @localhost -x 10.50.1.20
; <<>> DiG 9.16.23-RH <<>> @localhost -x 10.50.1.20 ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;20.1.50.10.in-addr.arpa. IN PTR ;; ANSWER SECTION: 20.1.50.10.in-addr.arpa. 86400 IN PTR ise-01.inside.domusdigitalis.dev. ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sat Feb 15 12:00:00 UTC 2026 ;; MSG SIZE rcvd: 100
11.3. Test CNAME Aliases
dig @localhost ise.inside.domusdigitalis.dev +short
ise-01.inside.domusdigitalis.dev. 10.50.1.20
11.4. From another host (workstation)
dig @10.50.1.90 ise-01.inside.domusdigitalis.dev +short
{ise-01-ip}
12. Phase 11: Configure VyOS DNS Forwarding
Configure VyOS to forward DNS queries to BIND (bind-01/bind-02).
|
VyOS acts as a DNS forwarder, sending all queries to BIND. BIND handles both authoritative zones (inside.domusdigitalis.dev) and recursive queries (external domains). |
12.1. Via VyOS CLI
SSH to vyos-01 and configure DNS forwarding:
ssh vyos-01
configure
set service dns forwarding listen-address 10.50.1.1
set service dns forwarding name-server 10.50.1.90
set service dns forwarding name-server 10.50.1.91
set service dns forwarding allow-from 10.50.0.0/16
commit
save
14. Troubleshooting
14.3. Permission Issues
sudo ls -la /var/named/
# All zone files should be owned by root:named, mode 640
14.4. DNS Queries "Refused" from Non-MGMT VLANs
|
If DNS works from VLAN 100 (MGMT) but fails from VLAN 10 (DATA), VLAN 20 (VOICE), etc., this is NOT a firewall issue - it’s a BIND ACL issue. |
14.4.1. Symptoms
-
DNS resolution works from MGMT network (10.50.1.0/24)
-
DNS resolution fails from employee VLANs (DATA, VOICE, etc.)
-
dig @10.50.1.90 google.comreturns nothing or times out -
Pinging 8.8.8.8 works (internet path is fine)
-
VyOS firewall logs show NO drops (traffic is allowed)
14.4.2. Diagnosis
Step 1: Capture traffic on VyOS:
# On VyOS
sudo tcpdump -i eth0.10 port 53 -n
Step 2: Test from VLAN 10 (another terminal):
dig @10.50.1.90 google.com
Step 3: Watch tcpdump output:
01:29:12.881867 IP 10.50.10.102.62677 > 10.50.1.91.53: 515+ A? CISCO-CAPWAP-CONTROLLER.inside.domusdigitalis.dev. (67) 01:29:12.881868 IP 10.50.10.102.62677 > 10.50.1.90.53: 515+ A? CISCO-CAPWAP-CONTROLLER.inside.domusdigitalis.dev. (67) 01:29:12.882457 IP 10.50.1.90.53 > 10.50.10.102.62620: 41284 Refused- 0/0/0 (67) 01:29:12.883036 IP 10.50.1.90.53 > 10.50.10.102.62677: 515 Refused- 0/0/0 (67)
Key observation:
-
Query goes OUT:
10.50.10.102 > 10.50.1.90.53(client → BIND) -
Response comes BACK:
10.50.1.90.53 > 10.50.10.102(BIND → client) -
Response says:
Refused(not timeout, not NXDOMAIN)
This proves: BIND is receiving the query, but REFUSING it due to ACL.
14.4.3. Root Cause
Default BIND config restricts queries to MGMT only:
grep -n "allow-query" /etc/named.conf
allow-query { localhost; 10.50.1.0/24; };
Only MGMT (10.50.1.0/24) is allowed. Other VLANs are refused.
14.4.4. Fix
Add employee VLANs to allow-query:
sudo sed -i '/allow-query/s|{ localhost; 10.50.1.0/24; }|{ localhost; 10.50.1.0/24; 10.50.10.0/24; 10.50.20.0/24; 10.50.110.0/24; 10.50.120.0/24; }|' /etc/named.conf
Verify the change:
grep "allow-query" /etc/named.conf
allow-query { localhost; 10.50.1.0/24; 10.50.10.0/24; 10.50.20.0/24; 10.50.110.0/24; 10.50.120.0/24; };
Validate and reload:
sudo named-checkconf && sudo systemctl reload named
Test from VLAN 10:
dig @10.50.1.90 google.com +short
14.4.5. VLAN ACL Reference
| VLAN | Subnet | DNS Access? |
|---|---|---|
MGMT (100) |
10.50.1.0/24 |
✓ Yes |
DATA (10) |
10.50.10.0/24 |
✓ Yes (employees) |
VOICE (20) |
10.50.20.0/24 |
✓ Yes (VoIP needs DNS) |
GUEST (30) |
10.50.30.0/24 |
✗ No (use public DNS) |
IOT (40) |
10.50.40.0/24 |
✗ No (isolated) |
SECURITY (110) |
10.50.110.0/24 |
✓ Yes (ISE, Vault) |
SERVICES (120) |
10.50.120.0/24 |
✓ Yes (k8s services) |
14.5. Kerberos "Cannot find KDC" After DNS Migration
|
This is the #1 issue when migrating from Windows DC DNS to BIND. If you see this error, AD SRV records are missing from your zone file. |
14.5.1. Symptoms
-
kinitfails immediately:kinit: Cannot find KDC for realm "INSIDE.DOMUSDIGITALIS.DEV" while getting initial credentials
-
802.1X authentication succeeds (EAP-TLS) but AD group authorization rules don’t match
-
Domain join fails
-
LDAP tools can’t find domain controllers
14.5.2. Root Cause
Windows Domain Controllers automatically register SRV records via dynamic DNS updates. When you migrate to BIND:
-
BIND doesn’t accept dynamic updates from Windows (unless configured with TSIG)
-
You must manually add all AD SRV records to your zone file
-
Common oversight: migrating A/PTR records but forgetting SRV records
14.5.3. Diagnosis
Check if SRV records exist:
# Query Kerberos SRV (should return DC hostname and port)
dig SRV _kerberos._tcp.inside.domusdigitalis.dev +short
0 100 88 home-dc01.inside.domusdigitalis.dev.
(empty - no output)
Or NXDOMAIN:
dig SRV _kerberos._tcp.inside.domusdigitalis.dev
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN
Check LDAP SRV:
dig SRV _ldap._tcp.inside.domusdigitalis.dev +short
14.5.4. Fix
-
SSH to bind-01:
ssh bind-01 -
Edit the zone file:
sudo vi /var/named/inside.domusdigitalis.dev.zone -
Add AD SRV records (before aliases section):
; Active Directory SRV Records (CRITICAL for Kerberos/LDAP) _kerberos._tcp IN SRV 0 100 88 home-dc01 _kerberos._udp IN SRV 0 100 88 home-dc01 _kpasswd._tcp IN SRV 0 100 464 home-dc01 _kpasswd._udp IN SRV 0 100 464 home-dc01 _ldap._tcp IN SRV 0 100 389 home-dc01 _gc._tcp IN SRV 0 100 3268 home-dc01 _ldap._tcp.dc._msdcs IN SRV 0 100 389 home-dc01 _kerberos._tcp.dc._msdcs IN SRV 0 100 88 home-dc01 _ldap._tcp.gc._msdcs IN SRV 0 100 3268 home-dc01 -
Increment the serial number (YYYYMMDDNN format):
2026021501 ; Was 2026021401 -
Validate and reload:
sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zonesudo rndc reload inside.domusdigitalis.dev -
Clear resolver cache (if using pfSense Unbound as upstream):
# On pfSense via SSH or shell pfctl -F all # Flush states # Or via GUI: Services > DNS Resolver > Click "Save" to restart -
Verify fix:
# From workstation dig SRV _kerberos._tcp.inside.domusdigitalis.dev +shortkinit evanusmodestus@INSIDE.DOMUSDIGITALIS.DEVShould now prompt for password instead of "Cannot find KDC".
14.5.5. Prevention
When migrating DNS from Windows to BIND, always:
-
Export all DNS records from Windows DNS Manager (including SRV)
-
Check for
_msdcs,_tcp,_udpsubdomains -
Run
nltest /dclist:DOMAINon a Windows machine to see expected DC records -
Verify SRV records immediately after migration with
dig SRV
14.5.6. Related: VyOS DNS Forwarding Architecture
VyOS forwards all DNS queries to BIND:
Clients query VyOS (10.50.1.1) → VyOS forwards to BIND (10.50.1.90/91) → BIND responds.
Cache issue: VyOS DNS forwarder caches responses. After adding SRV records to BIND, you may need to restart DNS forwarding on VyOS to clear the cache:
ssh vyos-01 "restart dns forwarding"
14.6. Console Blank (No Output)
Rocky 9 cloud images don’t enable serial console by default. Add VNC graphics:
# On kvm-01
sudo virsh destroy bind-01
sudo virt-xml bind-01 --add-device --graphics vnc,listen=0.0.0.0
sudo virsh start bind-01
# Get VNC port
sudo virsh vncdisplay bind-01
Access via Cockpit: kvm-01.inside.domusdigitalis.dev:9090 → Virtual Machines → bind-01 → Console
14.7. Manual Network Setup (If cloud-init Fails)
If cloud-init didn’t configure networking, set it up manually via console.
14.7.1. Step 1: Check Current State
# Check device status
nmcli device status
DEVICE TYPE STATE CONNECTION eth0 ethernet connected System eth0 lo loopback connected (externally) lo
# Check interface name (Rocky 9 cloud images use eth0, NOT enp1s0)
ip link
14.7.2. Step 2: Delete Existing Connection (if wrong config)
# Delete any incorrectly configured connection
sudo nmcli con delete mgmt 2>/dev/null
sudo nmcli connection delete 'Wired connection 1' 2>/dev/null
14.7.3. Step 3: Configure Static IP
sudo nmcli con add type ethernet ifname eth0 con-name mgmt \
ipv4.addresses 10.50.1.90/24 \
ipv4.gateway 10.50.1.1 \
ipv4.dns 10.50.1.1 \
ipv4.method manual
Connection 'mgmt' (uuid-here) successfully added.
14.7.5. Step 5: Verify Configuration
# Show connection details
nmcli con show mgmt | grep -E "ipv4\.(addr|gate|dns)"
ipv4.addresses: {bind-ip}/24
ipv4.gateway: {vyos-vip}
ipv4.dns: {vyos-vip}
# Show active IP (terse format)
nmcli -g IP4 device show eth0
IP4:{bind-ip}/24:{vyos-vip}:dst = {mgmt-network}, nh = 0.0.0.0, mt = 100 | dst = 0.0.0.0/0, nh = {vyos-vip}, mt = 100:{vyos-vip}:::
# Test connectivity
ping -c2 10.50.1.1
14.8. Add YubiKey SSH Keys (Manual)
If SSH keys weren’t added by cloud-init:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
cat >> ~/.ssh/authorized_keys << 'EOF'
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIFHfsGSAFAkqwYj6EGS9sA2MROjs28zM6LJds3gagsCkAAAACHNzaDpkMDAw evanusmodestus@d000-yubikey
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEBZ+kus4aTHzQt1zNnEnGxJs+Lf56vrCdcyvqLhpp9hAAAACHNzaDpkMDAw evanusmodestus@d000-secondary
EOF
chmod 600 ~/.ssh/authorized_keys
Appendix A: DNS Fundamentals for Security Professionals
This appendix provides deep DNS knowledge essential for security operations, incident response, and infrastructure defense.
A.1. DNS Record Types Reference
| Type | Name | Purpose |
|---|---|---|
A |
Address |
Maps hostname to IPv4 address |
AAAA |
IPv6 Address |
Maps hostname to IPv6 address |
CNAME |
Canonical Name |
Alias pointing to another hostname (cannot coexist with other records at same name) |
MX |
Mail Exchange |
Mail server with priority (lower = preferred) |
NS |
Name Server |
Authoritative DNS servers for zone |
PTR |
Pointer |
Reverse lookup (IP → hostname) |
SOA |
Start of Authority |
Zone metadata (serial, refresh, retry, expire, TTL) |
TXT |
Text |
Arbitrary text (SPF, DKIM, DMARC, domain verification) |
SRV |
Service |
Service location with port and priority (_ldap._tcp, _kerberos._udp) |
CAA |
Certificate Authority Authorization |
Which CAs can issue certificates for domain |
TLSA |
TLS Authentication |
DANE certificate pinning |
SSHFP |
SSH Fingerprint |
SSH host key fingerprints for DNSSEC-validated verification |
A.1.1. Security-Relevant Record Types
| Record | Security Use |
|---|---|
TXT (SPF) |
|
TXT (DKIM) |
Public key for email signature verification |
TXT (DMARC) |
|
CAA |
|
TLSA |
Certificate pinning via DANE |
SSHFP |
|
A.2. Zone File Syntax Deep Dive
A.2.1. SOA Record Breakdown
@ IN SOA bind-01.inside.domusdigitalis.dev. hostmaster.inside.domusdigitalis.dev. (
2026021501 ; Serial (YYYYMMDDNN) - MUST increment on every change
3600 ; Refresh (1 hour) - How often slaves check for updates
900 ; Retry (15 min) - Retry interval if refresh fails
604800 ; Expire (7 days) - Slave stops answering if no master contact
86400 ; Minimum TTL (1 day) - Negative caching TTL (NXDOMAIN)
)
|
Serial number is critical. If you edit a zone and forget to increment the serial, slaves won’t pick up changes. Use |
A.2.2. Record Syntax Patterns
; Absolute name (ends with dot - FQDN)
bind-01.inside.domusdigitalis.dev. IN A 10.50.1.90
; Relative name (no dot - zone suffix appended automatically)
bind-01 IN A 10.50.1.90
; Becomes: bind-01.inside.domusdigitalis.dev.
; @ symbol = zone origin (the zone name itself)
@ IN NS bind-01.inside.domusdigitalis.dev.
; Wildcards
*.lab IN A 10.50.2.100
; Matches: anything.lab.inside.domusdigitalis.dev
A.2.3. Common Mistakes
| Mistake | Consequence |
|---|---|
Missing trailing dot on FQDN |
|
CNAME at zone apex |
Breaks NS/MX records; use ALIAS/ANAME if needed |
Duplicate records |
Causes round-robin (may be intentional for load balancing) |
Forgot to increment serial |
Slaves don’t update; inconsistent responses |
A.3. DNS Query Anatomy
A.3.2. Query Flags Explained
dig ise-01.inside.domusdigitalis.dev
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
| Flag | Meaning |
|---|---|
QR |
Query Response (1 = response, 0 = query) |
AA |
Authoritative Answer (server is authoritative for zone) |
RD |
Recursion Desired (client wants recursive resolution) |
RA |
Recursion Available (server supports recursion) |
AD |
Authenticated Data (DNSSEC validated) |
CD |
Checking Disabled (DNSSEC validation skipped) |
A.4. Essential dig Commands
A.4.1. Basic Queries
# Simple A record lookup
dig host.domain.com
# Specific record type
dig MX domain.com
dig TXT domain.com
dig ANY domain.com # All records (often blocked)
# Short output (just the answer)
dig +short host.domain.com
# Reverse lookup
dig -x 10.50.1.50
A.4.2. Targeting Specific Servers
# Query specific DNS server
dig @10.50.1.90 host.domain.com
# Query authoritative server directly (bypass cache)
dig @bind-01.inside.domusdigitalis.dev host.inside.domusdigitalis.dev
# Compare responses from different servers
dig @10.50.1.1 host.domain.com +short # VyOS forwarder (forwards to BIND)
dig @10.50.1.90 host.domain.com +short # BIND directly
dig @8.8.8.8 host.domain.com +short # Google (external view)
A.4.3. Tracing Resolution Path
# Full trace from root servers
dig +trace google.com
# Trace with DNSSEC info
dig +trace +dnssec google.com
A.4.4. Security-Focused Queries
# Check DNSSEC status
dig +dnssec domain.com
# Query for DNSKEY (DNSSEC public key)
dig DNSKEY domain.com
# Check CAA records (certificate authority restrictions)
dig CAA domain.com
# Check SPF record
dig TXT domain.com | grep spf
# Check DMARC policy
dig TXT _dmarc.domain.com
# Check DKIM selector
dig TXT selector._domainkey.domain.com
# Check SSH fingerprints (if published)
dig SSHFP host.domain.com
A.4.5. Debugging and Forensics
# Full verbose output with timing
dig +stats +answer host.domain.com
# Show query time and server
dig host.domain.com | grep -E "Query time|SERVER"
# TCP query (useful when UDP blocked or large responses)
dig +tcp host.domain.com
# Specify source port (firewall testing)
dig -b 0.0.0.0#54321 host.domain.com
# EDNS client subnet (geolocation testing)
dig +subnet=203.0.113.0/24 cdn.domain.com
A.5. DNS Attack Vectors and Defenses
A.5.1. Attack: DNS Cache Poisoning
Attacker injects false records into resolver cache.
| Defense | Implementation |
|---|---|
Source port randomization |
Modern resolvers do this automatically |
DNSSEC validation |
|
Query ID randomization |
Enabled by default in BIND 9 |
0x20 encoding |
Case randomization (Unbound supports this) |
A.5.2. Attack: DNS Amplification (DDoS)
Attacker spoofs victim’s IP, sends queries to open resolvers; responses flood victim.
| Defense | Implementation |
|---|---|
Rate limiting |
|
Disable open recursion |
|
Response Rate Limiting (RRL) |
Built into BIND 9.9+ |
BCP38 (source address validation) |
Network-level anti-spoofing |
A.5.3. Attack: DNS Tunneling / Exfiltration
Data exfiltration via DNS queries (e.g., base64data.evil.com).
| Detection | Method |
|---|---|
Query length anomalies |
Normal hostnames < 30 chars; tunneling often > 100 |
High query volume |
Unusual QPS to single domain |
Entropy analysis |
Tunneled data has high entropy |
TXT record abuse |
Large TXT responses to suspicious domains |
# Monitor for suspicious query patterns
sudo journalctl -u named -f | grep -E "query.*\.(tk|ml|ga|cf|gq)"
# Check for unusually long queries in BIND query log
awk 'length($0) > 200' /var/log/named/queries.log
A.5.4. Attack: Zone Transfer Exploitation
Attacker retrieves entire zone contents via AXFR.
| Defense | Implementation |
|---|---|
Restrict transfers |
|
TSIG authentication |
Shared secret for zone transfers |
Disable for public zones |
|
# Test if zone transfer is allowed (should fail)
dig @bind-01.inside.domusdigitalis.dev inside.domusdigitalis.dev AXFR
; Transfer failed.
A.5.5. Attack: Subdomain Takeover
Dangling CNAME pointing to decommissioned service (S3, Azure, Heroku).
| Defense | Method |
|---|---|
Audit CNAME records |
Regular checks for orphaned CNAMEs |
Remove before decommission |
Delete DNS records BEFORE releasing cloud resources |
Monitor for changes |
Alert on CNAME additions |
# Find all CNAME records in zone
dig @bind-01 inside.domusdigitalis.dev AXFR | grep CNAME
A.6. BIND Security Hardening Checklist
| Control | Configuration | Status |
|---|---|---|
Hide version |
|
[ ] |
Restrict recursion |
|
[ ] |
Disable zone transfers |
|
[ ] |
Enable rate limiting |
|
[ ] |
DNSSEC validation |
|
[ ] |
Disable unnecessary features |
|
[ ] |
Chroot BIND |
|
[ ] |
Minimal zone exposure |
Don’t publish internal hosts externally |
[ ] |
Log queries |
|
[ ] |
Log security events |
|
[ ] |
A.7. Logging and Monitoring
A.7.1. Enable Query Logging
logging {
channel queries_log {
file "/var/log/named/queries.log" versions 5 size 50M;
severity info;
print-time yes;
print-category yes;
};
channel security_log {
file "/var/log/named/security.log" versions 3 size 10M;
severity dynamic;
print-time yes;
print-severity yes;
};
category queries { queries_log; };
category security { security_log; };
category dnssec { security_log; };
};
A.7.2. Create Log Directory
sudo mkdir -p /var/log/named
sudo chown named:named /var/log/named
sudo chmod 750 /var/log/named
A.7.3. Toggle Query Logging
# Enable query logging dynamically
sudo rndc querylog on
# Disable (reduces disk I/O in production)
sudo rndc querylog off
# Check current status
sudo rndc status | grep "query logging"
A.7.4. Log Analysis
# Top queried domains
awk '{print $4}' /var/log/named/queries.log | sort | uniq -c | sort -rn | head -20
# Queries by client IP
awk '{print $6}' /var/log/named/queries.log | sort | uniq -c | sort -rn | head -20
# Failed queries (NXDOMAIN)
grep "NXDOMAIN" /var/log/named/queries.log | awk '{print $4}' | sort | uniq -c | sort -rn
# Potential tunneling (long queries)
awk 'length($4) > 50 {print $4}' /var/log/named/queries.log | sort | uniq -c
A.8. DNSSEC Overview
DNSSEC provides authentication and integrity for DNS responses through cryptographic signatures.
A.8.1. DNSSEC Record Types
| Record | Purpose |
|---|---|
DNSKEY |
Public key for zone signing |
RRSIG |
Signature over a record set |
DS |
Delegation Signer (hash of child DNSKEY in parent zone) |
NSEC/NSEC3 |
Authenticated denial of existence |
A.9. DNS over TLS/HTTPS (DoT/DoH)
Modern encrypted DNS protocols prevent eavesdropping on queries.
| Protocol | Port | Use Case |
|---|---|---|
DNS (traditional) |
53/UDP, 53/TCP |
Internal networks, LANs |
DoT (DNS over TLS) |
853/TCP |
Encrypted to trusted resolver |
DoH (DNS over HTTPS) |
443/TCP |
Encrypted, bypasses network monitoring |
A.10. SRV Records (Service Discovery)
SRV records are critical for Active Directory, Kerberos, LDAP, SIP, and other service discovery.
A.10.1. SRV Record Format
_service._protocol.name. TTL IN SRV priority weight port target.
| Field | Description |
|---|---|
_service |
Service name (e.g., _ldap, _kerberos, _sip) |
_protocol |
_tcp or _udp |
priority |
Lower = preferred (like MX) |
weight |
Load balancing among same priority (higher = more traffic) |
port |
Service port number |
target |
FQDN of server providing service |
A.10.2. Active Directory SRV Records
; Kerberos
_kerberos._tcp.inside.domusdigitalis.dev. IN SRV 0 100 88 home-dc01.inside.domusdigitalis.dev.
_kerberos._udp.inside.domusdigitalis.dev. IN SRV 0 100 88 home-dc01.inside.domusdigitalis.dev.
_kpasswd._tcp.inside.domusdigitalis.dev. IN SRV 0 100 464 home-dc01.inside.domusdigitalis.dev.
_kpasswd._udp.inside.domusdigitalis.dev. IN SRV 0 100 464 home-dc01.inside.domusdigitalis.dev.
; LDAP
_ldap._tcp.inside.domusdigitalis.dev. IN SRV 0 100 389 home-dc01.inside.domusdigitalis.dev.
_ldap._tcp.dc._msdcs.inside.domusdigitalis.dev. IN SRV 0 100 389 home-dc01.inside.domusdigitalis.dev.
; Global Catalog
_gc._tcp.inside.domusdigitalis.dev. IN SRV 0 100 3268 home-dc01.inside.domusdigitalis.dev.
_ldap._tcp.gc._msdcs.inside.domusdigitalis.dev. IN SRV 0 100 3268 home-dc01.inside.domusdigitalis.dev.
A.10.3. FreeIPA SRV Records
; Kerberos (FreeIPA)
_kerberos._tcp.inside.domusdigitalis.dev. IN SRV 0 100 88 ipa-01.inside.domusdigitalis.dev.
_kerberos._udp.inside.domusdigitalis.dev. IN SRV 0 100 88 ipa-01.inside.domusdigitalis.dev.
_kerberos-master._tcp.inside.domusdigitalis.dev. IN SRV 0 100 88 ipa-01.inside.domusdigitalis.dev.
_kpasswd._tcp.inside.domusdigitalis.dev. IN SRV 0 100 464 ipa-01.inside.domusdigitalis.dev.
; LDAP (FreeIPA)
_ldap._tcp.inside.domusdigitalis.dev. IN SRV 0 100 389 ipa-01.inside.domusdigitalis.dev.
; NTP (FreeIPA provides time sync)
_ntp._udp.inside.domusdigitalis.dev. IN SRV 0 100 123 ipa-01.inside.domusdigitalis.dev.
A.10.4. Other Common SRV Records
; SIP (VoIP)
_sip._tcp.domain.com. IN SRV 10 60 5060 sip-server.domain.com.
_sip._udp.domain.com. IN SRV 10 60 5060 sip-server.domain.com.
_sips._tcp.domain.com. IN SRV 10 60 5061 sip-server.domain.com.
; XMPP (Jabber/Chat)
_xmpp-client._tcp.domain.com. IN SRV 5 0 5222 xmpp.domain.com.
_xmpp-server._tcp.domain.com. IN SRV 5 0 5269 xmpp.domain.com.
; CalDAV / CardDAV (Calendar/Contacts)
_caldavs._tcp.domain.com. IN SRV 0 0 443 calendar.domain.com.
_carddavs._tcp.domain.com. IN SRV 0 0 443 contacts.domain.com.
; IMAP / Submission (Email)
_imaps._tcp.domain.com. IN SRV 0 0 993 mail.domain.com.
_submission._tcp.domain.com. IN SRV 0 0 587 mail.domain.com.
A.11. Dynamic DNS with TSIG
TSIG (Transaction Signature) enables secure dynamic DNS updates from DHCP servers or clients.
A.11.1. Generate TSIG Key
# Generate HMAC-SHA256 key
tsig-keygen -a hmac-sha256 dhcp-update > /etc/named/dhcp-update.key
key "dhcp-update" {
algorithm hmac-sha256;
secret "Base64EncodedSecretHere==";
};
A.11.2. Configure BIND for Dynamic Updates
// Include TSIG key
include "/etc/named/dhcp-update.key";
zone "inside.domusdigitalis.dev" IN {
type master;
file "/var/named/dynamic/inside.domusdigitalis.dev.zone";
allow-update { key dhcp-update; }; // Only TSIG-signed updates
};
zone "1.50.10.in-addr.arpa" IN {
type master;
file "/var/named/dynamic/1.50.10.in-addr.arpa.zone";
allow-update { key dhcp-update; };
};
A.11.3. Configure DHCP Server (ISC DHCP)
# /etc/dhcp/dhcpd.conf
key dhcp-update {
algorithm hmac-sha256;
secret "Base64EncodedSecretHere==";
};
zone inside.domusdigitalis.dev. {
primary 10.50.1.90;
key dhcp-update;
}
zone 1.50.10.in-addr.arpa. {
primary 10.50.1.90;
key dhcp-update;
}
A.11.4. Manual Dynamic Update (nsupdate)
# Create update file
cat > /tmp/dns-update.txt << 'EOF'
server 10.50.1.90
zone inside.domusdigitalis.dev
update add newhost.inside.domusdigitalis.dev. 3600 A 10.50.1.200
send
EOF
# Execute with TSIG key
nsupdate -k /etc/named/dhcp-update.key /tmp/dns-update.txt
A.12. Split-Horizon DNS (Views)
Views allow different responses based on client source IP - essential for internal vs external DNS.
A.12.1. View Configuration
// Define ACLs first
acl "internal" {
10.50.0.0/16;
192.168.0.0/16;
127.0.0.1;
};
acl "external" {
any;
};
// Internal view (full access)
view "internal" {
match-clients { internal; };
recursion yes;
zone "inside.domusdigitalis.dev" IN {
type master;
file "/var/named/internal/inside.domusdigitalis.dev.zone";
};
zone "domusdigitalis.dev" IN {
type master;
file "/var/named/internal/domusdigitalis.dev.zone";
};
};
// External view (limited, no internal hosts)
view "external" {
match-clients { external; };
recursion no; // Never recurse for external clients
zone "domusdigitalis.dev" IN {
type master;
file "/var/named/external/domusdigitalis.dev.zone";
// Different zone file with only public records
};
};
A.13. Response Policy Zones (RPZ)
RPZ is a DNS firewall - block malicious domains, redirect, or return NXDOMAIN.
A.13.1. Enable RPZ in BIND
options {
// ... other options ...
response-policy {
zone "rpz.local" policy given; // Use zone-defined actions
zone "rpz.blocklist" policy nxdomain; // All matches return NXDOMAIN
};
};
zone "rpz.local" {
type master;
file "/var/named/rpz/rpz.local.zone";
allow-query { none; }; // RPZ zones shouldn't be queried directly
};
A.13.2. RPZ Zone File Examples
; /var/named/rpz/rpz.local.zone
$TTL 3600
@ IN SOA localhost. hostmaster.localhost. (
2026021501 3600 900 604800 86400 )
IN NS localhost.
; Block entire domain (returns NXDOMAIN)
malware.com IN CNAME .
; Block specific host
evil.badsite.com IN CNAME .
; Redirect to sinkhole (for logging/analysis)
phishing.com IN A 10.50.1.99
; Block by IP (any domain resolving to this IP)
32.1.2.3.rpz-ip IN CNAME .
; Wildcard block (all subdomains)
*.malware-domain.com IN CNAME .
; Passthru (whitelist - skip RPZ for this domain)
legitimate.com IN CNAME rpz-passthru.
A.14. ACL Definitions
Named ACLs make policies readable and maintainable.
A.14.1. Common ACL Patterns
// Localhost
acl "localhost" {
127.0.0.1;
::1;
};
// Internal networks
acl "internal" {
10.50.0.0/16; // Management VLAN
192.168.0.0/16; // User VLANs
172.16.0.0/12; // Lab networks
};
// DNS slaves (for zone transfers)
acl "slaves" {
10.50.1.91; // bind-02
};
// Trusted resolvers (can recurse)
acl "trusted" {
localhost;
internal;
};
// Management hosts (can query version, stats)
acl "management" {
10.50.1.10; // Monitoring server
10.50.1.5; // Admin workstation
};
A.15. Practical Record Addition Examples
Copy-paste examples for adding records to your zone file.
A.15.1. Add A Record
# Edit zone file
sudo vi /var/named/inside.domusdigitalis.dev.zone
# Add line (remember to increment serial!)
newserver IN A 10.50.1.150
# Reload zone
sudo rndc reload inside.domusdigitalis.dev
# Verify
dig @localhost newserver.inside.domusdigitalis.dev +short
A.15.2. Add CNAME (Alias)
# In zone file (increment serial first!)
wiki IN CNAME docs-01.inside.domusdigitalis.dev.
# Reload and verify
sudo rndc reload inside.domusdigitalis.dev
dig @localhost wiki.inside.domusdigitalis.dev +short
A.15.3. Add MX Record
# In zone file
@ IN MX 10 mail-01.inside.domusdigitalis.dev.
@ IN MX 20 mail-02.inside.domusdigitalis.dev.
# Verify
dig @localhost inside.domusdigitalis.dev MX +short
A.15.4. Add TXT Record (SPF)
# In zone file
@ IN TXT "v=spf1 mx ip4:10.50.1.0/24 -all"
# Verify
dig @localhost inside.domusdigitalis.dev TXT +short
A.15.5. Add SRV Record
# In zone file (LDAP service)
_ldap._tcp IN SRV 0 100 389 ipa-01.inside.domusdigitalis.dev.
# Verify
dig @localhost _ldap._tcp.inside.domusdigitalis.dev SRV +short
A.15.6. Add PTR Record (Reverse)
# Edit reverse zone
sudo vi /var/named/10.50.1.rev
# Add line (increment serial!)
150 IN PTR newserver.inside.domusdigitalis.dev.
# Reload and verify
sudo rndc reload 1.50.10.in-addr.arpa
dig @localhost -x 10.50.1.150 +short
A.15.7. Add CAA Record
# In zone file - only Let's Encrypt can issue certs
@ IN CAA 0 issue "letsencrypt.org"
@ IN CAA 0 issuewild "letsencrypt.org"
@ IN CAA 0 iodef "mailto:security@domusdigitalis.dev"
# Verify
dig @localhost inside.domusdigitalis.dev CAA +short
A.15.8. Add SSHFP Record
# Generate SSHFP records from host keys
ssh-keygen -r newserver.inside.domusdigitalis.dev
# Example output to add to zone:
newserver IN SSHFP 1 1 abc123...
newserver IN SSHFP 1 2 def456...
newserver IN SSHFP 4 1 ghi789...
newserver IN SSHFP 4 2 jkl012...
# Client verification (requires DNSSEC or trusted resolver)
ssh -o VerifyHostKeyDNS=yes newserver.inside.domusdigitalis.dev
A.16. Quick Reference: Zone File Templates
A.16.1. Forward Zone Template
$TTL 3600
@ IN SOA ns1.domain.com. hostmaster.domain.com. (
2026021501 3600 900 604800 86400 )
IN NS ns1.domain.com.
IN NS ns2.domain.com.
IN MX 10 mail.domain.com.
IN TXT "v=spf1 mx -all"
ns1 IN A 10.0.0.1
ns2 IN A 10.0.0.2
mail IN A 10.0.0.10
www IN CNAME web.domain.com.
web IN A 10.0.0.20
A.17. Incident Response: DNS Indicators
A.17.1. Signs of Compromise
| Indicator | Investigation |
|---|---|
Unexpected NS records |
|
TTL anomalies |
Very low TTL (< 60s) may indicate fast-flux |
New/unknown resolvers |
Check |
NXDOMAIN for valid hosts |
Cache poisoning or hijacking |
High query rate to single domain |
C2 beaconing or tunneling |
A.17.2. Rapid Response Commands
# Dump current cache (what has BIND resolved recently)
sudo rndc dumpdb -cache
sudo cat /var/named/data/cache_dump.db
# Flush entire cache
sudo rndc flush
# Flush specific domain
sudo rndc flushname compromised.domain.com
# Reload zone without restarting
sudo rndc reload inside.domusdigitalis.dev
# Stop all queries (emergency)
sudo systemctl stop named
A.18. Performance Tuning
A.18.1. Key Metrics
# Query statistics
sudo rndc stats
cat /var/named/data/named_stats.txt
# Current connections
sudo ss -tunlp | grep named
# Memory usage
sudo rndc status | grep -E "heap|zone"
A.18.2. Tuning Parameters
options {
// Worker threads (default: auto based on CPU)
// recursive-clients 1000; // Max concurrent recursive queries
// tcp-clients 150; // Max concurrent TCP connections
// Cache tuning
// max-cache-size 256M; // Limit cache memory
// max-cache-ttl 86400; // Max TTL for cached records
// max-ncache-ttl 3600; // Max TTL for negative cache
// Rate limiting (DDoS mitigation)
rate-limit {
responses-per-second 10;
window 5;
};
};
A.19. Further Reading
A.19.1. Official Documentation
-
Unbound Documentation (alternative resolver)
A.19.3. RFCs - Core DNS
| RFC | Description |
|---|---|
Domain Names - Concepts and Facilities |
|
Domain Names - Implementation and Specification |
|
Clarifications to the DNS Specification |
|
Negative Caching of DNS Queries (NXDOMAIN) |
|
DNS Extensions for IPv6 (AAAA records) |
A.19.4. RFCs - Record Types
| RFC | Description |
|---|---|
SRV Records (Service Location) |
|
SPF Records (Sender Policy Framework) |
|
DKIM Signatures |
|
SPF (Updated) |
|
DMARC (Domain-based Message Authentication) |
|
CAA Records (Certificate Authority Authorization) |
|
TLSA Records (DANE) |
|
SSHFP Records (SSH Fingerprints) |
A.19.5. RFCs - DNSSEC
| RFC | Description |
|---|---|
DNSSEC Introduction and Requirements |
|
Resource Records for DNSSEC |
|
Protocol Modifications for DNSSEC |
|
NSEC3 (Hashed Authenticated Denial of Existence) |
|
DNSSEC Operational Practices |
|
Automating DNSSEC Delegation Trust Maintenance |
A.19.6. RFCs - Security & Operations
| RFC | Description |
|---|---|
TSIG (Transaction Signatures for DNS) |
|
AXFR (Zone Transfer) |
|
NOTIFY (Zone Change Notification) |
|
DNS Transport over TCP |
|
Query Name Minimization |
|
NXDOMAIN Cut (Aggressive Negative Caching) |
A.19.7. RFCs - Encrypted DNS
| RFC | Description |
|---|---|
DNS over TLS (DoT) |
|
DNS over HTTPS (DoH) |
|
DNS over QUIC (DoQ) |
|
Usage Profiles for DNS over TLS/DTLS |
A.19.8. RFCs - Response Policy Zones (RPZ)
| RFC | Description |
|---|---|
DNS Response Policy Zones (Internet Draft) |
|
Community RPZ Documentation |
A.19.9. Books & Training
-
DNS and BIND, 5th Edition - O’Reilly (Cricket Liu)
-
Pro DNS and BIND 10 - Ron Aitchison
-
SANS SEC503 - Intrusion Detection (includes DNS analysis)
-
SANS SEC560 - Network Penetration Testing
A.19.10. Threat Intelligence Feeds (RPZ Sources)
-
Steven Black’s Hosts - Unified malware/ad blocklist
-
URLhaus - Malware URL blocklist
-
PhishTank - Phishing URL database
-
Spamhaus - Commercial threat feeds
-
SURBL - URI reputation data