No description
  • Go 72.8%
  • JavaScript 13.7%
  • Shell 7.2%
  • Makefile 6.3%
Find a file
Philipp 1e76dd02c6
All checks were successful
Build Technitium DDNS OpenWrt packages / build (push) Successful in 2m10s
Initial OpenWrt Technitium DDNS package
Add a production-ready OpenWrt package that syncs dnsmasq DHCP lease events to Technitium DNS using RFC 2136 DNS UPDATE with TSIG HMAC-SHA256 authentication.

Includes the Go daemon, dnsmasq hook, UCI/procd integration, LuCI UI, OpenWrt APK packaging, Forgejo release workflow, tests, and complete documentation.
2026-08-06 21:29:16 +02:00
.forgejo/workflows Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
cmd/openwrt-technitium-ddns Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
deploy Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
internal Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
luci-app-technitium-ddns Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
package/openwrt-technitium-ddns Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
scripts Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
tests Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
.gitignore Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
go.mod Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
go.sum Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
LICENSE Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
Makefile Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00
README.md Initial OpenWrt Technitium DDNS package 2026-08-06 21:29:16 +02:00

OpenWrt Technitium DDNS

openwrt-technitium-ddns verbindet OpenWrt dnsmasq DHCP-Leases mit einem autoritativen Technitium DNS Server. DHCP-Ereignisse werden lokal in eine atomare Queue geschrieben und danach als standardkonforme DNS UPDATE Nachrichten nach RFC 2136 übertragen. Die Authentifizierung erfolgt per TSIG nach RFC 8945 mit HMAC-SHA256, HMAC-SHA384 oder HMAC-SHA512. Es wird keine Technitium-HTTP-API für DNS-Einträge verwendet.

Architektur

  • /usr/libexec/technitium-ddns-dhcp-hook wird von dnsmasq über dhcpscript aufgerufen.
  • Der Hook validiert add, old und del und ruft kurzlebig openwrt-technitium-ddns enqueue ... auf.
  • Events landen als atomar geschriebene JSON-Dateien in /var/spool/technitium-ddns.
  • Der procd-Dienst verarbeitet Queue-Dateien, liest /etc/config/technitium-ddns, lädt das TSIG-Secret aus /etc/technitium-ddns/tsig.secret, führt RFC-2136-Updates aus und schreibt /var/lib/technitium-ddns/state.json atomar.
  • Forward- und Reverse-Updates sind getrennte DNS-UPDATE-Transaktionen, weil sie unterschiedliche autoritative Zonen betreffen.
  • Beim Start und periodisch liest der Dienst /tmp/dhcp.leases und reconciled aktive Leases.

IPv4 ist implementiert. IPv6 ist im Code strukturell vorbereitet, aber AAAA und ip6.arpa sind noch nicht aktiv.

Beispiel

UCI-Mappings:

192.168.1.0/24  -> lan.monapona.de
192.168.35.0/24 -> iot.monapona.de

Ein Lease laptop / 192.168.1.42 erzeugt:

laptop.lan.monapona.de. A 192.168.1.42
42.1.168.192.in-addr.arpa. PTR laptop.lan.monapona.de.

Ein Lease sensor-01 / 192.168.35.17 erzeugt:

sensor-01.iot.monapona.de. A 192.168.35.17
17.35.168.192.in-addr.arpa. PTR sensor-01.iot.monapona.de.

OpenWrt-Kompatibilität

Das Paket ist für moderne OpenWrt-Versionen mit Go-Paket-Support, procd, rpcd/ucode und LuCI-JavaScript-Views ausgelegt. dnsmasq-full wird empfohlen, weil Setups mit erweitertem DHCP/DNS üblicherweise dort landen. Der Hook ist POSIX-sh und vermeidet Bash-Abhängigkeiten.

Lokale Entwicklung

cd openwrt-technitium-ddns
CGO_ENABLED=0 go build -trimpath -o bin/openwrt-technitium-ddns ./cmd/openwrt-technitium-ddns
go test ./...
go vet ./...

Ein 256-Bit-TSIG-Secret kann ohne Shell-History-Leak erzeugt und direkt in eine Datei geschrieben werden:

