Ich wollte meine Dienste im Heimnetz unbedingt per HTTPS aufrufen können. Dabei ging es mir vor allem um die Bedienung. Ohne vernünftige DNS-Namen und Zertifikate, die der Browser direkt akzeptiert, fühlt sich das für mich nach Bastelei an. Der Sicherheitsgewinn war für mich eher zweitrangig.

Meine E-Book-Bibliothek mit Calibre-Web sollte deshalb unter bib.lab.weinboerner.de erreichbar sein. Die Domain weinboerner.de habe ich schon lange, sie ist meine persönliche Adresse im Internet. Damit haben auch die lokalen Dienste einen Namen mit Wiedererkennungswert, und ich muss mir keine IP-Adresse mit Port merken.

Caddy kannte ich bereits aus anderen Projekten als Reverse Proxy. lego ist im Laufe der Recherche dazugekommen, um für die internen Dienste öffentlich vertrauenswürdige Zertifikate zu bekommen. Zusammen mit AdGuard Home ist daraus mein Zugang zur Bibliothek geworden.

Aufbau

AdGuard Home beantwortet im Heimnetz die DNS-Anfrage für bib.lab.weinboerner.de mit der internen Adresse meines Caddy-Servers. Der Browser verbindet sich anschließend per HTTPS mit Caddy auf Port 443. Caddy liefert das Zertifikat aus und leitet die Anfrage an Calibre-Web weiter.

Mermaid Diagram

Caddy ist hier der Reverse Proxy. Der DNS-Server bleibt AdGuard Home. Für das öffentlich vertrauenswürdige Zertifikat übernimmt lego die Prüfung über die DNS-API von United-Domains. Das Zertifikat landet anschließend als Datei bei Caddy.

Damit die Bibliothek intern bleibt, müssen die Firewallregeln dazu passen. Ein fehlender öffentlicher DNS-Eintrag verhindert keinen Zugriff auf eine anderweitig erreichbare Adresse. Das gilt für IPv4 und IPv6, mögliche Portweiterleitungen und andere Zugangswege. Auch die Anmeldung an der Bibliothek bleibt erforderlich; ein gültiges Zertifikat ersetzt keine Zugriffskontrolle.

Voraussetzungen

Meine Domain liegt schon seit langer Zeit bei United-Domains. Mit dem Anbieter habe ich gute Erfahrungen gemacht. Für diesen Aufbau war es praktisch, dass es dort eine DNS-API gibt und lego den passenden Adapter mitbringt. Die Einrichtung hat bei mir sehr gut funktioniert.

Für die automatische Zertifikatsausstellung muss lego einen DNS-Nachweis in der öffentlichen Zone setzen können. Die DNS-API von United-Domains muss dafür im Portfolio hinzugebucht sein, und die Zone muss über die Nameserver des Anbieters verwaltet werden. Eine dort registrierte Domain mit externen Nameservern reicht für diesen API-Weg nicht aus.

Auch die lego-Version ist relevant. Der United-Domains-Adapter heißt uniteddomains und ist seit Version 4.29.0 enthalten. Das reguläre Debian-13-Paket liegt zum Recherchestand vom 2. November 2026 noch bei 4.9.1-2. Ein apt install lego aus dieser Quelle bringt den benötigten Adapter deshalb nicht mit. Dafür braucht man eine passende neuere Version, beispielsweise aus den offiziellen Releases.

Der Adapter erwartet den Schlüssel in UNITEDDOMAINS_API_KEY. Mit UNITEDDOMAINS_API_KEY_FILE lässt sich stattdessen auf eine geschützte Datei verweisen. Der Wert hat das Format prefix.secret und gehört weder ins Repository noch in eine offen lesbare Crontab.

United-Domains erlaubt laut eigener Dokumentation keine Beschränkung dieses Zugangs auf eine einzelne Domain. Der Schlüssel gilt für das gesamte Portfolio. Die Secretdatei sollte deshalb nur für den Prozess lesbar sein, der lego ausführt. Caddy braucht den DNS-API-Schlüssel zum Ausliefern des Zertifikats nicht.

Meine ältere Konfigurationsnotiz verwendet die lego-v4-Syntax mit getrenntem run und renew. Die lego-Dokumentation beschreibt bereits die v5-Umstellung auf run für beide Aufgaben sowie geänderte Optionen und Ablagepfade. Die Befehle und ein späterer Erneuerungsjob müssen zur tatsächlich installierten Version passen. Deshalb lassen sich alte Cronzeilen und aktuelle Beispiele nicht beliebig mischen.

DNS-Konfiguration

