BIND Operations Quick Reference
Add DNS Records
This section covers adding A records to BIND zone file. Use for new infrastructure deployments.
Workflow Summary
| Step | Action |
|---|---|
1 |
Backup zone file |
2 |
Add record(s) |
3 |
Increment serial (YYYYMMDDNN) |
4 |
Validate zone |
5 |
Reload zone |
6 |
Verify resolution |
Step 1: Backup + View Current Zone
ssh bind-01 "sudo cp /var/named/inside.domusdigitalis.dev.zone /var/named/inside.domusdigitalis.dev.zone.bak.$(date +%Y%m%d%H%M)"
ssh bind-01 "sudo tail -50 /var/named/inside.domusdigitalis.dev.zone"
Step 2: Add A Record
Template: hostname IN A ip-address
# Template: hostname IN A ip-address
ssh bind-01 "echo 'vyos-02 IN A 10.50.1.3' | sudo tee -a /var/named/inside.domusdigitalis.dev.zone"
Step 3: Increment Serial
CRITICAL: Serial must increment or changes won’t propagate.
Check current:
ssh bind-01 "grep -E '^[[:space:]]+[0-9]{10}' /var/named/inside.domusdigitalis.dev.zone"
Edit manually (recommended):
ssh bind-01 "sudo vi /var/named/inside.domusdigitalis.dev.zone"
Or calculate + sed:
CURRENT=$(ssh bind-01 "grep -oP '[0-9]{10}' /var/named/inside.domusdigitalis.dev.zone | head -1")
NEW=$((CURRENT + 1))
echo "Current: $CURRENT → New: $NEW"
ssh bind-01 "sudo sed -i 's/$CURRENT/$NEW/' /var/named/inside.domusdigitalis.dev.zone"
Bulk Add: VyOS HA
ssh bind-01 << 'EOF'
echo 'vyos-01 IN A 10.50.1.2' | sudo tee -a /var/named/inside.domusdigitalis.dev.zone
echo 'vyos-02 IN A 10.50.1.3' | sudo tee -a /var/named/inside.domusdigitalis.dev.zone
echo 'vyos IN A 10.50.1.1' | sudo tee -a /var/named/inside.domusdigitalis.dev.zone
EOF
Then: increment serial → validate → reload → verify.
Bulk Add: All Infrastructure
ssh bind-01 << 'EOF'
sudo tee -a /var/named/inside.domusdigitalis.dev.zone << 'RECORDS'
; === VyOS Firewall HA ===
vyos-01 IN A 10.50.1.2
vyos-02 IN A 10.50.1.3
vyos IN A 10.50.1.1
; === Hypervisors ===
kvm-01 IN A 10.50.1.110
kvm-02 IN A 10.50.1.111
ipmi-01 IN A 10.50.1.200
ipmi-02 IN A 10.50.1.201
; === Identity ===
home-dc01 IN A 10.50.1.50
home-dc02 IN A 10.50.1.51
keycloak-01 IN A 10.50.1.80
keycloak-02 IN A 10.50.1.81
ipa-01 IN A 10.50.1.100
ipa-02 IN A 10.50.1.101
; === Security ===
vault-01 IN A 10.50.1.60
vault-02 IN A 10.50.1.61
vault-03 IN A 10.50.1.62
ise-01 IN A 10.50.1.20
ise-02 IN A 10.50.1.21
ipsk-mgr-01 IN A 10.50.1.30
ipsk-mgr-02 IN A 10.50.1.31
ipsk-mgr IN A 10.50.1.32
; === DNS ===
bind-01 IN A 10.50.1.90
bind-02 IN A 10.50.1.91
; === Network ===
9800-wlc-01 IN A 10.50.1.40
9800-wlc-02 IN A 10.50.1.41
3560-cx IN A 10.50.1.10
c9300-01 IN A 10.50.1.11
; === Storage ===
nas-01 IN A 10.50.1.70
nas-02 IN A 10.50.1.71
gitea-01 IN A 10.50.1.72
minio-01 IN A 10.50.1.73
; === k3s Cluster ===
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
k3s-worker-01 IN A 10.50.1.123
k3s-worker-02 IN A 10.50.1.124
k3s-worker-03 IN A 10.50.1.125
; === Monitoring VIPs (MetalLB/Traefik) ===
traefik IN A 10.50.1.130
wazuh-indexer IN A 10.50.1.131
wazuh-dashboard IN A 10.50.1.132
wazuh-workers IN A 10.50.1.133
wazuh-manager IN A 10.50.1.134
zabbix-01 IN A 10.50.1.135
; === CNAMEs for convenience ===
dc IN CNAME home-dc01
vault IN CNAME vault-01
ise IN CNAME ise-01
wlc IN CNAME 9800-wlc-01
nas IN CNAME nas-01
wazuh IN CNAME wazuh-manager
prometheus IN CNAME traefik
grafana IN CNAME traefik
alertmanager IN CNAME traefik
RECORDS
EOF
After bulk add:
ssh bind-01 "sudo vi /var/named/inside.domusdigitalis.dev.zone"
ssh bind-01 "sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zone"
ssh bind-01 "sudo rndc reload inside.domusdigitalis.dev"
for h in vyos-01 vyos-02 kvm-01 kvm-02 vault-01 ise-01; do
echo -n "$h: "
dig @10.50.1.90 $h.inside.domusdigitalis.dev +short
done
Reverse DNS (PTR)
Zone file: /var/named/10.50.1.rev
Single PTR
ssh bind-01 "echo '2 IN PTR vyos-02.inside.domusdigitalis.dev.' | sudo tee -a /var/named/10.50.1.rev"
ssh bind-01 "sudo named-checkzone 1.50.10.in-addr.arpa /var/named/10.50.1.rev"
ssh bind-01 "sudo rndc reload 1.50.10.in-addr.arpa"
dig @10.50.1.90 -x 10.50.1.2 +short
Bulk PTR Records
ssh bind-01 << 'EOF'
sudo tee -a /var/named/10.50.1.rev << 'PTR'
; === VyOS ===
1 IN PTR vyos.inside.domusdigitalis.dev.
2 IN PTR vyos-01.inside.domusdigitalis.dev.
3 IN PTR vyos-02.inside.domusdigitalis.dev.
; === Switches ===
10 IN PTR 3560-cx.inside.domusdigitalis.dev.
11 IN PTR c9300-01.inside.domusdigitalis.dev.
; === ISE ===
20 IN PTR ise-01.inside.domusdigitalis.dev.
21 IN PTR ise-02.inside.domusdigitalis.dev.
; === iPSK HA ===
30 IN PTR ipsk-mgr-01.inside.domusdigitalis.dev.
31 IN PTR ipsk-mgr-02.inside.domusdigitalis.dev.
32 IN PTR ipsk-mgr.inside.domusdigitalis.dev.
; === WLC HA ===
40 IN PTR 9800-wlc-01.inside.domusdigitalis.dev.
41 IN PTR 9800-wlc-02.inside.domusdigitalis.dev.
; === Identity ===
50 IN PTR home-dc01.inside.domusdigitalis.dev.
51 IN PTR home-dc02.inside.domusdigitalis.dev.
; === Vault ===
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 HA ===
70 IN PTR nas-01.inside.domusdigitalis.dev.
71 IN PTR nas-02.inside.domusdigitalis.dev.
72 IN PTR gitea-01.inside.domusdigitalis.dev.
73 IN PTR minio-01.inside.domusdigitalis.dev.
; === Keycloak HA ===
80 IN PTR keycloak-01.inside.domusdigitalis.dev.
81 IN PTR keycloak-02.inside.domusdigitalis.dev.
; === DNS ===
90 IN PTR bind-01.inside.domusdigitalis.dev.
91 IN PTR bind-02.inside.domusdigitalis.dev.
; === FreeIPA ===
100 IN PTR ipa-01.inside.domusdigitalis.dev.
101 IN PTR ipa-02.inside.domusdigitalis.dev.
; === Hypervisors ===
110 IN PTR kvm-01.inside.domusdigitalis.dev.
111 IN PTR kvm-02.inside.domusdigitalis.dev.
; === k3s ===
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.
123 IN PTR k3s-worker-01.inside.domusdigitalis.dev.
124 IN PTR k3s-worker-02.inside.domusdigitalis.dev.
125 IN PTR k3s-worker-03.inside.domusdigitalis.dev.
; === Monitoring VIPs ===
130 IN PTR traefik.inside.domusdigitalis.dev.
131 IN PTR wazuh-indexer.inside.domusdigitalis.dev.
132 IN PTR wazuh-dashboard.inside.domusdigitalis.dev.
133 IN PTR wazuh-workers.inside.domusdigitalis.dev.
134 IN PTR wazuh-manager.inside.domusdigitalis.dev.
135 IN PTR zabbix-01.inside.domusdigitalis.dev.
; === IPMI ===
200 IN PTR ipmi-01.inside.domusdigitalis.dev.
201 IN PTR ipmi-02.inside.domusdigitalis.dev.
PTR
EOF
Then: increment serial → validate → reload.
Validation
Pre-Deployment Checks
Run before making changes:
echo "=== PRE-DEPLOYMENT: BIND Connectivity ==="
ssh bind-01 "hostname && uptime"
echo "=== PRE-DEPLOYMENT: Current Zone Status ==="
ssh bind-01 "sudo rndc zonestatus inside.domusdigitalis.dev | grep -E 'serial|expires'"
echo "=== PRE-DEPLOYMENT: Current Record Count ==="
ssh bind-01 "sudo grep -cE '^[a-z]' /var/named/inside.domusdigitalis.dev.zone"
Post-Deployment Validation
After reload, validate ALL records:
echo "=== POST-DEPLOYMENT: Validating ALL Records ==="
DOMAIN="inside.domusdigitalis.dev"
DNS="10.50.1.90"
FAILED=0
# All infrastructure hosts
HOSTS=(
"vyos-01:10.50.1.2"
"vyos-02:10.50.1.3"
"vyos:10.50.1.1"
"kvm-01:10.50.1.110"
"kvm-02:10.50.1.111"
"vault-01:10.50.1.60"
"vault-02:10.50.1.61"
"vault-03:10.50.1.62"
"ise-01:10.50.1.20"
"ise-02:10.50.1.21"
"home-dc01:10.50.1.50"
"home-dc02:10.50.1.51"
"keycloak-01:10.50.1.80"
"keycloak-02:10.50.1.81"
"ipa-01:10.50.1.100"
"ipa-02:10.50.1.101"
"bind-01:10.50.1.90"
"bind-02:10.50.1.91"
"9800-wlc-01:10.50.1.40"
"9800-wlc-02:10.50.1.41"
"nas-01:10.50.1.70"
"nas-02:10.50.1.71"
"gitea-01:10.50.1.72"
"minio-01:10.50.1.73"
"ipsk-mgr-01:10.50.1.30"
"ipsk-mgr-02:10.50.1.31"
"ipsk-mgr:10.50.1.32"
"3560-cx:10.50.1.10"
"c9300-01:10.50.1.11"
"ipmi-01:10.50.1.200"
"ipmi-02:10.50.1.201"
"k3s-master-01:10.50.1.120"
"k3s-master-02:10.50.1.121"
"k3s-master-03:10.50.1.122"
"k3s-worker-01:10.50.1.123"
"k3s-worker-02:10.50.1.124"
"k3s-worker-03:10.50.1.125"
"traefik:10.50.1.130"
"wazuh-indexer:10.50.1.131"
"wazuh-dashboard:10.50.1.132"
"wazuh-workers:10.50.1.133"
"wazuh-manager:10.50.1.134"
"zabbix-01:10.50.1.135"
)
for entry in "${HOSTS[@]}"; do
HOST="${entry%%:*}"
EXPECTED="${entry##*:}"
RESULT=$(dig @$DNS $HOST.$DOMAIN +short)
if [[ "$RESULT" == "$EXPECTED" ]]; then
echo "✓ $HOST → $RESULT"
else
echo "✗ $HOST: expected $EXPECTED, got $RESULT"
((FAILED++))
fi
done
echo ""
echo "=== SUMMARY ==="
echo "Total: ${#HOSTS[@]} records"
echo "Failed: $FAILED"
[[ $FAILED -eq 0 ]] && echo "STATUS: ALL RECORDS VALID" || echo "STATUS: VALIDATION FAILED"
✓ vyos-01 → 10.50.1.2 ✓ vyos-02 → 10.50.1.3 ... === SUMMARY === Total: 43 records Failed: 0 STATUS: ALL RECORDS VALID
Validate CNAMEs:
echo "=== POST-DEPLOYMENT: Validating CNAMEs ==="
DOMAIN="inside.domusdigitalis.dev"
DNS="10.50.1.90"
CNAMES=(
"dc:home-dc01"
"vault:vault-01"
"ise:ise-01"
"wlc:9800-wlc-01"
"nas:nas-01"
"wazuh:wazuh-manager"
)
for entry in "${CNAMES[@]}"; do
ALIAS="${entry%%:*}"
TARGET="${entry##*:}"
RESULT=$(dig @$DNS $ALIAS.$DOMAIN CNAME +short)
if [[ "$RESULT" == "$TARGET.$DOMAIN." ]]; then
echo "✓ $ALIAS → $TARGET"
else
echo "✗ $ALIAS: expected $TARGET.$DOMAIN., got $RESULT"
fi
done
Validate PTR records (sample):
echo "=== POST-DEPLOYMENT: Validating PTR Records (sample) ==="
DNS="10.50.1.90"
PTRS=(
"10.50.1.1:vyos"
"10.50.1.20:ise-01"
"10.50.1.60:vault-01"
"10.50.1.110:kvm-01"
"10.50.1.120:k3s-master-01"
)
for entry in "${PTRS[@]}"; do
IP="${entry%%:*}"
EXPECTED="${entry##*:}"
RESULT=$(dig @$DNS -x $IP +short)
if [[ "$RESULT" == *"$EXPECTED"* ]]; then
echo "✓ $IP → $RESULT"
else
echo "✗ $IP: expected *$EXPECTED*, got $RESULT"
fi
done
Confirm serial incremented:
echo "=== POST-DEPLOYMENT: Confirm Serial Incremented ==="
ssh bind-01 "sudo rndc zonestatus inside.domusdigitalis.dev | grep serial"
Zone Status
ssh bind-01 "sudo rndc zonestatus inside.domusdigitalis.dev"
ssh bind-01 "sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zone"
Zone Reload
After editing zone files:
ssh bind-01 "sudo rndc reload inside.domusdigitalis.dev"
Reload all zones:
ssh bind-01 "sudo rndc reload"
Service Management
ssh bind-01 "sudo systemctl status named"
ssh bind-01 "sudo systemctl restart named"
Query Logging
Enable (for debugging):
ssh bind-01 "sudo rndc querylog on"
Disable (production):
ssh bind-01 "sudo rndc querylog off"
Check status:
ssh bind-01 "sudo rndc status | grep 'query logging'"
Cache Management
Flush entire cache:
ssh bind-01 "sudo rndc flush"
Flush specific domain:
ssh bind-01 "sudo rndc flushname malicious.domain.com"
Dump cache for analysis:
ssh bind-01 "sudo rndc dumpdb -cache && sudo head -100 /var/named/data/cache_dump.db"
Logs
Live tail:
ssh bind-01 "sudo journalctl -u named -f"
Recent entries:
ssh bind-01 "sudo journalctl -u named --since '1 hour ago'"
Client DNS Queries
Basic Lookups
A record:
dig ise-01.inside.domusdigitalis.dev +short
Query specific server:
dig @10.50.1.90 ise-01.inside.domusdigitalis.dev +short
Service Discovery (SRV)
Kerberos:
dig _kerberos._tcp.inside.domusdigitalis.dev SRV +short
LDAP:
dig _ldap._tcp.inside.domusdigitalis.dev SRV +short
NetworkManager DNS Config
List 802.1X connections:
nmcli connection show | grep -E "802\.1X|ethernet|wifi"
Configure DNS:
nmcli connection modify "Wired-802.1X-Vault" ipv4.dns "10.50.1.90"
nmcli connection modify "Wired-802.1X-Vault" ipv4.dns-search "inside.domusdigitalis.dev"
nmcli connection modify "Wired-802.1X-Vault" ipv4.ignore-auto-dns yes
nmcli connection up "Wired-802.1X-Vault"
Verify:
nmcli connection show "Wired-802.1X-Vault" | grep -E "ipv4\.(dns|ignore)"
Troubleshooting
NXDOMAIN for Internal Host
# NXDOMAIN for internal host - check:
dig @10.50.1.90 host.inside.domusdigitalis.dev # Direct to BIND
dig @10.50.1.1 host.inside.domusdigitalis.dev # Via VyOS
ssh bind-01 "grep host /var/named/inside.domusdigitalis.dev.zone"
Kerberos "Cannot Find KDC"
# "Cannot find KDC" - SRV records missing
dig _kerberos._tcp.inside.domusdigitalis.dev SRV +short
# Should return: 0 100 88 home-dc01.inside.domusdigitalis.dev.
# If empty: add SRV records to zone file
Update Existing DNS Records
Complete workflow for updating A record IPs with pre/post validation.
Phase 1: Pre-Validation
echo "=== 1.1 CURRENT STATE ==="
ssh $DNS_SERVER "sudo grep -E 'HOSTNAME_PATTERN' $FORWARD_ZONE"
echo "=== 1.2 CURRENT SERIAL ==="
ssh $DNS_SERVER "sudo rndc zonestatus $DOMAIN | grep serial"
echo "=== 1.3 CURRENT DNS RESOLUTION ==="
for h in host1 host2 host3; do
echo -n "$h: "
dig @$DNS_IP $h.$DOMAIN +short
done
Phase 2: Backup
TIMESTAMP=$(date +%Y%m%d%H%M)
echo "=== 2.1 CREATE BACKUP ==="
ssh $DNS_SERVER "sudo cp $FORWARD_ZONE ${FORWARD_ZONE}.bak.${TIMESTAMP}"
echo "=== 2.2 VERIFY BACKUP EXISTS ==="
ssh $DNS_SERVER "sudo ls -la ${FORWARD_ZONE}.bak.${TIMESTAMP}"
Phase 3: Capture Serial
CURRENT_SERIAL=$(dig @$DNS_IP $DOMAIN SOA +short | awk '{print $3}')
NEW_SERIAL=$((CURRENT_SERIAL + 1))
echo "=== 3.1 SERIAL VALUES ==="
echo "Current: $CURRENT_SERIAL"
echo "New: $NEW_SERIAL"
Phase 4: Apply Changes
echo "=== 4.1 UPDATE IPS ==="
ssh $DNS_SERVER "sudo sed -i '/hostname/s/OLD_IP/NEW_IP/' $FORWARD_ZONE"
echo "=== 4.2 UPDATE SERIAL ==="
ssh $DNS_SERVER "sudo sed -i 's/$CURRENT_SERIAL/$NEW_SERIAL/' $FORWARD_ZONE"
echo "=== 4.3 VERIFY CHANGES ==="
ssh $DNS_SERVER "sudo grep -E 'HOSTNAME_PATTERN' $FORWARD_ZONE"
ssh $DNS_SERVER "sudo grep -E '[0-9]{10}.*[Ss]erial' $FORWARD_ZONE"
Phase 5: Validate & Reload
echo "=== 5.1 CHECK ZONE SYNTAX ==="
ssh $DNS_SERVER "sudo named-checkzone $DOMAIN $FORWARD_ZONE"
echo "=== 5.2 RELOAD ZONE ==="
ssh $DNS_SERVER "sudo rndc reload $DOMAIN"
echo "=== 5.3 VERIFY NEW SERIAL LOADED ==="
ssh $DNS_SERVER "sudo rndc zonestatus $DOMAIN | grep serial"
Phase 6: Post-Validation
echo "=== 6.1 VERIFY DNS RESOLUTION ==="
for h in host1 host2 host3; do
result=$(dig @$DNS_IP $h.$DOMAIN +short)
if [[ "$result" == "EXPECTED_IP" ]]; then
echo "✓ $h → $result"
else
echo "✗ $h: expected EXPECTED_IP, got '$result'"
fi
done
echo "=== 6.2 SUMMARY ==="
echo "Backup: ${FORWARD_ZONE}.bak.${TIMESTAMP}"
echo "Serial: $CURRENT_SERIAL → $NEW_SERIAL"
Example: Monitoring Records Fix (2026-03-04)
# Fix grafana, prometheus, alertmanager: 10.50.1.120 → 10.50.1.130
TIMESTAMP=$(date +%Y%m%d%H%M)
CURRENT_SERIAL=$(dig @$DNS_IP $DOMAIN SOA +short | awk '{print $3}')
NEW_SERIAL=$((CURRENT_SERIAL + 1))
ssh $DNS_SERVER "sudo cp $FORWARD_ZONE ${FORWARD_ZONE}.bak.${TIMESTAMP}"
ssh $DNS_SERVER "sudo sed -i '/grafana/s/10.50.1.120/10.50.1.130/' $FORWARD_ZONE"
ssh $DNS_SERVER "sudo sed -i '/prometheus/s/10.50.1.120/10.50.1.130/' $FORWARD_ZONE"
ssh $DNS_SERVER "sudo sed -i '/alertmanager/s/10.50.1.120/10.50.1.130/' $FORWARD_ZONE"
ssh $DNS_SERVER "sudo sed -i 's/$CURRENT_SERIAL/$NEW_SERIAL/' $FORWARD_ZONE"
ssh $DNS_SERVER "sudo named-checkzone $DOMAIN $FORWARD_ZONE"
ssh $DNS_SERVER "sudo rndc reload $DOMAIN"
# Result: Serial 2026030101 → 2026030102, all records → 10.50.1.130 ✓
Advanced CLI Patterns
Production-grade awk/jq patterns for DNS operations. Build muscle memory with these.
pfSense DNS Overrides (netapi + jq)
List all overrides with formatted table:
netapi pfsense dns list --format json | jq -r '
["HOST","IP","DESCRIPTION"],
["----","--","-----------"],
(.[] | [.host, .ip, .descr // ""])
| @tsv' | column -t
Filter overrides by pattern:
# Find all vyos-related overrides
netapi pfsense dns list --format json | jq -r '.[] | select(.host | test("vyos")) | "\(.host) → \(.ip)"'
# Find overrides pointing to specific IP
netapi pfsense dns list --format json | jq -r '.[] | select(.ip == "10.50.1.130") | .host'
Compare pfSense overrides vs BIND (find conflicts):
# Process substitution: compare two sources
diff <(netapi pfsense dns list --format json | jq -r '.[] | "\(.host) \(.ip)"' | sort) \
<(ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1, \$NF}' /var/named/inside.domusdigitalis.dev.zone" | sort)
BIND Zone Parsing (awk)
Extract A records with formatted output:
ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {printf \"%-20s %s\n\", \$1, \$NF}' /var/named/inside.domusdigitalis.dev.zone"
Count records by type:
ssh bind-01 "sudo awk '/IN[[:space:]]+(A|CNAME|PTR)[[:space:]]/ {type[\$4]++} END {for (t in type) print t, type[t]}' /var/named/inside.domusdigitalis.dev.zone"
Find duplicate IPs (potential conflicts):
ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {ip[\$NF]++; host[\$NF]=host[\$NF] \" \" \$1} END {for (i in ip) if (ip[i]>1) print i, \"→\", host[i]}' /var/named/inside.domusdigitalis.dev.zone"
Bulk DNS Validation (awk + dig + command substitution)
Validate all A records return expected IPs:
ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1, \$NF}' /var/named/inside.domusdigitalis.dev.zone" | \
while read host ip; do
result=$(dig @10.50.1.90 ${host}.inside.domusdigitalis.dev +short)
[[ "$result" == "$ip" ]] && echo "✓ $host" || echo "✗ $host: expected $ip, got $result"
done
Parallel validation with xargs (faster):
ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1}' /var/named/inside.domusdigitalis.dev.zone" | \
xargs -P4 -I{} sh -c 'r=$(dig @10.50.1.90 {}.inside.domusdigitalis.dev +short); echo "{} → $r"'
JSON Transform Patterns (jq)
Pivot DNS list to IP-grouped format:
netapi pfsense dns list --format json | jq -r 'group_by(.ip) | .[] | "=== \(.[0].ip) ===\n\([.[].host] | join(", "))\n"'
Export to CSV for spreadsheet:
netapi pfsense dns list --format json | jq -r '["host","ip","domain","descr"], (.[] | [.host, .ip, .domain, .descr // ""]) | @csv'
Create shell variables from JSON:
# Command substitution: JSON → shell vars
eval $(netapi pfsense dns list --format json | jq -r '.[] | select(.host == "vyos-01") | "VYOS01_IP=\(.ip)"')
echo $VYOS01_IP
Process Substitution Patterns
Compare two DNS servers' responses:
diff <(dig @10.50.1.90 vault-01.inside.domusdigitalis.dev +short) <(dig @10.50.1.1 vault-01.inside.domusdigitalis.dev +short)
Validate zone file against live DNS:
# Process substitution: file vs live
paste <(ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1, \$NF}' /var/named/inside.domusdigitalis.dev.zone" | sort) \
<(ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1}' /var/named/inside.domusdigitalis.dev.zone" | while read h; do echo "$h $(dig @10.50.1.90 $h.inside.domusdigitalis.dev +short)"; done | sort) | \
awk '{if ($2==$4) print "✓", $1; else print "✗", $1, $2, "vs", $4}'
Feed multiple hosts to loop without subshell:
# Process substitution avoids subshell (variables persist)
count=0
while read host ip; do
((count++))
done < <(ssh bind-01 "sudo awk '/IN[[:space:]]+A[[:space:]]+/ {print \$1, \$NF}' /var/named/inside.domusdigitalis.dev.zone")
echo "Total records: $count"
One-Liners Quick Reference
| Task | Command |
|---|---|
List pfSense overrides (vyos only) |
|
Count BIND A records |
|
Show zone serial |
|
Find stale overrides (IP not in BIND) |
|
Reverse lookup all k3s nodes |
|
Related
-
VyOS Migration - BIND ops support Phase A
-
BIND Infrastructure Records - Milestone A runbook