Der DNS-Server PowerDNS war bereits Gegenstand mehrerer Artikel hier im Blog. Und er läuft problemlos seit der Einrichtung und dem Deployment vor einiger Zeit, verrichtet unauffällig seine Dienste, was für alle Komponenten gilt – PowerDNS Authoritative Server, PowerDNS Recursor und dnsdist. Insofern lasse ich fast alle Domains von PowerDNS auflösen. Warum nur fast? Ganz einfach, für das beliebte “dynamische” DNS, also der Möglichkeit, einen Hostnamen für die jeweilige IP-Adresse, die einem der Zugangsprovider für den heimischen Internet-Zugang zur Verfügung stellt, zuzuweisen, habe ich bislang auf externe Dienste zurück gegriffen. Denn beim Wechsel der IP-Adresse – standardmäßig etwa bei DSL-Providern spätestens alle 24 Stunden – muss der DNS-Server benachrichtigt werden und die neue IP-Adresse möglichst zeitnah erhalten und an den bzw. die Secondary-DNS-Server verteilen.
Dynamic DNS damals und heute
Früher gab es etliche kostenlose Dynamic DNS Dienste, deren Manko war, dass sich damit keine eigenen Domains nutzen ließen. Zwar gibt es inzwischen Domain-Anbieter, die ebenfalls Dynamic DNS bereitstellen, andererseits werden DNS-Dienste auch weiterhin kostenlos bereit gestellt, etwa von Hurricane Electric oder DigitalOcean, aber wenn diese genutzt werden, obliegt die gesamte DNS-Verwaltung eben einem dieser Anbieter. Genau dies zu vermeiden, war jedoch eines der Ziele des Einsatzes von PowerDNS auf eigenen Servern bzw. VMs. Somit musste eine Lösung für Dynamic DNS mit dem PowerDNS Authoritative Server her.
Ein Blick in die Dokumentation verrät, dass PowerDNS dafür bereits Lösungen anbietet. Eine “Dynamic DNS Update” genannte Möglichkeit, die in RFC2136 definiert wird, und ein Update von Zonendaten mittels eigener HTTP API. Beides scheint – und das sei meine absolut eigene, subjektive Meinung – recht aufwändig zu sein. Beispielsweise lassen sich in der FRITZ!Box unterschiedliche Anbieter von Dynamic DNS Diensten konfigurieren, ebenso wie bei der hier genutzten Firewall-Appliance-Distribution OPNsense. Bei beiden habe ich zumindest bis zum aktuellen Zeitpunkt keinen RFC2136-Client gefunden. Und ddclient unterstützt mittels “nsupdate”-Protokoll einen Wrapper um das Kommandozeilentool namens nsupdate. Ebenso will PowerDNS dafür konfiguriert, für die betreffenden Domains müssen Keys generiert werden und spätestens beim Blick in das in Lua geschriebene Beispiel-Skript habe ich erstmal aufgegeben und nach Alternativen gesucht. Eine solche schien auf den ersten Blick die HTTP API von PowerDNS zu sein, mit der sich alle Zonendaten manipulieren, Domains anlegen lassen usw.. Die Admin-UI PowerDNS-Admin bedient sich genau dieser API, insofern besteht kein Zweifel an der Funktionalität der API. Genau darin liegt jedoch auch ein Problem – die API ist sehr mächtig, es lassen sich alle Daten ändern, es gibt Zonen-Objekte, Resource Record Sets, Records usw., was eine gewisse Komplexität mit sich bringt, und nicht zuletzt lassen sich alle diese Daten aller Domains, die vom jeweiligen PowerDNS-Server verwaltet werden, ändern, sofern man im Besitz des API-Keys ist. Und leider wird die PowerDNS-API weder von der FRITZ!Box, noch OPNsense und ebenfalls von ddclient nicht unterstützt. Da ich mir jedoch sicher war, dass es bereits einfachere Lösungen geben musste, die beispielsweise mit einem einfachen HTTP(S)-Request die Änderung der IP-Adresse für einen Hostnamen anstoßen, habe ich erstmal die Lieblings-Suchmaschine angeworfen und mir die Ergebnisse näher betrachtet.
Eigener Dynamic DNS Dienst “für den Hausgebrauch”
Und natürlich fanden sich einige Artikel, sogar die c’t beschäftigte sich vor einigen Jahren mit einer entsprechenden Lösung. Es stellten sich einige Gemeinsamkeiten heraus: Einerseits bedienten sich nahezu alle der vorgestellten Ansätze nicht der PowerDNS-API, sondern aktualisierten die Domain-Daten direkt in der zugrunde liegenden Datenbank, meist MySQL, aber auch SQLite kam zum Einsatz. Andererseits basierten die Lösungen auf PHP, Perl, oder sogar Shell-Skripten. Wenige Zeilen Programmcode genügten, um den Request entgegenzunehmen, die übermittelten Daten zu prüfen und anschließend den Host-Eintrag in der Datenbank zu ändern. Insofern schien diese direkte Änderung in der Datenbank anstatt die API zu nutzen ein durchaus gangbarer Weg zu sein. Mein erster Gedanke war somit, diese Idee zu übernehmen und ebenfalls PHP zu nutzen.
Dynamic DNS Service – Anforderungen
Nicht, dass die gefundenen Lösungen schlecht gewesen wären, aber aus meiner Sicht sollten folgende Anforderungen erfüllt werden:
- Update erfolgt durch HTTP GET-Request, Authentifizierung mittels API-Key
- der API-Key soll nicht global gültig sein, sondern nur jeweils für eine Domain
- die zu aktualisierenden Hostnamen sollen in einer Allowlist/Positivliste Platz finden, so dass nur einzeln erlaubte Hostnamen geändert werden können
- alle Elemente wie Host, Domain, API-Key sollen nicht hart codiert vorliegen, sondern flexibel konfiguriert werden können, wofür sich wiederum eine Datenbank empfiehlt
Docker und PHP, oder nicht PHP..?
Nun blieb die Frage – PHP oder vielleicht doch nicht? Und wenn ja, welches (Micro-)Framework? Oder der Einfachheit halber gar keines? Dazu kam, dass auf den für DNS genutzten Servern bereits alle Dienste “dockerized” sind, d.h. entweder als einfache Docker-Container oder als Docker-Services laufen. Die Dynamic-DNS-API sollte dabei keine Ausnahme machen, insofern als eigenständiger Dienst laufen, anstatt sich eines zentralen Web-Servers zu bedienen. Die paar Zeilen PHP hätten somit einen Docker-Container benötigt, in dem sich nicht nur das PHP-Skript befindet, sondern auch das PHP-FPM-Modul, diverse Libraries und nicht zu vergessen der Web-Server (die Nutzung des PHP-internen “Development”-Servers lassen wir mal außen vor). Alles müsste entsprechend konfiguriert werden usw., insgesamt eine Menge Overhead für eine sehr kleine Anwendung, die nur einen einzigen HTTP-Request zur Verfügung stellen soll. In diesem Kontext erinnerte ich mich an eine Aussage aus dem Golang Meetup, das ich vor einiger und in der Vor-Corona-Zeit besucht hatte. Denn das Ergebnis des Kompilierens mit dem Standard-Go-Compiler ist ein statisch gelinktes Binary, das einfach in ein Docker-Image verfrachtet und gestartet werden kann…
Dynpower – Neuland mit Golang
Da mir die Idee immer mehr gefiel und mich seit geraumer Zeit einmal mit Go beschäftigen wollte, schien mir diese Aufgabe für den Einstieg genau richtig. So ist dynpower entstanden – ein Server, der gemäß o.g. Anforderungen einen Request zwecks Update der IP-Adresse entgegen nimmt und den entsprechenden Host-Eintrag in der PowerDNS-Datenbank aktualisiert. Und da ich die Domains mit API-Key und deren Host nicht manuell in der dynpower-Datenbank eintragen wollte, gibt es zur Verwaltung dieser Einträge noch eine Anwendung für die Kommandozeile: dynpower-cli. Beides zusammen gibt es fertig zum Einsatz in einem Docker-Image: geschke/dynpower. Und Letzteres umfasst momentan gerade einmal knapp 16 MB…
Ein Disclaimer sei mir erlaubt – der Code zeigt sehr anschaulich meine ersten Gehversuche mit Go. Vermutlich werden sich gestandene Go-Programmierer die Haare raufen bei der Betrachtung, und genau das kann ich absolut nachvollziehen, ich bitte dafür um Verständnis. Sehr wahrscheinlich werde ich den Code auch noch mehrfach überarbeiten, Teile entfernen, ersetzen, hinzufügen oder ähnliches. Beispielsweise hatte ich die Parameter der CLI zunächst vom flag-Package parsen lassen, um dann auf die wesentlich leistungsfähigere Cobra-Library zu wechseln. Trotzdem wollte ich die Lösung insgesamt veröffentlichen und hiermit beschreiben, da sie sich von den bis dazu gefundenen Ansätzen doch ein wenig unterscheidet.
Konfiguration von dynpower
Voraussetzungen
Im Folgenden nun Hinweise zur Konfiguration, wie sie hier im Einsatz ist. Als Voraussetzung dient PowerDNS Authoritative Server mit einem MySQL-Backend zur Speicherung der Domain-Daten. Dynpower unterstützt momentan ausschließlich MySQL bzw. MariaDB. Auf den Betrieb außerhalb von Docker gehe ich nicht weiter ein, aber natürlich wäre es auch möglich, dynpower etwa als systemd-Dienst laufen zu lassen, einen Nginx-Proxy für https davor zu schalten usw.. In jedem Fall muss dynpower auf die PowerDNS-MySQL-Datenbank zugreifen können. Ebenfalls benötigt dynpower eine eigene Datenbank, diese kann sich auf derselben Instanz wie die PowerDNS-Datenbank befinden, muss es aber nicht.
Datenbank-Import
Da das Docker-Image von MariaDB einen SQL-Dump beim ersten Start automatisch importieren kann, wird im folgenden Beispiel davon Gebrauch gemacht. Der SQL-Dump befindet sich auf GitHub unter sql/synpower.sql. Aufgrund der einfacheren Konfiguration und eindeutigen Trennung soll dynpower eine eigene Datenbank-Instanz erhalten. Auf die Verzeichnisstruktur wurde im ersten Teil eingegangen, für die neue Datenbank kann darin ein entsprechendes Verzeichnis angelegt werden:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ tree . ├── dnsdist │ ├── dnsdist.conf ├── mariadb_dynpower │ ├── data [...] │ └── sql │ └── dynpower.sql ├── mariadb_powerdns [...] ├── mariadb_powerdnsadmin [...] ├── ns1.xyzcdn.xyz.compose.yml └── recursor └── forward_zones
Die Dump-Datei dynpower.sql
wurde hier in das neue Verzeichnis mariadb_dynpower/sql/
gelegt. Beim ersten Start des MariaDB-Containers wird die Datei einfach importiert, dazu muss das Verzeichnis innerhalb des Containers nach /docker-entrypoint-initdb.d
gemountet werden.
Docker-Compose-File für dynpower
Dynpower braucht Zugriff auf den PowerDNS-MariaDB-Container, der den MySQL-Port jedoch nicht nach außen zur Verfügung stellt. Das Docker-interne Netzwerk für alle PowerDNS-relevanten Container wurde im Docker-Compose-File definiert. Da ich an dieser Konfiguration nichts ändern wollte, wurde das Docker-Compose-File einfach um die Services für dynpower ergänzt:
version: '3.7' services: [...] mariadb_dynpower: image: mariadb:latest restart: always volumes: - ./mariadb_dynpower/data:/var/lib/mysql - ./mariadb_dynpower/sql:/docker-entrypoint-initdb.d environment: MYSQL_ROOT_PASSWORD: <Root Passwort> MYSQL_DATABASE: dynpower MYSQL_USER: dynuser MYSQL_PASSWORD: <Datenbank Passwort> networks: dns_net: ipv4_address: 172.30.1.100 dynpower: image: geschke/dynpower restart: always environment: DBHOST: mariadb_dynpower DBUSER: dynuser DBNAME: dynpower DBPASSWORD: <Datenbank Passwort> PDNS_DBHOST: mariadb_powerdns PDNS_DBUSER: powerdnsuser PDNS_DBNAME: powerdns PDNS_DBPASSWORD: <Passwort von mariadb_powerdns> labels: - "traefik.enable=true" - "traefik.docker.network=traefik-public" - "traefik.constraint-label=traefik-public" - "traefik.http.routers.ns1xyzcdnxyzdynapi.service=ns1xyzcdnxyzdynapi-secured" - "traefik.http.routers.ns1xyzcdnxyzdynapi.rule=Host(`ns1.xyzcdn.xyz`) && PathPrefix(`/dynapi`)" - "traefik.http.routers.ns1xyzcdnxyzdynapi.priority=20" - "traefik.http.routers.ns1xyzcdnxyzdynapi.entrypoints=http" - "traefik.http.middlewares.def-ns1xyzcdnxyzdynapi.headers.customrequestheaders.X-Forwarded-Ssl=on" - "traefik.http.middlewares.def-ns1xyzcdnxyzdynapi.headers.customrequestheaders.X-Forwarded-Server=ns1.xyzcdn.xyz" - "traefik.http.middlewares.def-ns1xyzcdnxyzdynapi.headers.referrerPolicy=origin" - "traefik.http.middlewares.def-ns1xyzcdnxyzdynapi-strip.stripprefix.prefixes=/dynapi" - "traefik.http.routers.ns1xyzcdnxyzdynapi.middlewares=https-redirect" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.service=ns1xyzcdnxyzdynapi-secured" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.rule=Host(`ns1.xyzcdn.xyz`) && PathPrefix(`/dynapi`)" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.priority=20" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.entrypoints=https" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.tls.certresolver=le" - "traefik.http.services.ns1xyzcdnxyzdynapi-secured.loadbalancer.server.port=8080" - "traefik.http.routers.ns1xyzcdnxyzdynapi-secured.middlewares=secHeaders@file,def-ns1xyzcdnxyzdynapi-strip,def-ns1xyzcdnxyzdynapi,def-compress" networks: traefik-public: dns_net: ipv4_address: 172.30.1.101
Im Unterschied zur bisherigen Variante mit Nginx-Proxy-Container kommt auf dem Server inzwischen der Traefik Proxy zum Einsatz, auf dessen Konfiguration ich nur rudimentär eingehen möchte, evtl. mehr dazu in einem späteren Artikel.
Die Service-Beschreibung von mariadb_dynpower
sieht den anderen Datenbank-Instanzen sehr ähnlich. Zusätzlich zu der Definition der anzulegenden Datenbank namens dynpower
, des Users dynuser
und den entsprechenden Root- und Datenbank-Passwörtern wird das Verzeichnis, in dem sich das Dump-File befindet, in das entsprechende Verzeichnis /docker-entrypoint-initdb.d
innerhalb des Containers gemountet. Beim ersten Start wird die Datenbank mit den angegebenen Parametern angelegt und alle Dateien, die bestimmte Endungen tragen, importiert bzw. ausgeführt. Danach können sowohl die Dump-Datei als auch der Mount gelöscht werden.
Dynpower-Konfiguration mittels Environment-Variablen
Die Definition von dynpower besteht größtenteils aus dem Setzen der richtigen Umgebungsvariablen. Dies wären einerseits die Verbindungsparameter für den zuvor definierten MariaDB-Service mariadb_dynpower und andererseits die Parameter für den Zugriff auf die Datenbank von PowerDNS.
Traefik ersetzt Nginx & Co(mpanion)
Mit der Vielzahl von Labels wird der Traefik Proxy konfiguriert. Traefik dient als zentraler Proxy für alle auf dem Server befindlichen Services. Traefik sorgt dabei für das Routing und für die verschlüsselte Kommunikation mittels HTTPS. Ähnlich wie beim Zusammenspiel der Container Nginx-Proxy und Nginx-Proxy-Companion wird das Let’s-Encrypt-Zertifikat automatisch für den entsprechenden Host eingerichtet. Das Routing lässt sich vielfältig konfigurieren, im o.g. Beispiel sollte für die dynpower-API kein weiterer Hostname definiert werden, sondern alle DNS-relevanten Services vereint werden unter ns1.xyzcdn.xyz. Standardmäßig – und bislang ohne Möglichkeit einer Änderung – ist die dynpower-API unter dem Pfad /api/
erreichbar. Die PathPrefix-Regel von Traefik sorgt nun dafür, dass dynpower unter genau diesem Pfad erreichbar ist. Da die URL normalerweise komplett durchgereicht wird, würde dynpower ebenfalls die Pfadangabe /dynapi/api/...
erhalten. Daher kommt die Traefik-Middleware StripPrefix zum Einsatz, die den Teil /dynapi
vor dem Weiterleiten entfernt, so dass dynpower nur die jeweils relevanten Teile der URL erhält. Des Weiteren wird der Port 8080 angesprochen, da dynpower aktuell ausschließlich diesen Port bedient.
Start von dynpower
Zum Start kommt wie gewohnt docker-compose zum Einsatz.
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker-compose -f ns1.xyzcdn.xyz.compose.yml up -d
Der erste Start dauert angesichts des Anlegens der Datenbank etwas länger, aber danach sollte sich der Container in der Liste zeigen. Der dynpower-Server wurde somit gestartet, nun fehlen noch die Domain- und Host-Einträge.
Eintragen des Hosts in PowerDNS
Da PowerDNS und dynpower unabhängig voneinander agieren, d.h. dynpower nur für die Aktualisierung des IP-Adresse in der PowerDNS-Datenbank sorgt, dort aber ansonsten keine Änderungen vornimmt, muss – falls noch nicht geschehen – ein entsprechender Hostname für die Verwendung von Dynamic DNS eingetragen werden. Beispielsweise könnte für eine Domain “example.com” ein Host “home” verwendet werden. Dabei muss eine kurze TTL (Time To Live) gesetzt werden, PowerDNS-Admin bietet in der Default-Auswahl beispielsweise fünf Minuten (300 Sekunden) an, kürzere Werte sind ebenfalls möglich. Eine klare Empfehlung habe ich nicht gefunden, die Wikipedia gibt hingegen an, dass bei dynamischen DNS-Diensten meist eine TTL von einer Minute verwendet wird.
Konfiguration mittels dynpower-cli
Für das Eintragen von Domains und Hosts in die dynpower-eigene Datenbank kommt die CLI-Anwendung zum Einsatz, die einfach innerhalb des laufenden Containers gestartet werden kann. Da dabei die Environment-Variablen zum Einsatz kommen, die beim Start des Containers definiert wurden, genügt es, dynpower-cli
als Kommando anzugeben:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli dynpower-cli is a small helper tool to manage the dynpower database. Usage: dynpower-cli [command] Available Commands: domain Manage domain entries encrypt Encrypt access key string to enter into database table. help Help about any command host Manage host entries Flags: -h, --help help for dynpower-cli Use "dynpower-cli [command] --help" for more information about a command.
Zunächst wird eine Domain angelegt, dabei nimmt das add-Kommando den Domainnamen sowie einen selbst gewählten API-Key entgegen:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli domain add example.com mein_ganz_toller_api_key Domain example.com added successfully
Achtung! Der API-Key wird verschlüsselt (bcrypt) in der Datenbank abgelegt, daher besteht keine Möglichkeit, diesen nachträglich wieder im Klartext sichtbar zu machen. Insofern empfiehlt es sich, den API-Key spätestens an dieser Stelle zu notieren.
Zur Prüfung des Erfolgs kann die Liste der vorhandenen Domains angezeigt werden:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli domain list Domains in database: Domain Created Updated example.com 2020-10-09 14:38:02 2020-10-09 14:38:02 gncdn.xyz 2020-10-05 01:32:31 2020-10-05 01:32:31
Nun fehlt noch ein entsprechender Host für die soeben angelegte Domain:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli host list example.com No host entry for domain example.com found.
Wie wäre es mit “home.example.com”?
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli host add example.com home Host home.example.com added successfully
Zur Überprüfung kann die Liste der eingetragenen Hosts für eine Domain angezeigt werden:
geschke@stralsund:~/services/ns1.xyzcdn.xyz$ docker exec -it ns1xyzcdnxyz_dynpower_1 dynpower-cli host list example.com Hosts in database: Host Created Updated home.example.com 2020-10-09 14:42:24 2020-10-09 14:42:24
Damit ist dynpower bereits fertig konfiguriert. Weitere Domains und Hosts können jederzeit hinzugefügt und natürlich auch wieder entfernt werden. Dabei bezieht sich das Hinzufügen oder Entfernen nur auf die dynpower-Datenbank, bei PowerDNS selbst wird nichts geändert!
Der Update-Request
Die URL zur Aktualisierung der Domain sieht nun wie folgt aus:
https://meinserver.tld/dynapi/api/update?key=mein_ganz_toller_api_key&host=home&domain=example.com
Die IP-Adresse wird standardmäßig automatisch ermittelt, kann aber auch mit einem weiteren Parameter direkt übermittelt werden, indem “ip=123.456.789.123” hinzugefügt wird. Weitere Pfade und damit Funktionen gibt es nicht, per HTTP-Request ist somit nur das Update eines bereits konfigurierten und existierenden Hosts möglich.
Wie geht es weiter?
Da dynpower gerade frisch geschlüpft ist, gibt es natürlich noch viel Raum für Verbesserungen und Ergänzungen, ein Problem ist mir beispielsweise beim Schreiben dieses Artikels aufgefallen… So läuft der dynpower-Server hier auch erst seit wenigen Tagen testweise und wird vom Dynamic-DNS-Aktualisierungsdienst von OPNsense verwendet – das funktioniert bislang ohne Probleme. Die Logs sind momentan auch noch sehr gesprächig, auch dies ist ein Punkt, der noch korrigiert werden muss. Und nicht zuletzt muss ich noch an meinen Go-Skills schrauben… Wer trotzdem gerne testen möchte, sei herzlich eingeladen, über Kommentare, Hinweise, Bug-Reports oder ähnliches würde ich mich natürlich sehr freuen!
Update vom 15.11.2020 – Traefik Magic
Bekanntlich lernt man nie aus und so erging es auch mir jüngst mit Traefik. Tatsächlich waren im hier verwendeten Docker-Compose-File zwei Zeilen enthalten, die völlig überflüssig waren. Zwar haben sich damit keine negativen Auswirkungen ergeben, aber eben auch keine positiven, soll heißen, so wie sie enthalten waren, waren sie eben sehr sinnlos. Es handelt sich um folgende Definitionen, die nun im ursprünglichen Beitrag gelöscht sind:
- "traefik.http.middlewares.ns1xyzcdnxyzdynapi.redirectscheme.scheme=https" - "traefik.http.middlewares.ns1xyzcdnxyzdynapi.redirectscheme.permanent=true"
Denn natürlich habe auch ich zunächst nach Beispielen in anderen Blog-Beiträgen, der Dokumentation etc. gesucht, die Konfiguration ein wenig angepasst und schließlich ausprobiert. Betrachtet man die beiden Zeilen, könnte einem die Idee kommen, dass sie eine Umleitung vom Protokoll http auf https bewirken. Das wäre auch der Fall, wenn die hierdurch definierte Middleware richtig eingesetzt geworden wäre.
Die Syntax ist in der Form – für mich zumindest – ein wenig gewöhnungsbedürftig, obwohl jedes YAML-File identisch aufgebaut ist. Die folgenden Beispiele sind der Dokumentation der Middleware RedirectScheme entnommen. Zum Vergleich der betreffende Abschnitt in YAML-Format:
# Redirect to https http: middlewares: test-redirectscheme: redirectScheme: scheme: https permanent: true
Bei der Verwendung als Docker-Label wären dies folgende Zeilen:
# Redirect to https labels: - "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https" - "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
In beiden Fällen wird eine Middleware mit dem Namen “test-redirectscheme
” definiert, die eine permanente Weiterleitung von http auf https bewirkt. Alleine für sich bewirkt jene Definition jedoch nichts, da die verwendeten Middlewares dem Router “übergeben” werden müssen, was in folgender Zeile geschieht:
- "traefik.http.routers.ns1xyzcdnxyzdynapi.middlewares=https-redirect"
Doch dieser Router (mit dem Namen “ns1xyzcdnxyzdynapi
“) erhält im Beispiel nur eine Middleware, und zwar diejenige mit dem Namen “https-redirect
“. Diese muss natürlich ebenfalls irgendwo definiert werden, und genau das passiert auch, und zwar in der Service-Definition im Docker-Compose-File von Traefik selbst. Somit kann diese global definierte Middleware überall verwendet werden, was einem ein wenig Schreibarbeit erspart.
Zur anfänglichen Verwirrung beigetragen haben dürfte die Tatsache, dass diese beiden Schritte des Definierens und anschließenden Verwendens nicht bei jeder Komponente von Traefik notwendig sind. Zum Beispiel bei der Router-Konfiguration wie folgt:
- "traefik.http.routers.ns1xyzcdnxyzdynapi.rule=Host(`ns1.xyzcdn.xyz`) && PathPrefix(`/dynapi`)"
Zum vollständigen Beispiel siehe weiter oben. Hier wird ein Teil des Routers mit dem Namen “ns1xyzcdnxyzdynapi
” konfiguriert. Der Router mit besagtem Namen ist umgehend nach dem Start des Containers in Traefik zu finden, was sich sehr schön im Traefik-Dashboard überprüfen lässt. Es ist jedoch nicht notwendig, diese Router-Definition bzw. deren Namen als eine Art Variable an anderer Stelle einzusetzen. Vermutlich sehen die Entwickler von Traefik Router als zentrale Komponente an, weshalb auf diesen Schritt verzichtet wurde. Dazu kommt, dass Router, Services, Middlewares und SSL-Zertifikate zum Teil der “dynamischen” Konfiguration gehören. Diese Komponenten lassen sich während der Laufzeit von Traefik hinzufügen bzw. entfernen.
Nicht unbedingt konsistenter ist auch die Verwendung von Services. Falls eine Service-Definition vorhanden ist, wird diese genutzt, auch wenn sie nicht dem Router hinzugefügt wurde oder falls sie einen anderen Namen tragen sollte als der Router. Fehlt eine Service-Definition, legt Traefik selbst eine solche an und verwendet dabei Port 80. Aber vielleicht liege ich mit meiner Einschätzung auch ganz falsch und das Traefik verhält sich einfach nur “magisch“…
Interessant. Der Meinung bin auch auch:
> ein Update von Zonendaten mittels eigener HTTP API … scheint … recht aufwändig zu sein