· DevOps  · 9 Min. Lesezeit

Wildcard-Zertifikate mit Traefik und DNS-01, und die Fallen dabei

Ein Wildcard-Zertifikat für beliebig viele Subdomains, vollständig automatisiert über DNS-01. Der komplette Weg mit Prüfbefehl nach jedem Schritt, dazu die vier Stolpersteine, deren Symptome nicht auf ihre Ursache zeigen.

Ein Wildcard-Zertifikat für beliebig viele Subdomains, vollständig automatisiert über DNS-01. Der komplette Weg mit Prüfbefehl nach jedem Schritt, dazu die vier Stolpersteine, deren Symptome nicht auf ihre Ursache zeigen.

Wer ein Dutzend Dienste auf einer IP betreibt, verwaltet mit HTTP-01 ein Dutzend einzelne Zertifikate. Das funktioniert, hat aber zwei Nachteile. Jeder neue Dienst braucht einen eigenen Ausstellungsvorgang über Port 80, und jedes ausgestellte Zertifikat trägt seinen vollständigen Hostnamen in die öffentlichen Certificate-Transparency-Logs ein. Interne Dienstnamen sind damit für jeden nachlesbar.

Ein Wildcard-Zertifikat auf *.lab.rz.jabi-it.de löst beides. Neue Subdomains sind sofort abgedeckt, ohne dass irgendetwas neu ausgestellt werden muss, und im Transparency-Log steht nur der Stern, nicht der Name dahinter.

Dafür muss die Ausstellung auf DNS-01 umgestellt werden. Dieser Beitrag zeigt den vollständigen Weg mit einem Prüfbefehl nach jedem Schritt, und danach die vier Stolpersteine, deren Symptome nicht auf ihre Ursache zeigen. Wer die HTTP-01-Grundlagen sucht, findet sie im Beitrag Zertifikate mit Traefik und Let’s Encrypt.

HTTP-01, DNS-01 oder TLS-ALPN-01

Bevor es an die Umstellung geht, die Entscheidung dahinter. Let’s Encrypt kennt drei Prüfverfahren, und DNS-01 ist nicht pauschal das bessere. Es ist das einzige, das Wildcards kann, und es hat dafür einen Preis.

HTTP-01DNS-01TLS-ALPN-01
Nachweis überDatei auf Port 80TXT-Eintrag in der ZoneTLS-Handshake auf Port 443
Wildcard möglichneinjanein
Server muss öffentlich erreichbar seinjaneinja
Zugangsdaten auf dem Server nötigneinja, DNS-APInein
Hostname im Transparency-Logvollständigbeim Wildcard nur der Sternvollständig
Client-Unterstützungsehr breitbreiteingeschränkt

Die Angaben stammen aus der Let’s-Encrypt-Doku zu den Challenge-Typen. Der inhaltliche Kern des Unterschieds: HTTP-01 und TLS-ALPN-01 weisen die Kontrolle über einen Host nach, DNS-01 die Kontrolle über die gesamte Zone. Nur der zweite Nachweis rechtfertigt ein Zertifikat für alle Subdomains.

Wann HTTP-01 die richtige Wahl bleibt

Bei einer überschaubaren Zahl öffentlich erreichbarer Hosts ist HTTP-01 das schlankere Verfahren. Es braucht kein Geheimnis auf dem Server, es funktioniert bei jedem DNS-Anbieter, und es ist der am breitesten unterstützte Weg. Wer drei Dienste betreibt, gewinnt durch ein Wildcard nichts und handelt sich nur zusätzliche Abhängigkeiten ein.

Dazu kommt ein Punkt, den man beim Wildcard leicht übersieht: Es bündelt Risiko. Ein Wildcard-Schlüssel deckt jede Subdomain ab. Wer ihn erbeutet, kann sich als jeder Dienst unter dieser Domain ausgeben. Einzelzertifikate lassen sich dagegen gezielt einzeln zurückziehen. Für Hosts mit unterschiedlichem Schutzbedarf ist die Trennung ein Vorteil und kein Umstand.

Wann TLS-ALPN-01 passt

Immer dann, wenn Port 80 nicht zur Verfügung steht oder bewusst geschlossen bleibt, aber kein Wildcard gebraucht wird. Die Prüfung läuft vollständig über Port 443 auf der TLS-Ebene. Der Nachteil laut Let’s Encrypt ist die geringere Verbreitung unter den ACME-Clients. Traefik beherrscht es, viele kleinere Werkzeuge nicht.

