Sun, 28 Jun 2026 00:00:00 +0200
crypto, letsencrypt, troubleshoot

Fehler suchen bei Let's Encrypt Zertifikatserneuerung

Bei einigen Servern verwende ich Zertifikate von Let's Encrypt Einer davon aktualisierte seine Zertifikate jedoch nicht automatisch.

Startete ich den Download des Zertifikats von Hand, funktionierte es wunderbar. Rief ich die automatische Aktualisierung auf, funktionierte es nicht.

Bei der Fehlersuche fiel mir auf, dass jedes Mal während der Aktualisierungsversuche folgendes in den Logs des Webservers zu finden war:

[mpm_prefork:notice] [...] AH00170: caught SIGWINCH, shutting down gracefully
[mpm_prefork:notice] [...] AH00163: Apache/2.4.67 (Debian) OpenSSL/3.0.20 configured -- resuming normal operations

Außerdem sah ich keinen Zugriff auf die Challenge-Datei außer den Tests von meinem Rechner, nicht jedoch von Let's Encrypt.

Da ich mit normaler Suche im Web zu keiner Lösung kam, versuchte ich es mit einem Chatbot (nein ich weiß nicht mehr mit welchem). Wirklich hilfreich war die Sitzung nicht, Vermutlich lag es daran, dass ich einerseits dem Chatbot nicht die richtigen Fragen stellte, andererseits zu wenig handfestes eigenes Wissen zum Problem hatte und darum das Geschwätz des Chatbots nicht sofort als Unfug abtat.

Das Problem liegt darin, dass systemd den apache2 runter und hochfährt, wenn eine HTTP-Anfrage via IPv6 kommt.

Das scheint mir im Nachhinein völliger Unsinn. Aber damals war ich ziemlich genervt von dem Problem und hatte mir das als Fazit der Sitzung und Hypothese für die weitere Fehlersuche notiert.

Einige Wochen später, kurz vor Ablauf des Zertifikats habe ich mich dem Problem noch einmal genähert und dazu erneut im Web gesucht. Bei hartnäckigen Fehlern schaue ich auch gern mal mit strace nach, was denn die Programme so veranstalten.

Zunächst schaute ich dem Webserver mit strace über die Schulter, aber da war tatsächlich keine Anfrage von Let's Encrypt zu sehen. Viel mehr gab die Auswertung auch nicht her.

Als nächstes schaute ich certbot bei der Arbeit zu:

sudo strace -f -o certbot.strace certbot renew --dry-run

Hier fand ich nun tatsächlich etwas interessantes:

...
execve("/etc/letsencrypt/renewal-hooks/pre/stop_services", ...
...
execve("/usr/bin/systemctl", ["systemctl", "stop", "apache2"], ...
...

Es war zwar systemd, der den Webserver beendete, aber nicht wegen einer IPv6-Anfrage, sondern weil certbot ihn darum bat.

Das Verzeichnis /etc/letsencrypt/renewal-hooks erregte meine Aufmerksamkeit. In der Certbot-Dokumentation fand ich dazu folgendes:

You can also specify hooks by placing files in subdirectories of Certbot’s configuration directory. Assuming your configuration directory is /etc/letsencrypt, any executable files found in /etc/letsencrypt/renewal-hooks/pre, /etc/letsencrypt/renewal-hooks/deploy, and /etc/letsencrypt/renewal-hooks/post will be run as pre, deploy, and post hooks respectively.

Auf dem betreffenden System sah das Verzeichnis so aus:

$ LANG=C tree /etc/letsencrypt/renewal-hooks
/etc/letsencrypt/renewal-hooks
|-- deploy
|-- post
|   `-- start_services
`-- pre
    `-- stop_services

Auf den anderen Systemen gab es keine Hooks. Ich wollte die Dateien nicht gleich löschen und schaute darum erst einmal hinein:

/etc/letsencrypt/renewal-hooks/pre/stop_services:

#!/bin/bash
echo "stopping service apache2"
systemctl stop apache2
echo "stopping service dovecot"
systemctl stop dovecot
echo "stopping service postfix"
systemctl stop postfix

/etc/letsencrypt/renewal-hooks/post/start_services:

#!/bin/bash
echo "starting service apache2"
systemctl start apache2
echo "starting service dovecot"
systemctl start dovecot
echo "starting service postfix"
systemctl start postfix

Nun war mir klar, was passierte. Beim Aufruf von certbot renewal fuhr certbot den Webserver herunter, bat Let's Encrypt um ein neues Zertifikat, bei dem die Challenge fehlschlug, weil der Webserver nicht erreichbar war. Anschließend startete certbot den Webserver wieder.

In der Datei stop_services entfernte ich die Zeilen, mit denen Apache beendet wurde. In der Datei start_services änderte ich den Aufruf zu systemctl restart apache2.

Damit funktioniert certbot renewal und ich muss mich nicht mehr selbst darum kümmern.