Schlanker DynDNS2 Server für die Netcup DNS-API
Implementierung eines schlanken DynDNS2 Servers für die Netcup DNS-API
Diese Implementierung stellt einen DynDNS2-kompatiblen HTTPS-Endpunkt für mehrere Kunden bereit und aktualisiert fest zugeordnete A-/AAAA-Records über die Netcup DNS-API. Die Administration erfolgt über die Kommandozeile und beinhaltet keine WebUI.
Systemvoraussetzungen
- Debian oder anderes Linux System mit systemd oder SysVinit
- Apache
mpm_preforkmitmod_php - PHP CLI 7.4 oder neuer
- PHP-Erweiterungen:
curl,json,openssl,pdo_sqlite,sockets,sodium
Dieses Dienstprogramm wurde mit Hilfe generativer KI entwickelt und dient zur Automatisierung dynamischer DNS-Aktualisierungen über die netcup-DNS-API. Es handelt sich um ein eigenständiges Projekt, das weder mit netcup in Verbindung steht noch von netcup unterstützt wird.
Download
sha256sum:
5ab58c6ce6278883bdd9fa0205a33b761373e6390bb846c62927d0374c352e1d netcup-ddns-1.1.1.tar.gz
Architektur
Kundenrouter
│ HTTPS + HTTP Basic Auth
▼
Apache/mod_php als www-data
│ /run/ddns/update/worker.sock
▼
PHP-CLI-Worker als ddns-worker
├── SQLite: Hosts, Tokenhashes, Status, Audit
└── Netcup DNS API über HTTPS
Der Apache-Prozess kann weder die SQLite-Datenbank noch die Netcup-Zugangsdaten lesen. Er übergibt die Anfrage nur über einen Unix-Socket. Der Worker prüft Token, Host-Zuordnung, Recordtyp und IP-Adresse erneut.
Sicherheitsmodell
- ein kryptografisch zufälliges Token pro Host
- HTTP Basic Auth über HTTPS
- Zugangsdaten in Query-String oder URL-Pfad werden nicht unterstützt
- Token wird nur als HMAC-SHA-256 mit serverseitigem Pepper gespeichert
- Token ist fest an genau einen Host gebunden
- nur A und/oder AAAA sind pro Host freischaltbar
- öffentliche IP-Adressen werden erzwungen
- Reverse-Proxy-Header werden nur von konfigurierten Proxy-Adressen akzeptiert
- getrennte Update- und Admin-Sockets
- Netcup-Secrets ausschließlich beim Worker-Benutzer
- serielle API-Verarbeitung verhindert parallele Zonenänderungen
- Rate-Limits pro Quell-IP und Benutzername
- Query-Strings und Authorization-Header gehören nicht in Logs
- doppelte gleichartige Netcup-Records führen zum sicheren Abbruch statt zu einer mehrdeutigen Änderung
- systemd-Unit mit grundlegender Prozesshärtung
Verzeichnisstruktur
/opt/ddns/ Programmcode und lokale Dokumentation
/etc/opt/ddns/ Konfiguration und Secrets
/var/opt/ddns/database/ SQLite-Datenbank
/run/ddns/ Unix-Sockets und ggf. PID-Datei
/etc/systemd/system/ddns-worker.service systemd-Integration
/etc/init.d/ddns-worker SysVinit-Integration
Der Installer installiert nur die zum aktiven Init-System passende Integration.
Installation
Archiv entpacken und als root ausführen:
./install.sh
Das Skript erkennt automatisch, ob das System mit systemd oder SysVinit läuft. Die Auswahl kann bei Bedarf erzwungen werden:
./install.sh --init=systemd
./install.sh --init=sysvinit
Das Skript:
- prüft PHP und Erweiterungen,
- erstellt
ddns-workerund die gemeinsame Socket-Gruppeddns-update, - fügt
www-dataundddns-workerder Socket-Gruppe hinzu, - installiert Code und die passende Init-Integration,
- erzeugt bei einer Erstinstallation einen zufälligen Token-Pepper,
- überschreibt bestehende Konfigurationen nicht,
- startet den Dienst bei einer Erstinstallation bewusst noch nicht.
War der Worker vor einem Upgrade aktiv, wird er kontrolliert gestoppt und nach erfolgreicher Installation wieder gestartet.
Danach Apache neu starten, damit dessen Prozesse die neue Gruppenmitgliedschaft erhalten:
service apache2 restart
Konfiguration
1. Worker
/etc/opt/ddns/worker.php bearbeiten:
'endpoint_base_url' => 'https://dyn.example.de/nic/update',
'zones' => [
// Erlaubter DynDNS-Namensraum:
'ddns.example.de' => [
// Bei Netcup verwaltete Basiszone:
'domain' => 'example.de',
],
],
Dabei ist ddns.example.de der ausschließlich für DynDNS erlaubte Namensraum. Netcup verwaltet den Record innerhalb der Basiszone example.de, beispielsweise als router4711.ddns für den vollständigen Namen router4711.ddns.example.de.
2. Secrets
/etc/opt/ddns/secrets.php mit einem dedizierten Netcup-API-Key ausfüllen:
'netcup' => [
'customer_number' => '123456',
'api_key' => '...',
'api_password' => '...',
],
Der Worker verwendet den offiziellen JSON-Endpunkt:
https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON
Die Netcup-API verlangt Login mit Kundennummer, API-Key und API-Passwort, liefert eine Session-ID und bietet infoDnsRecords sowie updateDnsRecords. Technische Dokumentation:
- https://www.netcup.com/de/helpcenter/dokumentation/domain/unsere-api
- https://ccp.netcup.net/run/webservice/servers/endpoint.php
3. Apache
etc/apache2/sites-available/ddns.conf.example als Vorlage verwenden.
- Darin eine gültige HTTPS-URL im Router konfigurieren
/nic/updateauf Port 80 ablehnen, nicht erst nach Übertragung umleiten- Access-Log mit
%U, nicht mit%roder%q - keine Authorization-Header protokollieren
- ausschließlich
/nic/updateöffentlich freigeben
Nach Aktivierung Konfiguration prüfen:
apache2ctl configtest
service apache2 reload
4. Worker starten
systemd:
systemctl start ddns-worker
systemctl status ddns-worker
SysVinit:
service ddns-worker start
service ddns-worker status
Anschließend für beide Systeme:
/opt/ddns/bin/ddns-admin worker ping
/opt/ddns/bin/ddns-admin doctor
Host anlegen
/opt/ddns/bin/ddns-admin host add \
--fqdn router4711.ddns.example.de \
--customer kunde4711 \
--ipv4 \
--ipv6
Ausgabe:
Hostname: router4711.ddns.example.de
Benutzer: router4711.ddns.example.de
Token: <einmalig ausgegebenes Token>
Update-URL: https://dyn.example.de/nic/update
Das Klartext-Token wird nicht gespeichert und kann später nicht ausgelesen werden.
Router-Konfiguration
URL für IPv4:
https://dyn.example.de/nic/update?hostname=<domain>&myip=<ipaddr>
URL für IPv6 (separater Aufruf):
https://dyn.example.de/nic/update?hostname=<domain>&myip=<ip6addr>
Optionaler kombinierter Aufruf für Router mit getrennten Platzhaltern:
https://dyn.example.de/nic/update?hostname=<domain>&myip=<ipaddr>&myipv6=<ip6addr>
Zugangsdaten:
Benutzername: router4711.ddns.example.de
Passwort: das einmalig ausgegebene Token
myipv6 ist eine optionale Erweiterung für Geräte, die IPv4 und IPv6 in einem Aufruf übermitteln. Fehlen beide IP-Parameter, wird die verifizierte Client-Adresse verwendet.
Verwaltung
# Alle Hosts
/opt/ddns/bin/ddns-admin host list
# Nur ein Kunde
/opt/ddns/bin/ddns-admin host list --customer kunde4711
# Details
/opt/ddns/bin/ddns-admin host show router4711.ddns.example.de
# Sperren/freigeben
/opt/ddns/bin/ddns-admin host disable router4711.ddns.example.de
/opt/ddns/bin/ddns-admin host enable router4711.ddns.example.de
# Token sofort ersetzen
/opt/ddns/bin/ddns-admin token rotate router4711.ddns.example.de
# Lokale Zuordnung löschen, DNS bestehen lassen
/opt/ddns/bin/ddns-admin host delete router4711.ddns.example.de --keep-dns
# Lokale Zuordnung und vorhandene A-/AAAA-Records löschen
/opt/ddns/bin/ddns-admin host delete router4711.ddns.example.de --delete-dns
# Audit anzeigen
/opt/ddns/bin/ddns-admin audit list --limit 100
Alle Befehle unterstützen --json, soweit dies für Automatisierung sinnvoll ist.
DynDNS2-Antworten
good IP– DNS-Record geändertnochg IP– Record war bereits aktuellbadauth– Zugangsdaten, Sperrstatus oder Host-Zuordnung ungültignotfqdn– Hostname fehlt911– temporärer Validierungs-, Rate-Limit-, Worker- oder Netcup-Fehler
Backup
Worker passend zum Init-System stoppen, dann sichern:
install -d -m 0700 /var/backups/ddns
cp -a /var/opt/ddns/database/ddns.sqlite* /var/backups/ddns/
cp -a /etc/opt/ddns /var/backups/ddns/config
Danach den Worker wieder starten. Die Secret-Datei und Backups müssen mindestens genauso streng geschützt werden wie die Netcup-Zugangsdaten.
Software-Update
- Datenbank und Konfiguration sichern.
- Neue Version separat entpacken und Tests ausführen.
- Als root
./install.shstarten. - Der Installer erkennt das aktive Init-System automatisch.
- War der Worker aktiv, wird er nach dem Upgrade wieder gestartet.
ddns-admin doctorund einen Testhost prüfen.
Bestehende Dateien unter /etc/opt/ddns werden bei einem Update nicht überschrieben.
Deinstallation
Programmcode und Init-Integration entfernen, Konfiguration und Daten jedoch behalten:
./install.sh --uninstall
Vollständiges Löschen von Konfiguration und SQLite-Daten:
./install.sh --uninstall --purge
In nicht-interaktiven Skripten ist zusätzlich --yes erforderlich:
./install.sh --uninstall --purge --yes
Die Dienstkonten und Gruppen bleiben standardmäßig bestehen, um versehentliche Eingriffe in gemeinsam genutzte Systemkonten zu vermeiden. Nur wenn diese Namen nachweislich ausschließlich von diesem Projekt angelegt und verwendet wurden:
./install.sh --uninstall --purge --remove-users --yes
Eine manuell aus der Beispielvorlage angelegte Apache-VHost-Datei wird nicht automatisch entfernt.
Tests
find opt/ddns -type f \( -name '*.php' -o -path '*/bin/ddns-*' \) -print0 | xargs -0 -n1 php -l
php tests/run.php
sh -n install.sh
sh -n etc/init.d/ddns-worker
systemd-analyze verify etc/systemd/system/ddns-worker.service
Ein Live-Test gegen Netcup sollte ausschließlich mit einer Testdomain beziehungsweise einem eindeutig entbehrlichen Testhost erfolgen.
Bekannte Grenzen
- Keine Weboberfläche und keine Selbstregistrierung
- ASCII-/Punycode-DNS-Namen, keine automatische IDN-Konvertierung
- Netcup kann API-Keys nicht dokumentiert auf einzelne Zonen oder Records beschränken
- Ein separater Netcup-Account nur für die DynDNS-Domain reduziert dieses Restrisiko
Anmerkungen
Dieses Dienstprogramm wurde mit Hilfe generativer KI entwickelt und dient zur Automatisierung dynamischer DNS-Aktualisierungen über die netcup-DNS-API. Es handelt sich um ein eigenständiges Projekt, das weder mit netcup in Verbindung steht noch von netcup unterstützt wird.