Für den Zugriff im LAN zeigt die DNS-Umschreibung in AdGuard Home auf Caddy. Als Beispiel verwende ich hier 192.168.10.10 für den Proxy:

1
bib.lab.weinboerner.de -> 192.168.10.10

Das ist ein Beispielwert, keine Abschrift meiner laufenden Netzkonfiguration. Die Antwort enthält die Adresse des Proxys. Adresse und Port von Calibre-Web werden erst in Caddy hinterlegt.

Für die Bibliothek reicht der einzelne Name. Eine Umschreibung für *.lab.weinboerner.de kann auch mehrere Dienstnamen auf denselben Proxy zeigen lassen. Für jeden Dienst braucht Caddy dann die passende Weiterleitung.

Mit dieser Umschreibung findet der Browser den richtigen Server. Für eine vertrauenswürdige HTTPS-Verbindung fehlt noch das Zertifikat für den angefragten Namen.

Zertifikatsausstellung

Für das Zertifikat verwende ich DNS-01. Dabei prüft die Zertifizierungsstelle, ob ich einen vorgegebenen TXT-Eintrag im öffentlichen DNS setzen kann. lego erledigt das über die United-Domains-API. Die lokale Antwort von AdGuard Home ist an diesem Nachweis nicht beteiligt.

Für ein Zertifikat nur für bib.lab.weinboerner.de lautet der Nachweisname:

1
_acme-challenge.bib.lab.weinboerner.de

lego setzt den geforderten Wert, wartet auf dessen Verfügbarkeit und lässt ihn prüfen. Anschließend wird der Challenge-Eintrag wieder entfernt. Let's Encrypt beschreibt DNS-01 ausdrücklich als Verfahren für Dienste, deren Webserver nicht aus dem Internet erreichbar ist. Dafür braucht mein Caddy keine Portfreigabe. Ein öffentlicher A- oder AAAA-Eintrag für die Bibliothek ist ebenfalls nicht nötig.

Meine Konfigurationsnotiz umfasst lab.weinboerner.de und *.lab.weinboerner.de in einem Zertifikat. Das Wildcard deckt Namen wie bib.lab.weinboerner.de ab. Für den Basisnamen lab.weinboerner.de selbst gilt es nicht; dieser steht deshalb zusätzlich im Zertifikat. Weitere Ebenen wie test.bib.lab.weinboerner.de sind ebenfalls nicht abgedeckt. Beim Wildcard-Nachweis liegt der TXT-Eintrag unter _acme-challenge.lab.weinboerner.de.

Öffentlich vertrauenswürdig bedeutet allerdings auch, dass die Zertifikatsnamen sichtbar werden können. Let's Encrypt veröffentlicht ausgestellte Zertifikate in Certificate-Transparency-Logs. Ein Einzelzertifikat nennt dort den Bibliotheksnamen, ein Wildcard-Zertifikat den Wildcard-Namen. Die einzelnen damit verwendeten Dienstnamen stehen dadurch nicht automatisch im Log. Der Dienst selbst muss dafür weiterhin nicht öffentlich erreichbar sein.

Caddy-Konfiguration

Nach der Ausstellung braucht Caddy das Zertifikat mit der Zwischenzertifikatskette und den passenden privaten Schlüssel als PEM-Dateien. Ein DNS-Provider-Modul in Caddy ist dafür nicht nötig, weil lego die ACME-Verwaltung übernimmt.

Die Dateien müssen vor dem Laden vorhanden und für den Caddy-Prozess lesbar sein. Der private Schlüssel braucht entsprechend enge Rechte. Auch die Verzeichnisse im Pfad müssen den Zugriff erlauben. Bei einem Container müssen die Dateien unter dem dort angegebenen Pfad erreichbar sein.

Ein reduziertes Beispiel für die Bibliothek sieht so aus. Die Backendadresse 192.168.10.30:8083 und die Dateipfade sind ebenfalls Beispielwerte:

1
2
3
4
bib.lab.weinboerner.de {
    tls /etc/caddy/certs/lab.fullchain.pem /etc/caddy/certs/lab.key
    reverse_proxy http://192.168.10.30:8083
}

Damit kann Caddy die HTTPS-Anfrage für bib.lab.weinboerner.de an Calibre-Web weitergeben. Im Beispiel ist die Verbindung vom Browser zu Caddy verschlüsselt; zum Backend geht es über HTTP. Soll auch diese Strecke verschlüsselt werden, braucht sie eine passende HTTPS-Konfiguration mit geprüfter Zertifikatskette.

