Die Situation

Ich habe tech-tales.blog bei World4You gekauft. Hier habe ich die Challenge, dass World4You mir keinen API-Zugang zum DNS Server bietet, ich kann also primär keine DNS Verifizierung für ein Wildcard-Zertifikat machen. Trotzdem würde ich gerne automatisch ein Wildcard-Zertifikat generieren, da alle meine internen Services via *.tech-tales.blog erreichbar sind.

Zusätzlich ist es mir aus verschiedenen Gründen nicht möglich, den DNS Server einfach zu wechseln. Insbesondere betreibt World4You aktuell meinen eMail Server, und das Ändern des DNS Servers würde das umstellen. Das möchte ich (noch) nicht machen.

Die Lösung: Eine zweite Domain!

Ich habe schon länger eine alternative Domain bei Hetzner geshoppt, nennen wir sie tech-tales-alternative.blog.

Mit acme.sh kann ich diese zweite Domain nutzen, um eine DNS Challenge auf der ersten Domain zu beantworten! Dieser Post soll erklären wie ich das mache.

Die Erklärung

Wenn ich Let’s Encrypt um ein Zertifikat für *.tech-tales.blog frage und dafür die DNS-Challenge nutzen möchte, muss ich einen TXT DNS-Eintrag auf _acme-challenge.tech-tales.blog erzeugen, mit einem von Let’s Encrypt bereitgestelltem Inhalt. Sobald ich das gemacht habe verifiziert Let’s Encrypt dass dieser Eintrag korrekt existiert und stellt dann mein Zertifikat aus.

Ich kann jetzt das folgende machen: Statt einem TXT Record auf _acme-challenge.tech-tales.blog erzeuge ich diesen TXT Eintrag auf _acme-challenge.tech-tales-alternative.blog. Zusätzlich erzeuge ich einen CNAME Eintrag wie folgt:

_acme-challenge.tech-tales.blog	IN	CNAME	_acme-challenge.tech-tales-alternative.blog.

Let’s Encrypt versteht das und folgt dem CNAME, und so funktioniert auch die Erstellung meines Zertifikats!

Das Skript

Ich gehe davon aus dass acme.sh bereits installiert ist. Dann hole ich mir über die Hetzner DNS Console einen API Key, und dann führe ich das folgende im Terminal aus:

# Token hier einfügen
EXPORT HETZNER_TOKEN=abcdefghijklmnopqrstuvwxyz

acme.sh \
    --issue \
    --server letsencrypt_test \
    -m admin@tech-tales.blog \
    -d *.tech-tales.blog \
    --dns dns_hetzner \
    --challenge-alias tech-tales-alternative.blog

Beachte dass ich zur Zeit --server letsencrypt_test setze, also den Test-Server verwende. Sobald ich mir sicher bin dass alles klappt wie vorgesehen, wechsel ich zu --server letsencrypt.

Dieser Befehl wartet einige Sekunden, bis alles klappt. Im Wesentlichen gibt es ein paar relevante Zeilen im Output:

  • Adding TXT value: ... for domain: _acme-challenge.tech-tales-alternative.blog - Dadurch wird der DNS Eintrag dort erzeugt, wo er benötigt wird.
  • Success for domain tech-tales.blog '_acme-challenge.tech-tales-alternative.blog - Der DNS Eintrag wurde erfolgreich erzeugt.
  • Als nächstes verifiziert die CA, also Let’s Encrypt, die Domain. Das könnte ein bisschen dauern, antwortet aber schlussendlich hoffentlich mit Success.

Der DNS Eintrag selbst wird dann von acme.sh automatisch wieder gelöscht, und der Zustand ist der gleiche wie vorher.

Profit

In ~/.acme.sh/*.tech-tales.blog gibt es jetzt ein ca.cer und ein fullchain.cer File; diese beiden kann ich auf allen meinen Servern nutzen!

Addendum: Automatisches Deployment mit einem Renew Hook

Ich nutze im acme.sh Befehl zusätzlich das Argument --renew-hook /path/to/some/script.sh. Dadurch passiert das Folgende: Jedes Mal wenn das Zertifikat tatsächlich erneuert wird, wird das angegebene Skript anschließend ausgeführt. Ich nutze diesen Befehl, um das Skript überall zu deployen wo es benötigt wird.

Addendum: Die CA von Test auf Pro ändern

Let’s Encrypt empfiehlt, ihr Test-Environment zu nutzen (was passiert ist, per --server letsencrypt_test), solange noch getestet wird. Das TLS Zertifikat das vom Test-Environment ausgestellt wird, wird von Browsern typischerweise nicht akzeptiert. Trotzdem sollte der Test-Server zuerst genutzt werden, weil der produktive Server Rate Limits definiert hat.

Der Wechsel auf den produktiven Server ist etwas schwierig, weil ich dafür den vollständigen Befehl noch einmal eingeben müsste. Ich habe aber eine etwas hacky Alternative gefunden:

  • Navigiere nach ~/.acme.sh/*.tech-tales.blog
  • In diesem Ordner gibt es die Datei *.tech-tales.blog.conf.
  • Und darin insbesondere eine wesentliche Zeile: LE_API=https://acme-staging-v2.api.letsencrypt.org/directory. Wechsle diese URL auf https://acme-v2.api.letsencrypt.org/directory. Der Rest der Datei bleibt unverändert.
    Beachte insbesondere dass LE_OrderFinalize, LE_LinkOrder und LE_LinkCert nicht verändert werden müssen!
  • Führe jetzt den Befehl acme.sh --renew --force *.tech-tales.blog aus, beachte hier insbesondere --force. Dadurch wird das Zertifikat neu ausgestellt, diesmal von der korrekten CA.