install -d -m 0700 /etc/technitium-ddns
openwrt-technitium-ddns generate-secret > /etc/technitium-ddns/tsig.secret
chmod 0600 /etc/technitium-ddns/tsig.secret

Build Mit OpenWrt SDK

./scripts/build-openwrt.sh /path/to/openwrt-sdk

Das Skript verlinkt beide Pakete in den SDK-/Buildroot-Baum und baut:

  • package/openwrt-technitium-ddns
  • luci-app-technitium-ddns

Auf OpenWrt 25.x SDKs mit apk-Paketformat entstehen .apk-Artefakte unter bin/packages/<arch>/.... Aeltere SDKs koennen weiterhin .ipk erzeugen; der Forgejo-Workflow fuer dieses Projekt sammelt gezielt .apk-Pakete ein.

Alternativ manuell:

ln -s /path/to/openwrt-technitium-ddns/package/openwrt-technitium-ddns \
  /path/to/openwrt/package/network/services/openwrt-technitium-ddns
ln -s /path/to/openwrt-technitium-ddns/luci-app-technitium-ddns \
  /path/to/openwrt/package/luci/luci-app-technitium-ddns
cd /path/to/openwrt
make menuconfig
make package/openwrt-technitium-ddns/compile V=s
make package/luci-app-technitium-ddns/compile V=s

Installation Auf OpenWrt

OpenWrt 25.x mit apk:

scp openwrt-technitium-ddns-*.apk luci-app-technitium-ddns-*.apk root@192.168.1.1:/tmp/
ssh root@192.168.1.1 'apk add --allow-untrusted /tmp/openwrt-technitium-ddns-*.apk /tmp/luci-app-technitium-ddns-*.apk'
/etc/init.d/rpcd restart
/etc/init.d/technitium-ddns enable
/etc/init.d/technitium-ddns start

Aeltere OpenWrt-Versionen mit opkg:

opkg install openwrt-technitium-ddns_*.ipk luci-app-technitium-ddns_*.ipk
/etc/init.d/rpcd reload
/etc/init.d/technitium-ddns enable

Das Paket erstellt sichere Verzeichnisse. Vorhandene /etc/config/technitium-ddns und /etc/technitium-ddns/tsig.secret werden als Konfigurationsdateien behandelt. Beim Entfernen werden Konfiguration und Secret nicht ungefragt gelöscht.

dnsmasq-Hook

Bei Installation setzt ein UCI-Defaults-Skript dhcp.@dnsmasq[0].dhcpscript auf:

/usr/libexec/technitium-ddns-dhcp-hook

Nur wenn bereits kein Hook gesetzt ist. Ist bereits ein Hook vorhanden, schreibt das Paket eine syslog-Meldung und erwartet, dass der vorhandene Hook diesen Hook chained. Manuell:

uci set dhcp.@dnsmasq[0].dhcpscript='/usr/libexec/technitium-ddns-dhcp-hook'
uci commit dhcp
/etc/init.d/dnsmasq restart

Der Hook verarbeitet add, old und del, validiert MAC/IP, nutzt keine Shell-Auswertung von Hostnamen und schreibt Fehler per logger.

UCI-Konfiguration

Datei: /etc/config/technitium-ddns

config technitium_ddns 'main'
    option enabled '1'
    option dns_server '192.168.1.10'
    option dns_port '53'
    option transport 'tcp'
    option tsig_key_name 'openwrt-ddns.'
    option tsig_algorithm 'hmac-sha256'
    option tsig_secret_file '/etc/technitium-ddns/tsig.secret'
    option default_ttl '300'
    option update_timeout '5'
    option retry_count '3'
    option retry_backoff '2'
    option hostname_policy 'sanitize'
    option conflict_policy 'replace-owned'
    option delete_on_lease_expiry '1'
    option lease_file '/tmp/dhcp.leases'
    option periodic_reconcile '1'
    option reconcile_interval '900'
    option log_level 'info'

