Setting Up Dynamic DNS Updates with BIND and TSIG Authentication

Setting Up Dynamic DNS Updates with BIND and TSIG Authentication

Dynamic DNS (DDNS) allows automated systems to update DNS records in real-time. This is essential for DHCP servers registering client hostnames, Kubernetes services updating their endpoints, or any system that needs programmatic DNS record management. BIND supports secure dynamic updates using TSIG (Transaction Signature) authentication.

How Dynamic DNS Works

Dynamic DNS updates use the DNS UPDATE protocol (RFC 2136). A client sends an update request to the authoritative DNS server, which validates the request and modifies the zone file. TSIG keys provide cryptographic authentication to ensure only authorized clients can make changes.

The process:

  1. Client generates an update request
  2. Client signs the request with a TSIG key
  3. Server receives and validates the signature
  4. Server applies the update to the zone
  5. Server responds with success or error

Generating TSIG Keys

Create a dedicated key for dynamic updates:

tsig-keygen -a hmac-sha256 ddns-key

Output:

key "ddns-key" {
    algorithm hmac-sha256;
    secret "X7s8F2pL+Kd9mN1vQ4rT6wY0zA3cE5hG7jU2nM4xB8=";
};

For different clients, create separate keys:

tsig-keygen -a hmac-sha256 dhcp-update-key > /etc/named/keys/dhcp.key
tsig-keygen -a hmac-sha256 k8s-update-key > /etc/named/keys/kubernetes.key
tsig-keygen -a hmac-sha256 admin-update-key > /etc/named/keys/admin.key

Secure the key files:

