When attempting dynamic DNS updates using nsupdate with BIND, receiving a SERVFAIL response typically indicates a fundamental misconfiguration in either the key authentication setup or zone file permissions. Let's break down the complete solution.
The HMAC-MD5 key generation appears correct, but let's verify the complete implementation:
key "sub.example.com." {
algorithm hmac-md5;
secret "your-base64-secret-here";
};
Critical points about key configuration:
1. Ensure the key name matches exactly in named.conf and nsupdate command
2. Verify the secret is base64-encoded without spaces
3. Check key file permissions (600 recommended)
The zone file must:
- Exist before first update attempt
- Contain proper SOA record
- Have correct permissions (bind:bind with 644 rights)
Example minimal zone file:
$ORIGIN sub.example.com.
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2023080601 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
86400 ; minimum TTL
)
The configuration shown needs these adjustments:
zone "sub.example.com" {
type master;
file "/etc/bind/primary/sub.example.com";
allow-update { key "sub.example.com."; };
journal "/var/lib/bind/sub.example.com.jnl";
};
Your update file should include proper authentication:
key sub.example.com. your-base64-secret
server dns-server-ip
zone sub.example.com
update add test.sub.example.com. 300 A 192.168.1.1
show
send
These directory permissions are essential:
chown bind:bind /etc/bind/primary
chmod 775 /etc/bind/primary
chown bind:bind /var/cache/bind
setfacl -Rm u:bind:rwx /var/cache/bind
Enable detailed logging in named.conf:
logging {
channel update_debug {
file "/var/log/bind/update.log";
severity debug 3;
};
category update { update_debug; };
category security { update_debug; };
};
- Key name mismatch between config and command
- Incorrect key algorithm specification
- Missing journal file permissions
- Firewall blocking TCP/53 (needed for updates)
- SELinux/AppArmor restrictions
- Zone file serial number not incrementing
Here's a verified nsupdate sequence:
nsupdate -k Ksub.example.com.+157+12345.private
> server 192.168.1.100
> zone sub.example.com
> update add test.sub.example.com. 300 A 192.168.1.50
> show
> send
> quit
Remember to verify with dig after update:
dig @dns-server test.sub.example.com A +short
The SERVFAIL error during DNS dynamic updates typically indicates a server-side rejection of the update request, despite the client successfully authenticating. From the logs, we can see the authentication succeeds (request has valid signature
) but the update itself fails.
For successful TSIG-authenticated updates, these elements must align:
// named.conf.local minimum working example
key "sub.example.com." {
algorithm hmac-md5;
secret "base64-encoded-key-here";
};
zone "sub.example.com" {
type master;
file "/etc/bind/dynamic/sub.example.com.db";
allow-update { key "sub.example.com."; };
journal "/var/lib/bind/sub.example.com.db.jnl";
};
File Structure Considerations
The zone file must exist before the first update attempt. Create it with a minimal SOA record:
; /etc/bind/dynamic/sub.example.com.db
$ORIGIN .
$TTL 86400
sub.example.com IN SOA ns1.example.com. admin.example.com. (
2023080601 ; serial
3600 ; refresh
900 ; retry
604800 ; expire
86400 ) ; minimum
NS ns1.example.com.
Directory Permissions
BIND needs write access to both the zone file and journal location:
sudo chown bind:bind /etc/bind/dynamic
sudo chmod 775 /etc/bind/dynamic
sudo touch /etc/bind/dynamic/sub.example.com.db
sudo chown bind:bind /etc/bind/dynamic/sub.example.com.db
The client-side update script needs these improvements:
# Working nsupdate command file
server dns-server.example.com
key sub.example.com. hmac-md5 base64-key-here
zone sub.example.com.
update add test.sub.example.com. 300 A 192.168.1.100
show
send
Enable detailed logging in named.conf:
logging {
channel update_debug {
file "/var/log/bind/update.log" versions 3 size 5m;
severity debug 3;
print-category yes;
print-severity yes;
print-time yes;
};
category update { update_debug; };
category security { update_debug; };
};
- Clock Synchronization: TSIG requires time sync within 5 minutes
- Key Encoding: Ensure the key in named.conf matches EXACTLY what dnssec-keygen generated
- Zone File Ownership: The bind user must have write permissions to both the zone file and its parent directory
For production environments, consider these enhancements:
// More secure configuration
options {
directory "/var/cache/bind";
allow-update-forwarding { none; };
update-check-ksk yes;
dnssec-validation auto;
};
zone "sub.example.com" {
type master;
file "/etc/bind/dynamic/sub.example.com.db";
allow-update {
key "sub.example.com.";
// Can also restrict by IP:
// 192.168.1.0/24;
};
update-policy {
grant sub.example.com. name test.sub.example.com. A;
};
};