config mapping
    option enabled '1'
    option network '192.168.1.0/24'
    option forward_zone 'lan.monapona.de'
    option reverse_zone '1.168.192.in-addr.arpa'
    option interface 'lan'

config mapping
    option enabled '1'
    option network '192.168.35.0/24'
    option forward_zone 'iot.monapona.de'
    option reverse_zone '35.168.192.in-addr.arpa'
    option interface 'iot'

reverse_zone darf für IPv4 /8, /16 und /24 fehlen; sie wird automatisch aus dem Netz berechnet. Für /25 und andere nicht oktettweise delegierte Netze wird keine RFC-2317-Annahme getroffen. Setze dort eine explizite Reverse-Zone oder verzichte auf Reverse-Updates.

Hostname-Regeln

Standard ist sanitize:

  • Kleinbuchstaben
  • abschließender Punkt und bekannter Zonensuffix werden entfernt
  • ungültige ASCII-Zeichen werden kontrolliert zu Bindestrichen
  • leere Hostnamen und * werden ignoriert
  • Label-Länge 63 und FQDN-Länge 255 werden geprüft

strict lehnt ungültige Namen ab. ignore-invalid ignoriert sie ohne DNS-Update. Unicode/IDNA wird derzeit nachvollziehbar abgelehnt beziehungsweise in sanitize nicht als Unicode-DNS-Label übernommen.

TSIG-Sicherheit

Das Secret steht nicht im UCI-File. Erwartet wird:

/etc/technitium-ddns/tsig.secret

Anforderungen:

  • Inhalt: Base64-kodiertes Secret
  • Rechte: 0600
  • Eigentümer: root:root
  • LuCI und status zeigen nur set, missing oder fehlerhafte Rechte an
  • Logs enthalten keine Secret-Werte

Unterstützte Algorithmen: hmac-sha256, hmac-sha384, hmac-sha512. Standard ist hmac-sha256; HMAC-MD5 wird nicht akzeptiert.

Technitium Vorbereiten

  1. Forward-Zonen als Primary Zones erstellen:
    • lan.monapona.de
    • iot.monapona.de
  2. Reverse-Zonen als Primary Zones erstellen:
    • 1.168.192.in-addr.arpa
    • 35.168.192.in-addr.arpa
  3. In Technitium einen TSIG-Schlüssel anlegen:
    • Name: openwrt-ddns.
    • Algorithmus: HMAC-SHA256
    • Secret: eigenes Base64-Secret
  4. Dynamic Updates für jede Forward- und Reverse-Zone aktivieren.
  5. Restriktive Dynamic-Update-Security-Policy setzen:
    • nur der konfigurierte TSIG-Key
    • Forward-Zonen: nur Recordtyp A
    • Reverse-Zonen: nur Recordtyp PTR
    • nur innerhalb der jeweiligen Zone
  6. Secret sicher auf OpenWrt übertragen, zum Beispiel per scp in eine temporäre Datei und danach mit install -m 0600 nach /etc/technitium-ddns/tsig.secret.

Forward- und Reverse-Zonen müssen separat für Dynamic Updates autorisiert werden. Eine autorisierte Forward-Zone erlaubt nicht automatisch PTR-Updates.

LuCI

Nach Installation: Dienste -> Technitium DDNS.

  • Status: Dienststatus, Queue, aktive Leases, verwaltete Einträge, Logs und Aktionen.
  • Einstellungen: Server, TSIG-Key-Name, Algorithmus, Secret-Schreibfeld, Retry/Timeouts und Richtlinien.
  • Netzwerk-Zuordnungen: CIDR, Interface, Forward-Zone, Reverse-Zone, TTL-Override und Reverse-Vorschau.

Das rpcd-Backend wird als Exec-Plugin luci.technitium_ddns ausgeliefert. Das vermeidet Image-spezifische Unterschiede beim automatischen Laden neuer ucode-rpcd-Objekte.

Der Button Verbindung testen prüft Erreichbarkeit und SOA-Abfragen für konfigurierte Zonen. Der implementierte Test legt keine temporären Records an und hinterlässt keine DNS-Daten.

Firewall

OpenWrt muss den Technitium-Server auf Port 53 erreichen:

