5 Commits
Author SHA1 Message Date
sduesterhaupt f3b563228f docs(changelog): document dns update and rndc synchronization changes 2026-09-05 14:11:31 +02:00
sduesterhaupt 4e375b130c feat(dyndns): add optional rndc journal synchronization
Add configurable post-update journal synchronization via rndc sync -clean.

Separate RFC 2136 update settings from local RNDC control settings.
Run synchronization only after a successful nsupdate request and only when
DYNDNS_RNDC_SYNC_CLEAN is enabled.

Treat RNDC synchronization failures as warnings because the DNS update has
already been accepted and named retains the authoritative state in its journal.
2026-09-05 13:55:34 +02:00
sduesterhaupt 24d23012c5 Add project home reference to dyndns.sh header 2026-06-28 10:24:44 +02:00
sduesterhaupt edef3c3303 style: refine dyndns.sh and vars.example comments and formatting 2026-06-28 10:08:08 +02:00
sduesterhaupt b3a4c05001 docs: refine README wording and formatting 2026-06-28 10:06:30 +02:00
4 changed files with 147 additions and 62 deletions
+22
View File
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.1.0 - 2026-09-05
### Added
- Added optional post-update BIND journal synchronization through `rndc sync -clean`.
- Added `DYNDNS_RNDC_SERVER`, `DYNDNS_RNDC_KEY`, and `DYNDNS_RNDC_SYNC_CLEAN` configuration parameters.
- Added validation for the RNDC synchronization switch and required RNDC settings when synchronization is enabled.
- Added conditional runtime validation for the `rndc` command when post-update journal synchronization is enabled.
- Added `nsupdate` diagnostic output to the dynDNS log and preservation of failed update instructions for troubleshooting.
### Changed
- Separated RFC 2136 update settings from BIND RNDC control settings.
- Updated dynamic update processing to target the authoritative DNS listener instead of a non-authoritative localhost resolver view.
- Changed the post-update workflow to synchronize accepted dynamic updates into the primary zone file and clean BIND journal files when explicitly enabled.
- Made RNDC synchronization failures non-fatal after a successful `nsupdate` transaction, while retaining a warning in the dynDNS log.
### Removed
- Removed unsafe DNSSEC handling that attempted to delete the zone `DNSKEY` RRset before dynamic updates.
- Removed the unnecessary `rndc freeze` and `rndc thaw` workflow after RFC 2136 updates.
## 1.0.0 - 2026-06-20
### Added
+4 -4
View File
@@ -32,7 +32,7 @@ This repository contains only the backend components of the dynDNS service: the
1. Clone the repository.
2. Copy `vars.example` to a site-specific vars file.
3. Adjust paths, database settings, BIND zone settings, and TSIG key references.
3. Adjust paths, database settings, BIND zone settings and TSIG key references.
4. Make `dyndns.sh` executable.
5. Run the script manually once before automation.
@@ -87,7 +87,7 @@ GRANT ALL PRIVILEGES ON dyndns.* TO 'dyndnsuser'@'127.0.0.1';
FLUSH PRIVILEGES;
```
This setup allows local access for the dedicated dynDNS user from `localhost`, `127.0.0.1`, and `::1`, and keeps the dynDNS backend user restricted to the `dyndns` database and local connections only.
This setup allows local access for the dedicated dynDNS user from `localhost`, `127.0.0.1` and `::1` and keeps the dynDNS backend user restricted to the `dyndns` database and local connections only.
After database initialization, adjust the matching settings in your vars file:
@@ -262,8 +262,8 @@ See `LICENSE` for details.
CB-601 - the open tec Elevator
- Stephan Düsterhaupt ([XMPP](xmpp:me@jabber.stephanduesterhaupt.de))
- Ivo Noack aka Insonic ([XMPP](xmpp:me@jabber.ivonoack.de))
- [Stephan Düsterhaupt](xmpp:me@jabber.stephanduesterhaupt.de)
- [Ivo Noack](xmpp:me@jabber.ivonoack.de) aka Insonic
## Project Home
+62 -35
View File
@@ -17,9 +17,12 @@
# - Validate database connectivity before command execution
# - POSIX-compatible operation for cron and service integration
# Authors: Stephan Düsterhaupt
# Authors: Stephan Düsterhaupt, Ivo Noack aka Insonic
# Copyright (c) 2018-2026 CB-601 - the open tec Elevator
# License: MIT
#
# Project Home: https://dev.town-square.de/cb601/dyndns
#
###############################################################################
@@ -238,6 +241,19 @@ _vars_source_check() {
[ -n "$DYNDNS_BIND_ZONE_DIR" ] || _die "DYNDNS_BIND_ZONE_DIR env-var undefined" 5
[ -n "$DYNDNS_BIND_ZONE_FILE" ] || _die "DYNDNS_BIND_ZONE_FILE env-var undefined" 5
case "$DYNDNS_RNDC_SYNC_CLEAN" in
0|1)
;;
*)
_die "DYNDNS_RNDC_SYNC_CLEAN must be either 0 or 1" 5
;;
esac
if [ "$DYNDNS_RNDC_SYNC_CLEAN" = "1" ]; then
[ -n "$DYNDNS_RNDC_SERVER" ] || _die "DYNDNS_RNDC_SERVER env-var undefined" 5
[ -n "$DYNDNS_RNDC_KEY" ] || _die "DYNDNS_RNDC_KEY env-var undefined" 5
fi
_log "Leave the function '_vars_source_check()'..." 1
return 0
@@ -262,12 +278,15 @@ _verify_runtime_init() {
|| command -v "$DYNDNS_DB_PROGRAM" >/dev/null 2>&1 \
|| _die "Database client '$DYNDNS_DB_PROGRAM' not available. $help_note" 5
command -v rndc >/dev/null 2>&1 || _die "rndc not available. $help_note" 5
command -v sed >/dev/null 2>&1 || _die "sed not available. $help_note" 5
command -v grep >/dev/null 2>&1 || _die "grep not available. $help_note" 5
command -v awk >/dev/null 2>&1 || _die "awk not available. $help_note" 5
command -v nsupdate >/dev/null 2>&1 || _die "nsupdate not available. $help_note" 5
if [ "$DYNDNS_RNDC_SYNC_CLEAN" = "1" ]; then
command -v rndc >/dev/null 2>&1 || _die "rndc not available. $help_note" 5
fi
_log "Leave the function '_verify_runtime_init()'..." 1
return 0
} #=> _verify_runtime_init()
@@ -368,6 +387,16 @@ _vars_setup() {
set_var DYNDNS_BIND_ZONE_FILE_SIGNED "$DYNDNS_BIND_ZONE_DIR/$DYNDNS_BIND_ZONE.zone.signed"
set_var DYNDNS_BIND_ZONE_TTL 300
# RNDC post-update zone synchronization defaults
#
# The RNDC control interface is separate from the authoritative DNS
# listener used by nsupdate. Keep the default disabled so existing
# installations do not gain a new administrative post-update action
# unless it is explicitly enabled in their vars file.
set_var DYNDNS_RNDC_SERVER "127.0.0.1"
set_var DYNDNS_RNDC_KEY ""
set_var DYNDNS_RNDC_SYNC_CLEAN 0
# Create a per-run temp directory name. The directory itself may be created
# lazily later by dyndns_mktemp().
if [ -z "$DYNDNS_TEMP_DIR_session" ]; then
@@ -398,7 +427,7 @@ set_var() {
# SDuesterhaupt: 2026-06-18 - Write a message to the dynDNS log file.
#
# Severity levels:
# 0 -> OFF
# 1 -> DEBUG
@@ -406,7 +435,7 @@ set_var() {
# 3 -> WARNING
# 4 -> ERROR
# 5 -> CRITICAL
#
# @param1: Log message.
# @param2: Severity level.
_log() {
@@ -444,7 +473,7 @@ _log() {
# SDuesterhaupt: 2026-06-18 - Check whether DYNDNS_MEMBER_HOSTNAME is a valid hostname
#
# Returns:
# 0 if valid
# 1 if invalid
@@ -1294,26 +1323,11 @@ _zone_updates_apply() {
return 1
}
_log "Check the zone '$DYNDNS_BIND_ZONE' for DNSSEC capabilities..." 1
if [ -f "$DYNDNS_BIND_ZONE_FILE_SIGNED" ]; then
_log "The zone '$DYNDNS_BIND_ZONE' uses DNSSEC..." 1
zone_uses_dnssec=1
else
_log "The zone '$DYNDNS_BIND_ZONE' does not use DNSSEC..." 1
zone_uses_dnssec=0
fi
{
printf 'debug\n'
printf 'server %s\n' "$DYNDNS_BIND_SERVER"
printf 'zone %s.\n' "$DYNDNS_BIND_ZONE"
if [ "$zone_uses_dnssec" -eq 1 ]; then
printf '; Move %s from secure to insecure temporarily\n' "$DYNDNS_BIND_ZONE_FILE"
printf 'update delete %s. DNSKEY\n' "$DYNDNS_BIND_ZONE"
printf 'send\n'
fi
awk -F '\t' -v zone_file="$DYNDNS_BIND_ZONE_FILE" '
$1 == "del" {
del_owner[++del_count] = $2
@@ -1372,9 +1386,20 @@ _zone_updates_apply() {
cp "$nsupdate_file" "$DYNDNS_LOG_UPDATE_FILE" 2>/dev/null || true
if ! nsupdate -k "$DYNDNS_BIND_ZONE_KEY" "$nsupdate_file" >/dev/null 2>&1; then
nsupdate_output=$(nsupdate -d -k "$DYNDNS_BIND_ZONE_KEY" "$nsupdate_file" 2>&1)
nsupdate_rc=$?
if [ "$nsupdate_rc" -ne 0 ]; then
{
printf '%s\n' "nsupdate output (exit code: $nsupdate_rc):"
printf '%s\n' "$nsupdate_output"
} >> "$DYNDNS_LOG_FILE"
cp "$nsupdate_file" \
"$DYNDNS_LOG_UPDATE_FILE.failed" 2>/dev/null || true
rm -f "$nsupdate_file"
_log "Zone updates could not be applied." 4
_log "Zone updates could not be applied (nsupdate exit code: $nsupdate_rc)." 4
_log "Leave the function '_zone_updates_apply()'..." 1
return 1
fi
@@ -1394,20 +1419,22 @@ _zone_updates_apply() {
rm -f "$nsupdate_file"
_log "Zone updates were applied successfully." 2
if command -v rndc >/dev/null 2>&1; then
if rndc -s "$DYNDNS_BIND_SERVER" -k "$DYNDNS_BIND_ZONE_KEY" freeze "$DYNDNS_BIND_ZONE" >/dev/null 2>&1; then
_log "The zone '$DYNDNS_BIND_ZONE' was frozen." 2
if rndc -s "$DYNDNS_BIND_SERVER" -k "$DYNDNS_BIND_ZONE_KEY" thaw "$DYNDNS_BIND_ZONE" >/dev/null 2>&1; then
_log "The zone '$DYNDNS_BIND_ZONE' was reloaded." 2
# Synchronize dynamic zone journal changes into the master zone file.
#
# BIND keeps successful RFC 2136 updates in the zone journal until a zone dump
# occurs. sync -clean writes the active journal state to the master zone file
# and removes the journal after a successful synchronization.
#
# A synchronization failure is intentionally non-fatal: nsupdate has already
# succeeded and named keeps the authoritative runtime state in the journal.
if [ "$DYNDNS_RNDC_SYNC_CLEAN" = "1" ]; then
if rndc \
-s "$DYNDNS_RNDC_SERVER" \
-k "$DYNDNS_RNDC_KEY" \
sync -clean "$DYNDNS_BIND_ZONE" >/dev/null 2>&1; then
_log "The zone '$DYNDNS_BIND_ZONE' was synchronized and journals were cleaned." 2
else
_log "Couldn't reload the zone '$DYNDNS_BIND_ZONE'." 4
_log "Leave the function '_zone_updates_apply()'..." 1
return 1
fi
else
_log "Couldn't freeze the zone '$DYNDNS_BIND_ZONE'." 4
_log "Leave the function '_zone_updates_apply()'..." 1
return 1
_log "Zone update succeeded, but rndc sync -clean failed for '$DYNDNS_BIND_ZONE'." 3
fi
fi
+40 -4
View File
@@ -69,7 +69,7 @@
# Database login credentials.
# These are mainly used when DYNDNS_SQL_GROUP is unset/empty.
#set_var DYNDNS_SQL_USER "dyndns_user"
#set_var DYNDNS_SQL_PASS "strong_passphrase"
#set_var DYNDNS_SQL_PASS "<REPLACE_WITH_STRONG_PASSWORD>"
# Additional database client options, if needed.
# Example:
@@ -81,7 +81,12 @@
# DNS / BIND
# ------------------------------------------------------------------
# Authoritative DNS server used for update operations
# Authoritative DNS server used for RFC 2136 dynamic update operations.
#
# This address is used by nsupdate on DNS port 53. It must point to the
# authoritative BIND listener and matching view serving DYNDNS_BIND_ZONE.
#
# Do not use a local recursive-only listener or a non-authoritative view.
#set_var DYNDNS_BIND_SERVER "127.0.0.1"
# Service name used when reloading/reconfiguring the name server
@@ -90,7 +95,10 @@
# DNS zone handled by dynDNS
#set_var DYNDNS_BIND_ZONE "example24.com"
# TSIG key file or key identifier used for nsupdate access
# TSIG key file or key identifier used for nsupdate access.
#
# This key should be restricted in BIND with update-policy and must not be
# confused with the RNDC control key configured below.
#set_var DYNDNS_BIND_ZONE_KEY "keyfile"
# Directory containing the primary zone files
@@ -105,6 +113,34 @@
# Default TTL for generated/managed records
#set_var DYNDNS_BIND_ZONE_TTL 300
# ------------------------------------------------------------------
# RNDC POST-UPDATE ZONE SYNCHRONIZATION
# ------------------------------------------------------------------
# Local BIND control endpoint used for optional post-update synchronization.
#
# rndc connects to named's control interface, normally TCP port 953.
# This is independent from DYNDNS_BIND_SERVER, which is used by nsupdate
# against DNS port 53.
#set_var DYNDNS_RNDC_SERVER "127.0.0.1"
# RNDC control key authorized by the named controls {} configuration.
#
# This key is intentionally separate from DYNDNS_BIND_ZONE_KEY. The latter is
# used by nsupdate for RFC 2136 DNS updates, while this key authorizes BIND
# administrative operations such as "rndc sync -clean".
#set_var DYNDNS_RNDC_KEY "/etc/named/rndc.key"
# Synchronize dynamic zone journals after a successful nsupdate request.
#
# 0 = disabled; named handles zone dumps using its normal maintenance schedule.
# 1 = execute "rndc sync -clean <zone>" after a successful DNS update.
#
# A failed synchronization must be logged as a warning only. The dynamic update
# remains successful because named retains the authoritative state in its
# journal until the next successful zone dump.
#set_var DYNDNS_RNDC_SYNC_CLEAN 0
# ------------------------------------------------------------------
# MEMBER DEFAULTS
# ------------------------------------------------------------------
@@ -119,7 +155,7 @@
#set_var DYNDNS_MEMBER_EMAIL "admin@example24.com"
# Password or shared secret used during member creation/authentication
#set_var DYNDNS_MEMBER_PASS "strong_passphrase"
#set_var DYNDNS_MEMBER_PASS "<REPLACE_WITH_STRONG_PASSWORD>"
# ------------------------------------------------------------------
# OPTIONAL COMMAND HOOKS