chmod 640 /etc/named/keys/*.key
chown root:named /etc/named/keys/*.key

Configuring BIND for Dynamic Updates

Basic Update Configuration

Include the key and configure the zone:

// /etc/named.conf

include "/etc/named/keys/ddns.key";

zone "dynamic.example.com" {
    type primary;
    file "/var/named/dynamic/dynamic.example.com.zone";
    allow-update { key "ddns-key"; };
    
    // Journal file for tracking updates
    journal "/var/named/dynamic/dynamic.example.com.zone.jnl";
};

Multiple Keys with Different Permissions

Grant different access levels to different keys:

include "/etc/named/keys/dhcp.key";
include "/etc/named/keys/kubernetes.key";
include "/etc/named/keys/admin.key";

zone "internal.example.com" {
    type primary;
    file "/var/named/zones/internal.example.com.zone";
    
    // DHCP can only update specific record types
    update-policy {
        grant dhcp-update-key name *.internal.example.com A AAAA;
        grant dhcp-update-key name *.internal.example.com TXT;
    };
};

zone "k8s.example.com" {
    type primary;
    file "/var/named/zones/k8s.example.com.zone";
    
    // Kubernetes can manage its subdomain
    update-policy {
        grant k8s-update-key subdomain k8s.example.com ANY;
    };
};

zone "example.com" {
    type primary;
    file "/var/named/zones/example.com.zone";
    
    // Admin has full control
    update-policy {
        grant admin-update-key zonesub ANY;
    };
};

Update Policy Rules

BIND supports granular update policies:

update-policy {
    // Grant based on key name
    grant keyname ruletype name [types];
};

Rule types:

  • name - Exact name match
  • subdomain - Name and all names below it
  • wildcard - Pattern match with wildcards
  • zonesub - Any name in the zone
  • self - Client can update its own name (based on key name)
  • selfsub - Client can update its name and below
  • selfwild - Client can update wildcards of its name

Examples:

update-policy {
    // DHCP can add A records for any host in clients subdomain
    grant dhcp-key subdomain clients.example.com A AAAA PTR;
    
    // Let-encrypt can update TXT records for ACME challenges
    grant acme-key name _acme-challenge.example.com TXT;
    
    // Monitoring can update health check records
    grant monitoring-key name health.example.com A TXT;
    
    // Self-registration: key name matches the record name
    grant self self * A AAAA;
};

Directory and File Permissions

BIND needs write access to zone directories for journal files:

# Create directory for dynamic zones
mkdir -p /var/named/dynamic

# Set ownership
chown named:named /var/named/dynamic
chmod 750 /var/named/dynamic

# If using SELinux
semanage fcontext -a -t named_zone_t "/var/named/dynamic(/.*)?" 
restorecon -Rv /var/named/dynamic

Sending Dynamic Updates

Using nsupdate

The nsupdate command sends dynamic updates:

# Interactive mode
nsupdate -k /etc/named/keys/ddns.key
> server ns1.example.com
> zone dynamic.example.com
> update add newhost.dynamic.example.com 300 A 192.168.1.100
> send
> quit

From a script:

nsupdate -k /etc/named/keys/ddns.key << EOF
server ns1.example.com
zone dynamic.example.com
update delete oldhost.dynamic.example.com A
update add newhost.dynamic.example.com 300 A 192.168.1.100
send
EOF

Update Operations

# Add a record
update add hostname.example.com 300 A 192.168.1.10

# Delete specific record
update delete hostname.example.com A 192.168.1.10

# Delete all records of a type
update delete hostname.example.com A

# Delete all records for a name
update delete hostname.example.com

# Update (delete + add)
update delete hostname.example.com A
update add hostname.example.com 300 A 192.168.1.20

# Add multiple records
update add hostname.example.com 300 A 192.168.1.10
update add hostname.example.com 300 A 192.168.1.11

# Add different record types
update add hostname.example.com 300 A 192.168.1.10
update add hostname.example.com 300 AAAA 2001:db8::10
update add hostname.example.com 300 TXT "v=spf1 -all"

Prerequisites (Conditional Updates)

Require certain conditions before applying updates:

nsupdate -k /etc/named/keys/ddns.key << EOF
server ns1.example.com
zone example.com

# Only add if record doesn't exist
prereq nxdomain newhost.example.com
update add newhost.example.com 300 A 192.168.1.10
send

# Only update if record exists
prereq yxdomain oldhost.example.com
update delete oldhost.example.com A
update add oldhost.example.com 300 A 192.168.1.20
send

# Only update if specific record exists
prereq yxrrset oldhost.example.com A 192.168.1.10
update delete oldhost.example.com A
update add oldhost.example.com 300 A 192.168.1.20
send
EOF

Integrating with ISC DHCP Server

Configure ISC DHCP to update DNS when leases are assigned:

DHCP Server Configuration

# /etc/dhcp/dhcpd.conf

ddns-updates on;
ddns-update-style interim;
ddns-domainname "internal.example.com";
ddns-rev-domainname "in-addr.arpa";

# Update both forward and reverse zones
update-static-leases on;

# Key for DNS updates
key "dhcp-key" {
    algorithm hmac-sha256;
    secret "your-secret-from-tsig-keygen";
};

# Forward zone
zone internal.example.com. {
    primary 192.168.1.10;
    key dhcp-key;
}

# Reverse zone
zone 1.168.192.in-addr.arpa. {
    primary 192.168.1.10;
    key dhcp-key;
}

# Subnet definition
subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    option domain-name "internal.example.com";
    option domain-name-servers 192.168.1.10;
    
    # Clients provide their hostname
    ddns-hostname = pick-first-value(
        option fqdn.hostname,
        option host-name,
        concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address))
    );
}

BIND Configuration for DHCP

// Forward zone
zone "internal.example.com" {
    type primary;
    file "/var/named/dynamic/internal.example.com.zone";
    update-policy {
        grant dhcp-key subdomain internal.example.com A AAAA DHCID;
    };
};

// Reverse zone
zone "1.168.192.in-addr.arpa" {
    type primary;
    file "/var/named/dynamic/192.168.1.rev";
    update-policy {
        grant dhcp-key subdomain 1.168.192.in-addr.arpa PTR;
    };
};

Reverse DNS Updates

Configure reverse zones for PTR records:

nsupdate -k /etc/named/keys/ddns.key << EOF
server ns1.example.com
zone 1.168.192.in-addr.arpa
update add 100.1.168.192.in-addr.arpa 300 PTR hostname.example.com.
send
EOF

Script for simultaneous forward and reverse updates:

#!/bin/bash

HOSTNAME="$1"
IP="$2"
ZONE="internal.example.com"
KEY="/etc/named/keys/ddns.key"
SERVER="ns1.example.com"
TTL=300

# Generate reverse zone from IP
IFS='.' read -ra OCTETS <<< "$IP"
REVERSE="${OCTETS[3]}.${OCTETS[2]}.${OCTETS[1]}.${OCTETS[0]}.in-addr.arpa"
REVZONE="${OCTETS[2]}.${OCTETS[1]}.${OCTETS[0]}.in-addr.arpa"

# Update forward zone
nsupdate -k "$KEY" << EOF
server $SERVER
zone $ZONE
update delete ${HOSTNAME}.${ZONE} A
update add ${HOSTNAME}.${ZONE} $TTL A $IP
send
EOF

# Update reverse zone
nsupdate -k "$KEY" << EOF
server $SERVER
zone $REVZONE
update delete $REVERSE PTR
update add $REVERSE $TTL PTR ${HOSTNAME}.${ZONE}.
send
EOF

echo "Updated: ${HOSTNAME}.${ZONE} -> $IP"

Managing Journal Files

BIND stores dynamic updates in journal files before merging them with the zone file.

Freeze and Thaw

To manually edit a dynamic zone:

# Freeze zone (syncs journal to zone file)
rndc freeze example.com

# Edit the zone file manually
vim /var/named/dynamic/example.com.zone

# Update serial number!
# Thaw zone (re-enables dynamic updates)
rndc thaw example.com

Sync Without Freezing

# Sync journal to zone file without freezing
rndc sync example.com

# Sync and clear journal
rndc sync -clean example.com

Viewing Zone Contents

# Dump zone including dynamic updates
rndc dumpdb -zones
cat /var/named/data/named_dump.db | grep -A100 "example.com"

# Or view via dig
dig @localhost example.com AXFR

Troubleshooting

Enable Update Logging

logging {
    channel update_log {
        file "/var/log/named/update.log" versions 5 size 10m;
        severity debug;
        print-time yes;
        print-category yes;
    };
    
    category update { update_log; };
    category update-security { update_log; };
};

Common Errors

REFUSED

; TSIG error with server: tsig verify failure
update failed: REFUSED

Causes:

  • Key not configured on server
  • Key name mismatch
  • Secret mismatch
  • Update policy doesn't allow the operation

NOTAUTH

update failed: NOTAUTH

Causes:

  • Server is not authoritative for the zone
  • Zone name incorrect

SERVFAIL

update failed: SERVFAIL

Causes:

  • Zone file permission issues
  • Journal file permission issues
  • Filesystem full

Debug Mode

# Verbose nsupdate
nsupdate -d -k /etc/named/keys/ddns.key << EOF
server ns1.example.com
zone example.com
update add test.example.com 300 A 192.168.1.99
send
EOF

Check Permissions

# Zone file and directory permissions
ls -la /var/named/dynamic/

# SELinux context
ls -laZ /var/named/dynamic/

# Check for SELinux denials
ausearch -m AVC -ts recent | grep named

Security Considerations

Limit Update Scope

Never use allow-update { any; }. Always restrict updates:

// Bad - anyone can update
allow-update { any; };

// Good - only authenticated clients
allow-update { key "update-key"; };

// Better - granular policy
update-policy {
    grant update-key subdomain clients.example.com A AAAA;
};

Separate Keys per Service

Each service should have its own key with minimum required permissions:

update-policy {
    // DHCP only manages client records
    grant dhcp-key subdomain clients.internal.example.com A AAAA PTR DHCID;
    
    // Let's Encrypt only manages ACME challenges
    grant acme-key name _acme-challenge.example.com TXT;
    
    // Kubernetes only manages its namespace
    grant k8s-key subdomain k8s.example.com A AAAA SRV;
};

Protect Key Files

chmod 400 /etc/named/keys/*.key
chown root:named /etc/named/keys/*.key

# For client systems, only the specific key they need
chmod 400 /etc/dhcp/dhcp-key.key
chown root:dhcpd /etc/dhcp/dhcp-key.key

Conclusion

Dynamic DNS with TSIG authentication enables secure, automated DNS record management. Combined with granular update policies, you can safely integrate DNS updates into your DHCP infrastructure, container orchestration, and automation workflows.

The next post will cover integrating BIND with Kubernetes external-dns for automated service discovery and DNS management.

Read more

HAProxy Monitoring with Prometheus: Complete Observability Guide

HAProxy Monitoring with Prometheus: Complete Observability Guide

Monitoring HAProxy is essential for maintaining reliable load balancing infrastructure. Prometheus provides powerful metrics collection, alerting capabilities, and seamless Grafana integration for visualizing HAProxy performance and health. Why Prometheus for HAProxy? Prometheus offers: * Pull-based metrics - Prometheus scrapes HAProxy metrics endpoints * Time-series database - Store historical data for trend analysis

By Patrick de Ruiter