Homelab-Umbau Teil 8: Eine eigene Zertifizierungsstelle
Das Ärgernis Link zu Überschrift
Jeder interne Dienst mit Weboberfläche begrüsst einen mit einer Zertifikatswarnung. Firewall, NAS, Backup-Server, Access Point — überall dasselbe Bild, überall einmal wegklicken.
Das ist nicht nur lästig. Es erzieht einen dazu, Warnungen wegzuklicken, und irgendwann klickt man auch die weg, die berechtigt gewesen wäre. Genau die Reaktion, die man sich nicht antrainieren möchte.
Die Lösung ist eine eigene Zertifizierungsstelle. Ein Root-Zertifikat, das man einmal auf seinen Geräten hinterlegt, und danach vertraut der Browser allem, was von dieser Stelle ausgestellt wurde.
Warum step-ca Link zu Überschrift
Naheliegend wären selbstsignierte Zertifikate gewesen. Die kann man mit OpenSSL in zwei Befehlen erzeugen.
Nur skaliert das nicht: Jedes Zertifikat muss einzeln erzeugt, einzeln verteilt und einzeln erneuert werden. Bei einer Handvoll Dienste ist das ein Termin im Kalender, den man verpasst.
step-ca bringt ACME mit — dasselbe Protokoll, mit dem Let’s Encrypt im Internet arbeitet, nur nach innen gerichtet. Der Dienst holt sich sein Zertifikat selbst und erneuert es selbst. Man richtet es einmal ein und fasst es nicht wieder an.
Der Aufbau ist der übliche: ein Root-Zertifikat mit langer Laufzeit, das offline bleiben könnte, und ein Intermediate, das die eigentliche Arbeit macht. Signiert wird nur mit dem Intermediate — geht der verloren, tauscht man ihn aus, ohne dass alle Geräte ein neues Root-Zertifikat brauchen.
Wie es zusammenspielt Link zu Überschrift
Der Rückweg ist der interessante Teil — dort lagen alle vier Fehler.
Der Ablauf hat vier Schritte, und die Reihenfolge erklärt, warum die Fehlersuche so mühsam war:
- Die Firewall fragt bei der CA nach einem Zertifikat für ihren Namen
- Die CA prüft, ob der Anfragende diesen Namen wirklich kontrolliert — indem sie selbst eine Verbindung dorthin aufbaut
- Erst wenn das klappt, wird das Zertifikat ausgestellt
- Und dann muss der Dienst es auch tatsächlich laden
Schritt zwei ist der Knackpunkt. Die CA baut eine Verbindung zurück auf, und alles, was diesem Rückweg im Weg steht, lässt die Ausstellung scheitern — mit Fehlermeldungen, die auf den ersten Blick nach einem Problem beim Anfragenden aussehen.
Was schiefging Link zu Überschrift
Vier Fehler. Drei davon auf dem Rückweg, einer danach.
Der Name, der nur über IPv6 auflöste Link zu Überschrift
Die Ausstellung scheiterte mit einer Zeitüberschreitung. Ein Test mit
curl von einem anderen Rechner aus zeigte, dass der Port erreichbar
war. Also warum kam die CA nicht durch?
Weil sie einen anderen Weg nahm. Der Name der Firewall wurde vom internen DNS mit ausschliesslich IPv6-Adressen beantwortet — zwei Stück, für LAN und WAN. Eine IPv4-Adresse gab es nicht.
Und der Container, in dem die CA läuft, hat keine IPv6-Route. Der Versuch scheiterte, bevor ein Paket das Gerät verliess:
Immediate connect fail: Network is unreachable
Kein Timeout, keine Firewall — die Adressfamilie war schlicht nicht erreichbar.
Die Lehre: Wenn eine Prüfung über einen Namen läuft, muss man testen,
was der prüfende Rechner auflöst. Nicht, was der eigene Laptop
auflöst. Ein getent hosts name auf der richtigen Maschine hätte das in
zehn Sekunden gezeigt.
Behoben mit einem Host-Eintrag, der ausschliesslich IPv4 liefert.
Die IP-Adresse, die niemand ausstellen wollte Link zu Überschrift
Der Zertifikatsantrag enthielt zwei Namen: einen Hostnamen und zusätzlich die IP-Adresse der Firewall. Praktisch gedacht — dann funktioniert der Aufruf über beide Wege ohne Warnung.
Die CA sah das anders:
"status":"invalid"
"type":"urn:ietf:params:acme:error:rejectedIdentifier"
"detail":"The server will not issue certificates for the identifier"
Zertifikate auf IP-Adressen sind möglich, aber nicht überall erlaubt, und in der Standardkonfiguration lehnt die CA sie ab. Solange die Adresse im Antrag stand, konnte der gesamte Vorgang nie abschliessen — ein abgelehnter Bestandteil lässt den ganzen Antrag scheitern, auch wenn der Rest in Ordnung ist.
Behoben, indem die IP aus dem Antrag flog. Der Preis: Die Oberfläche muss über den Namen aufgerufen werden, über die Adresse bleibt die Warnung.
Der Webserver, der den Rückweg blockierte Link zu Überschrift
Jetzt kam die Prüfung überhaupt an — und lieferte das Falsche.
Für die Prüfung legt der Client eine Datei ab, die die CA über Port 80 abholen soll. Nur antwortete dort der Webserver der Firewall selbst und schickte eine Weiterleitung auf HTTPS:
< HTTP/1.1 301 Moved Permanently
< Location: https://.../.well-known/acme-challenge/...
Ein Henne-Ei-Problem in Reinform: Der Dienst, für den das Zertifikat gedacht ist, belegt genau den Port, über den die Prüfung läuft.
Das ACME-Plugin der Firewall hat dafür einen Modus, der den eigenen Webserver für die Dauer der Prüfung um ein Schnipsel Konfiguration erweitert. Der war ausgewählt — griff aber nicht, weil in einem Feld für lauschende Adressen die IP der CA eingetragen war statt der eigenen. Ein Wert, der auf der Firewall gar nicht existiert.
Sowas findet man nicht durch Nachdenken. Nur durch Hinschauen.
Das Zertifikat, das niemand lud Link zu Überschrift
Und dann der Fehler, der am längsten unentdeckt geblieben wäre.
Die Ausstellung lief durch. Im Plugin stand ein frisches Zertifikat mit neuem Ablaufdatum. Alles gut?
Nein. Ein Abruf von aussen zeigte weiterhin das alte:
echo | openssl s_client -connect name:443 2>/dev/null \
| openssl x509 -noout -dates -serial
Die Seriennummer war unverändert. Der Webserver hielt die alte Datei im Speicher. Erneuerung und Auslieferung sind zwei getrennte Dinge, und das eine folgt nicht automatisch aus dem anderen.
Behoben mit einer Automation im Plugin, die nach jeder Erneuerung den Webserver neu startet. Getestet mit einer erzwungenen Erneuerung: Die Seriennummer änderte sich, ohne dass ich etwas von Hand gemacht habe.
Und die Lehre daraus ist allgemeiner: Prüf nicht, ob die Software sagt, dass etwas passiert ist. Prüf, was tatsächlich ankommt.
Die Falle mit der Laufzeit Link zu Überschrift
Eine Sache, die einem erst am nächsten Morgen auffällt: step-ca stellt standardmässig Zertifikate mit 24 Stunden Laufzeit aus.
Das ist als Vorgabe durchdacht — kurze Laufzeiten sind sicherer, und mit funktionierender Automatik merkt man es nicht. Nur muss diese Automatik dann jeden einzelnen Tag fehlerfrei laufen. Fällt sie einmal aus, steht man morgens vor einer abgelaufenen Oberfläche.
Für ein Heimnetz halte ich das für zu eng. Ich habe die Laufzeit auf 30 Tage gesetzt. Damit tut ein einzelner fehlgeschlagener Lauf nicht weh, und ich habe knapp einen Monat Zeit, es zu bemerken.
Die Einstellung sitzt in den Claims der CA. Wichtig dabei: Claims am Provisioner überschreiben die auf Authority-Ebene. Wer sie oben setzt und unten eigene stehen hat, ändert nichts.
Dazu ein Cron-Eintrag, der den Webserver einmal nachts neu startet — als Rückfallebene, falls die Automation nach einem Update mal stillschweigend nicht mehr greift. Kostet nichts und fängt den Fall auf, den man sonst erst nach vier Wochen bemerkt.
Nebenbei gelernt Link zu Überschrift
Zwei Kleinigkeiten, die Zeit gekostet haben.
Zwei parallel laufende Erneuerungen vertragen sich nicht. Ich hatte im Plugin zweimal auf „Erneuern" geklickt, weil beim ersten Mal scheinbar nichts passierte. Ergebnis war ein Fehler über einen ungültigen Einmalwert — beide Läufe teilten sich denselben Arbeitsordner und kamen sich in die Quere. Einmal klicken, dann warten.
Die Root-Shell der Firewall ist csh, nicht bash. Konstrukte wie
2>/dev/null vor einer Pipe verhalten sich dort anders und quittieren
mit „Ambiguous output redirect". Entweder csh-Syntax nutzen oder ein
bash -c '...' davorsetzen. Das trifft einen genau dann, wenn man
gerade unter Zeitdruck etwas debuggen will.
Das Root-Zertifikat verteilen Link zu Überschrift
Damit der Browser dem Ganzen vertraut, muss das Root-Zertifikat auf die Endgeräte. Und hier gibt es eine Stolperfalle:
Firefox nutzt einen eigenen Zertifikatsspeicher. Es reicht nicht, das Root-Zertifikat im Betriebssystem zu hinterlegen — unter Linux übernimmt Firefox das nicht. Der Import läuft über die Einstellungen, und dort muss man den Haken bei „Dieser CA vertrauen, um Websites zu identifizieren" setzen. Ohne den ist das Zertifikat zwar importiert, aber wirkungslos.
Für mehrere Rechner geht das auch zentral über eine Richtliniendatei. Das Betriebssystem braucht das Zertifikat trotzdem separat, damit Kommandozeilenwerkzeuge und andere Browser mitziehen.
Prüfen sollte man den Fingerabdruck, bevor man importiert. Ein Root-Zertifikat ist eine Vertrauensentscheidung — wer dort das falsche einträgt, hat ein Problem, das man nicht mehr sieht.
Wo ich jetzt stehe Link zu Überschrift
Die Firewall-Oberfläche zeigt ein grünes Schloss. Das Zertifikat erneuert sich selbst, wird selbst importiert, und der Webserver lädt es selbst neu.
Was noch aussteht: der Beweis, dass der vollautomatische Lauf auch ohne mein Zutun funktioniert. Erzwungen habe ich ihn getestet, die planmässige Erneuerung steht Mitte September an. Ich schreibe hier nach, wie es ausgegangen ist.
Und die Kette ist damit erst für einen Dienst gebaut. NAS, Backup-Server und Proxmox zeigen weiterhin ihre Warnungen — jeder davon spricht ACME oder kann zumindest ein Zertifikat entgegennehmen. Das ist Fleissarbeit für einen ruhigen Abend.
Nächster Schritt Link zu Überschrift
Im nächsten Teil kommen die Regeln. Die VLANs stehen, die Geräte landen automatisch im richtigen Netz — aber der Verkehr darf noch überallhin. Erst wenn das eingeschränkt ist, wird aus der Trennung auch eine Grenze.
Teil einer Serie über den Umbau meines Heimnetzes. Stand: August 2026. Fragen, Korrekturen und eigene Erfahrungen gerne in die Kommentare.