Wann DNS-01 sich lohnt

Drei Konstellationen sprechen klar dafür.

Sie brauchen ein Wildcard. Das ist der offensichtliche Fall, und es ist der einzige Weg dorthin.

Der Dienst ist nicht öffentlich erreichbar. DNS-01 ist das einzige Verfahren, das ohne eingehende Verbindung von außen auskommt. Für interne Dienste hinter einer Firewall gibt es keine Alternative, weil Let’s Encrypt für HTTP-01 und TLS-ALPN-01 den Host tatsächlich erreichen muss.

Sie betreiben viele Subdomains. Let’s Encrypt begrenzt die Ausstellung auf 50 Zertifikate je registrierter Domain in sieben Tagen, und auf 5 Zertifikate für denselben Satz an Namen im selben Zeitraum, nachzulesen in der Doku zu den Rate-Limits. Wer zwei Dutzend Subdomains einzeln ausstellt und dabei einmal in eine Neuinstallations-Schleife gerät, steht schneller vor der Grenze als erwartet. Ein Wildcard ist ein einziges Zertifikat und nimmt diesen Druck vollständig heraus.

Der Preis ist in allen drei Fällen derselbe, und Let’s Encrypt benennt ihn in der eigenen Doku deutlich: Zugangsdaten für die DNS-Zone auf dem Server vorzuhalten ist ein Risiko. Wie ich damit umgehe, steht weiter unten unter Grenzen von DNS-01.

Daraus folgt die zentrale Voraussetzung für alles Weitere: Traefik braucht schreibenden API-Zugriff auf Ihre DNS-Zone.

Voraussetzungen prüfen, bevor Sie etwas ändern

Zwei Prüfungen vorab ersparen den größten Teil der späteren Fehlersuche.

Traefik-Version. Traefik nutzt intern die Bibliothek lego für ACME. Welche Provider-APIs unterstützt werden, hängt an der lego-Version und damit an der Traefik-Version. Die aktuelle Hetzner-API setzt lego 4.27 voraus, was Traefik 3.5.4 entspricht. Aktueller stabiler Stand ist zum Zeitpunkt dieses Beitrags Traefik 3.7.8.

docker exec traefik traefik version

Ist die Version älter, aktualisieren Sie zuerst. Eine zu alte lego-Version erzeugt Authentifizierungsfehler, die wie ein falsches Token aussehen, aber keines sind.

Der richtige API-Token. Hetzner hat DNS in die Cloud-Console überführt. Die alte Adresse dns.hetzner.com/api/v1 antwortet mit einem 301, die Tokens aus der alten DNS-Console sind wirkungslos. Maßgeblich ist heute api.hetzner.cloud/v1 mit einem Cloud-API-Token und Bearer-Authentifizierung.

Beide Token-Arten sehen gleich aus, unterscheiden sich aber in der Länge. Der alte DNS-Token hat 32 Zeichen, der gültige Cloud-Token 64. Verlassen Sie sich nicht auf die Optik, sondern testen Sie:

curl -s -H "Authorization: Bearer $HETZNER_API_TOKEN" \
  https://api.hetzner.cloud/v1/zones | jq -r '.zones[].name'

Erwartet wird die Liste Ihrer Zonen. Kommt ein 401 oder eine leere Liste, ist der Token falsch oder hat keine Rechte auf die Zone. Erst wenn dieser Befehl sauber durchläuft, lohnt sich der nächste Schritt.

Schritt 1: Den Token bereitstellen

Der Token gehört als Umgebungsvariable in die Compose-Datei, nicht in die Router-Labels und nicht ins Git:

services:
  traefik:
    environment:
      - HETZNER_API_TOKEN=${HETZNER_API_TOKEN}

Den erwarteten Variablennamen legt der lego-Provider fest, für Hetzner ist er in der lego-Providerdoku dokumentiert. Bei anderen Anbietern heißt er anders, die Systematik bleibt gleich.

Schritt 2: Den Resolver auf DNS-01 umstellen

In der statischen Konfiguration wird aus dem httpChallenge ein dnsChallenge mit Provider. Den Pfad des Zertifikatsspeichers behalten Sie bei, sonst verlieren Sie die bestehenden Zertifikate und stellen alles unnötig neu aus.