Caddy erneuert diese ausdrücklich geladenen Zertifikate standardmäßig nicht selbst. Das beschreibt die TLS-Dokumentation. Für den dauerhaften Betrieb muss deshalb auch die Erneuerung über lego funktionieren.

Zertifikatserneuerung

Bei mir übernimmt ein Cronjob die regelmäßige Zertifikatserneuerung. Damit läuft der Aufbau im Alltag sehr gut. Die Erneuerung gehört also genauso zur Einrichtung wie der erste Aufruf der Bibliothek.

Als Intervall für die Prüfung bietet sich beispielsweise ein täglicher Lauf an. So bleibt Zeit für weitere Versuche, wenn DNS-Anbieter oder Zertifizierungsstelle vorübergehend nicht erreichbar sind. Ein neues Zertifikat wird dabei nur benötigt, wenn eine Erneuerung ansteht.

Nach erfolgreicher Erneuerung müssen Zertifikat und Schlüssel als zusammengehöriges Paar an den vorgesehenen Ort gelangen. Beide Dateien müssen vollständig vorliegen und die Rechte stimmen, bevor Caddy neu geladen wird. Bei einem Fehler sollten die bisherigen funktionierenden Dateien erhalten bleiben. Fehlgeschlagene Läufe müssen auffallen, bevor das bisherige Zertifikat abläuft.

Zum erneuten Einlesen manuell geladener Zertifikate dokumentiert Caddy --force. Bei einer üblichen lokalen Installation mit aktivierter Admin-Schnittstelle lautet der Aufruf beispielsweise:

1
caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile --force

Der Force-Reload berücksichtigt, dass sich Zertifikatsdateien ändern können, obwohl die Konfiguration gleich bleibt. Ein systemctl reload caddy genügt dafür nur, wenn die hinterlegte Serviceaktion das ebenfalls berücksichtigt. Anschließend muss Caddy das neue Zertifikat tatsächlich über HTTPS ausliefern. Das Ablaufdatum einer Datei auf der Platte allein sagt darüber nichts aus.

Ohne Internet können die lokale DNS-Auflösung und der Zugriff auf die lokal laufende Bibliothek mit noch gültigem Zertifikat weiter funktionieren. Für Ausstellung und Erneuerung benötigt lego dagegen die öffentlichen DNS- und ACME-Dienste.

Funktionsprüfung

Ob die Einrichtung das ursprüngliche Ziel erfüllt, lässt sich am vollständigen Zugriff prüfen: Der Name muss auf Caddy zeigen, der Browser muss dessen Zertifikat akzeptieren und hinter dem Proxy muss die Bibliothek antworten. Bei Problemen lassen sich diese Schritte einzeln untersuchen.

Für eine gezielte DNS-Abfrage steht im folgenden Beispiel 192.168.10.2 für AdGuard Home:

1
2
dig @192.168.10.2 bib.lab.weinboerner.de A
dig @192.168.10.2 bib.lab.weinboerner.de AAAA

Die Antworten müssen zur vorgesehenen Proxyadresse passen. Auch ein unerwarteter AAAA-Eintrag ist relevant, weil ein Client über IPv6 sonst ein anderes Ziel erreichen kann. Die lokale DNS-Umschreibung wirkt außerdem nur, wenn das Gerät den vorgesehenen Resolver verwendet. Ein Browser mit externem DNS-over-HTTPS kann daran vorbeifragen; das beschreibt auch die Firefox-Dokumentation.

Mit der Beispieladresse von Caddy lässt sich anschließend DNS für einen gezielten HTTPS-Test übergehen. Hostname und Zertifikatsprüfung bleiben dabei erhalten:

1
2
curl --resolve bib.lab.weinboerner.de:443:192.168.10.10 \
    https://bib.lab.weinboerner.de/

Klappt dieser Zugriff, während der normale Aufruf scheitert, ist die Namensauflösung ein naheliegender Ansatzpunkt. Bei einem Zertifikatsfehler kommen Name, Vertrauenskette, Gültigkeit und ein vergessenes Neuladen infrage. Antwortet Caddy mit 502 Bad Gateway, ist unter anderem zu prüfen, ob er Calibre-Web an der hinterlegten Adresse und am richtigen Port erreicht. Beim normalen Browseraufruf sollte schließlich die erwartete Bibliotheksoberfläche oder deren Anmeldung erscheinen.

Für den Aufruf meiner Bibliothek bleibt damit https://bib.lab.weinboerner.de. IP-Adresse und Backendport stehen in der Konfiguration; im Browser habe ich den vertrauten Namen mit akzeptiertem Zertifikat. Genau so wollte ich meine Bibliothek im Heimnetz benutzen.