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_prefork mit mod_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

netcup-ddns-1.1.1.tar.gz

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:

  1. prüft PHP und Erweiterungen,
  2. erstellt ddns-worker und die gemeinsame Socket-Gruppe ddns-update,
  3. fügt www-data und ddns-worker der Socket-Gruppe hinzu,
  4. installiert Code und die passende Init-Integration,
  5. erzeugt bei einer Erstinstallation einen zufälligen Token-Pepper,
  6. überschreibt bestehende Konfigurationen nicht,
  7. 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:

3. Apache

etc/apache2/sites-available/ddns.conf.example als Vorlage verwenden.

  • Darin eine gültige HTTPS-URL im Router konfigurieren
  • /nic/update auf Port 80 ablehnen, nicht erst nach Übertragung umleiten
  • Access-Log mit %U, nicht mit %r oder %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ändert
  • nochg IP – Record war bereits aktuell
  • badauth – Zugangsdaten, Sperrstatus oder Host-Zuordnung ungültig
  • notfqdn – Hostname fehlt
  • 911 – 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

  1. Datenbank und Konfiguration sichern.
  2. Neue Version separat entpacken und Tests ausführen.
  3. Als root ./install.sh starten.
  4. Der Installer erkennt das aktive Init-System automatisch.
  5. War der Worker aktiv, wird er nach dem Upgrade wieder gestartet.
  6. ddns-admin doctor und 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.