certificatesResolvers:
  hetzner:
    acme:
      email: acme@ihre-domain.de
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: hetzner

Die vollständigen Optionen stehen in der Traefik-Doku zu ACME Certificate Resolvers. Relevant ist dort vor allem delayBeforeChecks, wenn Ihr Anbieter TXT-Einträge langsam über seine Nameserver verteilt.

Vor der Änderung ein datiertes Backup, damit ein Rückweg in einem Schritt möglich bleibt:

cp -a ~/traefik ~/traefik-backup-$(date +%Y%m%d_%H%M%S)

Schritt 3: Die Konfiguration wirksam machen

Liegt traefik.yml als einzelne Datei im Bind-Mount, ziehen Sie den Container hart neu. Ein einfacher Restart genügt hier nicht zuverlässig, warum das so ist steht weiter unten unter Stolperstein 4.

docker compose up -d --force-recreate traefik

Danach den ACME-Teil der Logs prüfen:

docker compose logs --tail=100 traefik | grep -i acme

Schritt 4: Die Router auf das Wildcard umstellen

Traefik fordert Wildcards nicht von selbst an. Sie deklarieren pro Router explizit, welches Zertifikat gelten soll:

labels:
  - "traefik.http.routers.app.tls.certresolver=hetzner"
  - "traefik.http.routers.app.tls.domains[0].main=*.lab.rz.jabi-it.de"
  - "traefik.http.routers.app.tls.domains[0].sans=lab.rz.jabi-it.de"

main ist das Wildcard, sans nimmt die nackte Domain dazu. Der sans-Eintrag ist keine Kür: ein Wildcard deckt genau eine Ebene ab, lab.rz.jabi-it.de selbst gehört nicht dazu. Ohne ihn liefert der Aufruf ohne Subdomain einen Zertifikatsfehler.

Schritt 5: Das Ergebnis prüfen

Verlassen Sie sich nicht auf die Logzeile, sondern sehen Sie nach, was tatsächlich über die Leitung geht:

openssl s_client -connect dienst.lab.rz.jabi-it.de:443 -servername dienst.lab.rz.jabi-it.de </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

In der SAN-Liste muss *.lab.rz.jabi-it.de stehen. Steht dort weiterhin der einzelne Hostname, greift Stolperstein 1.

Die vier Stolpersteine

Alle vier haben gemeinsam, dass ihr Symptom nicht auf die Ursache zeigt. Wer sie kennt, spart sich die Suche an der falschen Stelle.

1. Bestehende exakte Zertifikate haben Vorrang

Symptom: Nach der Umstellung ändert sich nichts, es wird weiter das alte Zertifikat pro Host ausgeliefert.

Ursache: Traefik bevorzugt ein vorhandenes exaktes Zertifikat gegenüber einem passenden Wildcard. Die alten Einträge laufen bis zum Ablauf weiter.

Prüfung: Der openssl-Befehl aus Schritt 5 zeigt den einzelnen Hostnamen statt des Sterns.

Lösung: Abwarten, die Umstellung erfolgt beim Ablauf von selbst und ohne Unterbrechung. Wer sofort umschalten muss, entfernt die betroffenen Einträge aus dem ACME-Speicher. Dabei sollte das Backup aus Schritt 2 bereitliegen, denn ein Fehler an dieser Stelle betrifft alle Zertifikate im Speicher.

2. HostRegexp erzeugt keine SNI-Route

Symptom: Connection-Reset beim Verbindungsaufbau, obwohl das Zertifikat gültig und korrekt ist.

Ursache: Traefik leitet SNI-Routen ausschließlich aus Host() ab. Ein Router, der seine Hosts nur über HostRegexp matcht, bekommt keine SNI-Route. Der TLS-Verkehr fällt in den TCP-Catchall und wird verworfen, bevor HTTP überhaupt ins Spiel kommt.

Prüfung: Das Zertifikat prüfen und für gut befinden, aber die Verbindung bricht trotzdem ab. Genau diese Kombination weist auf das Routing hin, nicht auf TLS.

Lösung: Jeden real genutzten Host zusätzlich als konkretes Host() deklarieren. HostRegexp darf als Fangnetz bestehen bleiben, ersetzt die namentliche Nennung aber nicht. Wie Traefik Matcher auswertet und priorisiert, steht in der Doku zu Rules and Priority.

