- Go 72.8%
- JavaScript 13.7%
- Shell 7.2%
- Makefile 6.3%
|
All checks were successful
Build Technitium DDNS OpenWrt packages / build (push) Successful in 2m10s
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. |
||
|---|---|---|
| .forgejo/workflows | ||
| cmd/openwrt-technitium-ddns | ||
| deploy | ||
| internal | ||
| luci-app-technitium-ddns | ||
| package/openwrt-technitium-ddns | ||
| scripts | ||
| tests | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
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-hookwird vondnsmasqüberdhcpscriptaufgerufen.- Der Hook validiert
add,oldunddelund ruft kurzlebigopenwrt-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.jsonatomar. - Forward- und Reverse-Updates sind getrennte DNS-UPDATE-Transaktionen, weil sie unterschiedliche autoritative Zonen betreffen.
- Beim Start und periodisch liest der Dienst
/tmp/dhcp.leasesund 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-ddnsluci-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
statuszeigen nurset,missingoder 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
- Forward-Zonen als Primary Zones erstellen:
lan.monapona.deiot.monapona.de
- Reverse-Zonen als Primary Zones erstellen:
1.168.192.in-addr.arpa35.168.192.in-addr.arpa
- In Technitium einen TSIG-Schlüssel anlegen:
- Name:
openwrt-ddns. - Algorithmus: HMAC-SHA256
- Secret: eigenes Base64-Secret
- Name:
- Dynamic Updates für jede Forward- und Reverse-Zone aktivieren.
- 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
- Secret sicher auf OpenWrt übertragen, zum Beispiel per
scpin eine temporäre Datei und danach mitinstall -m 0600nach/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:
- Zonen und TSIG-Key wie oben erstellen.
- OpenWrt-Konfiguration setzen und Dienst starten.
- Einen DHCP-Client im LAN erneuern lassen.
- A- und PTR-Records mit
digprüfen. - 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
BADTIMEliefern. transport tcpist 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.