nc -vz 192.168.1.10 53

Für TCP-Updates muss TCP/53 erlaubt sein. Wenn transport udp verwendet wird, muss UDP/53 erlaubt sein; TCP bleibt für Diagnose und große DNS-Antworten sinnvoll.

Prüfkommandos

Status und Konfiguration:

openwrt-technitium-ddns check-config
openwrt-technitium-ddns status
/etc/init.d/technitium-ddns status

DNS-Inhalte ohne Secret in der Shell-History prüfen:

dig @192.168.1.10 laptop.lan.monapona.de A +short
dig @192.168.1.10 42.1.168.192.in-addr.arpa PTR +short
dig @192.168.1.10 lan.monapona.de SOA +short

Manuelles Test-Event:

openwrt-technitium-ddns enqueue add 02:00:00:00:00:42 192.168.1.42 laptop
openwrt-technitium-ddns process-queue

Tests

go test ./...
go vet ./...

Integrationstest mit lokalem DNS-Testserver:

go test -tags integration ./tests/integration

Manueller End-to-End-Test mit Technitium:

  1. Zonen und TSIG-Key wie oben erstellen.
  2. OpenWrt-Konfiguration setzen und Dienst starten.
  3. Einen DHCP-Client im LAN erneuern lassen.
  4. A- und PTR-Records mit dig prüfen.
  5. Lease löschen oder Client trennen und prüfen, dass nur der lokal verwaltete Datensatz entfernt wird.

Fehlerbehebung

  • REFUSED: Dynamic-Update-Policy verweigert den Recordtyp, die Zone oder den Key.
  • NOTAUTH: Server ist für die Zone nicht autoritativ oder TSIG-Autorisierung passt nicht.
  • NOTZONE: Owner-Name liegt außerhalb der UPDATE-Zone.
  • SERVFAIL: Technitium konnte das Update intern nicht verarbeiten.
  • FORMERR: Server lehnt die Nachricht als fehlerhaft ab.
  • BADKEY: Key-Name oder Algorithmus stimmt nicht.
  • BADSIG: Secret stimmt nicht.
  • BADTIME: Router-Uhrzeit prüfen, NTP aktivieren.
  • YXDOMAIN, YXRRSET, NXRRSET: unerwartete Voraussetzung im DNS-Datenbestand.
  • no enabled mapping: DHCP-IP passt zu keiner aktivierten CIDR-Zuordnung.
  • invalid hostname: Hostname-Richtlinie hat den Namen abgelehnt.

Queue prüfen:

ls -la /var/spool/technitium-ddns /var/spool/technitium-ddns/dead
logread -e technitium-ddns

Deinstallation

OpenWrt 25.x mit apk:

/etc/init.d/technitium-ddns stop
/etc/init.d/technitium-ddns disable
apk del luci-app-technitium-ddns openwrt-technitium-ddns

Aeltere OpenWrt-Versionen mit opkg:

/etc/init.d/technitium-ddns stop
/etc/init.d/technitium-ddns disable
opkg remove luci-app-technitium-ddns openwrt-technitium-ddns

Konfiguration, State und Secret bleiben erhalten. Manuelles Entfernen nur bewusst:

rm -f /etc/config/technitium-ddns
rm -rf /etc/technitium-ddns /var/lib/technitium-ddns /var/spool/technitium-ddns

Sicherheit

  • TSIG-Secret niemals in UCI, LuCI-Status, ubus-Antworten oder Logs speichern.
  • Technitium-Policy pro Zone und Recordtyp begrenzen.
  • OpenWrt-Uhrzeit per NTP synchronisieren, sonst kann TSIG BADTIME liefern.
  • transport tcp ist Default, weil es Fragmentierungsprobleme vermeidet und für signierte Updates robust ist.
  • Der dnsmasq-Hook führt keine Hostnamen als Shell-Code aus und blockiert DHCP nicht mit Netzwerk-DNS-Updates.
  • Der lokale State verwaltet nur Datensätze, die der Dienst selbst erfolgreich geschrieben hat; Reconcile löscht keine fremden Records.