3. Neustart während einer laufenden Challenge

Symptom: Die Provider-API antwortet mit duplicate value, die Ausstellung schlägt fehl.

Ursache: Der neu gestartete Prozess greift dieselbe noch offene Autorisierung bei Let’s Encrypt auf und will denselben TXT-Eintrag ein zweites Mal anlegen.

Prüfung: Direkt in der Zone nachsehen, ob der Eintrag noch steht.

dig +short TXT _acme-challenge.lab.rz.jabi-it.de

Lösung: Warten, bis die Ausgabe leer ist, dann neu starten. Weitere Neustarts während der offenen Autorisierung verschlimmern die Lage und laufen auf die Rate-Limits von Let’s Encrypt zu.

4. Einzelne Dateien im Bind-Mount und sed -i

Symptom: Die Konfiguration ist nachweislich geändert, der Container lädt aber weiterhin den alten Stand.

Ursache: sed -i ersetzt die Datei durch eine neue mit neuer Inode. Ein Bind-Mount auf eine einzelne Datei zeigt weiter auf die alte Inode, die Änderung erreicht den Container nie.

Prüfung: Den Stand im Container gegen den auf dem Host vergleichen.

docker exec traefik cat /etc/traefik/traefik.yml | head -20

Lösung: Solche Dateien im Editor bearbeiten statt mit sed -i, und den Container mit --force-recreate neu erzeugen. Wer den Ordner statt der Einzeldatei mountet, hat das Problem grundsätzlich nicht.

Das Wildcard an Dienste ohne HTTP weiterreichen

Nicht jeder Dienst sitzt hinter dem Reverse Proxy. Ein MQTT-Broker auf Port 8883 braucht dasselbe Zertifikat, spricht aber kein HTTP und kann sich bei Traefik nichts abholen.

Statt einen zweiten ACME-Client zu betreiben, lesen Sie das Zertifikat aus Traefiks Speicher aus. Ein kleines Skript liest den JSON-Speicher, dekodiert den passenden Eintrag mit jq und base64 -d, schreibt Zertifikat und Schlüssel in das Verzeichnis des Dienstes und startet ihn nur dann neu, wenn sich der Inhalt tatsächlich geändert hat. Ein täglicher Cron-Aufruf genügt.

Der Gewinn ist struktureller Natur: Nur eine Komponente spricht mit Let’s Encrypt, alles andere zieht ab. Das reduziert die Zahl der Stellen, an denen eine Erneuerung scheitern kann, und macht ein zweites ACME-Werkzeug überflüssig.

Grenzen von DNS-01

DNS-01 setzt eine API auf Ihrer DNS-Zone voraus. Liegt eine Domain bei einem Anbieter ohne brauchbare API, lässt sich für sie kein Wildcard automatisieren, und sie bleibt bei HTTP-01 pro Host. Ein gemischter Betrieb ist unproblematisch, er muss nur bewusst so eingerichtet sein.

Der zweite Punkt ist sicherheitsrelevant. Ein API-Token für eine DNS-Zone ist ein mächtiges Geheimnis. Wer ihn besitzt, kann die Zone umschreiben und sich damit gültige Zertifikate für Ihre Domains ausstellen lassen. Er gehört außerhalb der Versionsverwaltung, eng begrenzt auf die benötigte Zone, und auf einen Account, dessen Rechte nicht weiter reichen als nötig.

Und schließlich eine Erwartung, die man ablegen sollte: Die Umstellung ist kein Schalter. Solange alte Einzelzertifikate gültig sind, laufen sie weiter. Planen Sie den Übergang über die Restlaufzeit hinweg und stellen Sie öffentlich sichtbare Domains zuletzt um.

Sie betreiben mehrere Dienste hinter einem Reverse Proxy und wollen TLS sauber automatisieren? Ich baue Reverse-Proxy- und Zertifikats-Setups, die ohne manuelles Nachfassen laufen. Mehr unter Infrastruktur & Cloud, das Erstgespräch ist unverbindlich.

Alex Jabi, Jabi IT

Alex Jabi

Ich betreue IT, Informationssicherheit und Datenschutz für KMU und Praxen an der Bergstraße und im Odenwald, persönlich, dokumentiert und ohne Cloud-Zwang.

Mehr über mich
Zurück zum Blog

Passende Artikel

Alle Artikel »