Resolving Persistent BIND .jnl Files and DDNS Update Failures in ISC DHCPD Integration


2 views

When integrating BIND 9 with ISC DHCP server for dynamic DNS updates, administrators frequently encounter situations where:

-rw-r--r-- 1 bind bind 691 Dec 10 11:06 example.com.zone
-rw-r--r-- 1 bind bind 765 Dec 10 12:17 example.com.zone.jnl

The journal file (.jnl) persists while the main zone file fails to update, causing resolution failures for newly registered or updated hosts.

BIND's journal files serve as transaction logs for zone updates. Under normal operation:

  1. DDNS updates are written to .jnl first
  2. BIND periodically commits changes to zone file
  3. Journal gets cleared after successful commit

The sticking point occurs when step 2 fails silently.

Verify these critical settings in named.conf:

options {
    directory "/var/cache/bind";
    dump-file "data/cache_dump.db";
    statistics-file "data/named_stats.txt";
    memstatistics-file "data/named_mem_stats.txt";
    allow-update { key "dhcp-update"; };
    journal "example.com.zone.jnl"; // Explicit journal declaration
    max-journal-size 10M; // Default is unlimited
};

zone "example.com" {
    type master;
    file "/var/lib/bind/db.example.com";
    allow-update { key "dhcp-update"; };
    update-policy {
        grant dhcp-update name *.example.com. A;
    };
};

Permission Gotchas: Despite correct ownership, check:

# Verify SELinux contexts if applicable
ls -Z /var/lib/bind/
chcon -t named_zone_t /var/lib/bind/*

DHCP Server Configuration: Ensure proper key usage in dhcpd.conf:

ddns-updates on;
ddns-update-style interim;
update-static-leases on;
key "dhcp-update" {
    algorithm hmac-md5;
    secret "base64-key-here";
};
zone example.com. {
    primary 127.0.0.1;
    key "dhcp-update";
}

Create a watchdog script to force journal commits:

#!/bin/bash
# Force journal flush for stuck zones
ZONES=$(find /var/lib/bind -name "*.jnl" -exec basename {} .jnl \;)
for zone in $ZONES; do
    rndc thaw $zone
    rndc reload $zone
done
# Verify with
rndc status | grep "number of zones"

Enable detailed logging in named.conf:

logging {
    channel update_debug {
        file "/var/log/named/update.log";
        severity debug 3;
        print-time yes;
    };
    category update { update_debug; };
    category security { update_debug; };
};

Monitor journal file growth patterns:

watch -n 60 'ls -lh /var/lib/bind/*.jnl'

When working with BIND 9.x and ISC DHCP server integration, many administrators encounter a frustrating scenario where:

$ ls -la /var/lib/bind/zones/
-rw-r--r-- 1 bind bind  691 Dec 10 11:06 myzone.zone
-rw-r--r-- 1 bind bind  765 Dec 10 12:17 myzone.zone.jnl  # This shouldn't persist

The persistence of .jnl files indicates BIND's journaling mechanism isn't properly committing changes. This typically happens when:

  • BIND lacks write permissions to the parent directory (not just the zone files)
  • Zone file serial numbers aren't properly incremented
  • DHCP server's update requests contain formatting issues
  • Journal file corruption from improper shutdowns

First, verify these critical settings in named.conf:

zone "example.com" {
    type master;
    file "/var/lib/bind/zones/example.com.zone";
    allow-update { key "dhcp-update"; };
    journal "/var/lib/bind/zones/example.com.zone.jnl";  # Explicit path helps
};

And in dhcpd.conf:

ddns-updates on;
ddns-update-style interim;
ignore client-updates;
key "dhcp-update" {
    algorithm hmac-md5;
    secret "BASE64KEY==";
};
zone example.com. {
    primary 127.0.0.1;
    key "dhcp-update";
}

Here's a step-by-step fix procedure:

1. Permission Remediation

# Correct directory ownership
sudo chown -R bind:bind /var/lib/bind/zones/
sudo chmod 775 /var/lib/bind/zones/

2. Force Journal Commit

When stuck, manually trigger processing:

rndc freeze example.com
rndc thaw example.com
# Or for immediate effect:
rndc reload example.com

3. Serial Number Management

Add this to your DHCP configuration to ensure proper serial increments:

on commit {
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    set ClientName = pick-first-value(option host-name, host-decl-name);
    execute("/usr/local/bin/dhcp-dns-update", "add", ClientIP, ClientName);
}

Create a watchdog script (/usr/local/bin/check_bind_journal):

#!/bin/bash
ZONE_PATH="/var/lib/bind/zones"
JNL_AGE_THRESHOLD=300  # 5 minutes in seconds

find $ZONE_PATH -name "*.jnl" -type f | while read jnl_file; do
    zone_file=${jnl_file%.*}
    if [ -f "$zone_file" ]; then
        jnl_age=$(($(date +%s) - $(stat -c %Y "$jnl_file")))
        if [ "$jnl_age" -gt "$JNL_AGE_THRESHOLD" ]; then
            logger -t bind-journal "Stale journal found: $jnl_file (${jnl_age}s old)"
            /usr/sbin/rndc freeze ${zone_file##*/}
            /usr/sbin/rndc thaw ${zone_file##*/}
        fi
    fi
done

Enable detailed logging in named.conf:

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

Sample log analysis pattern:

grep "update:" /var/log/named/ddns.log | \
awk '/client.*#{IP}/ {print $5,$6,$7,$8}' | \
sort | uniq -c | sort -nr