Letsencrypt - Expected cert.pem to be a symlink

Ein kleiner Quickie für meine eigenen Tipps & Tricks Box, falls mir das Problem nochmal passieren sollte.

Ich habe mal wieder einen Server migriert und dabei auch das Letsencrypt Verzeichnis aus /etc/letsencrypt/ mitgenommen. Irgendwo in dem Prozess scheint ein cp -r oder rsync vorhandene Symlinks aufgelöst und daraus Dateien gemacht zu haben. Oder die alte Letsencrypt Version war veraltet … lässt sich leider nicht mehr verifizieren, da der vorherige Server nicht mehr verfügbar ist.

Beim Setup des Webservers ist nichts aufgefallen und auch certbot ließ sich ohne Murren installieren. Auch ein Test des Befehls test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew aus dem Cronjob /etc/cron.d/certbot hat keine Fehlermeldung zutage gebracht.

An der Stelle fällt dem geneigten Leser auf, wieso ich den gesamten Befehl kopiert habe: JA, ich habe das -q übersehen

Ein paar Wochen nach dem Setup des Servers bekam ich vom Let’s Encrypt Expiry Bot eine typische Expiry-Email mit dem Titel Let’s Encrypt certificate expiration notice for domain “example.com”.
Randnotiz: wieder mal hat sich bewiesen, dass es sich lohnt die Email Adresse bei der Ausstellung des Zertifikats anzugeben.

Logfile und Fehler

Die Email war ein Weckruf und nach einer kurzen Recherche auf dem Server kam das Logfile /var/log/letsencrypt/letsencrypt.log zu Tage und eine Kontrolle eben jenes die Fehlermeldung:

DEBUG:certbot.main:certbot version: 0.40.0
DEBUG:certbot.main:Arguments: ['-q']
DEBUG:certbot.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#manual,PluginEntryPoint#nginx,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
DEBUG:certbot.log:Root logging level set at 30
INFO:certbot.log:Saving debug log to /var/log/letsencrypt/letsencrypt.log
WARNING:certbot.renewal:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 65, in _reconstitute
    renewal_candidate = storage.RenewableCert(full_path, config)
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 465, in __init__
    self._check_symlinks()
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 522, in _check_symlinks
    raise errors.CertStorageError(
certbot.errors.CertStorageError: expected /etc/letsencrypt/live/example.com/cert.pem to be a symlink
2021-01-24 11:24:27,110:WARNING:certbot.renewal:Renewal configuration file /etc/letsencrypt/renewal/example.com.conf is broken. Skipping.
2021-01-24 11:24:27,110:DEBUG:certbot.renewal:Traceback was:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 65, in _reconstitute
    renewal_candidate = storage.RenewableCert(full_path, config)
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 465, in __init__
    self._check_symlinks()
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 522, in _check_symlinks
    raise errors.CertStorageError(
certbot.errors.CertStorageError: expected /etc/letsencrypt/live/example.com/cert.pem to be a symlink

DEBUG:certbot.log:Exiting abnormally:
Traceback (most recent call last):
  File "/usr/bin/certbot", line 11, in <module>
    load_entry_point('certbot==0.40.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1382, in main
    return config.func(config, plugins)
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1287, in renew
    renewal.handle_renewal_request(config)
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 486, in handle_renewal_request
    raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
certbot.errors.Error: 0 renew failure(s), 1 parse failure(s) 

Hervorzuheben ist der relevante Fehler:

Renewal configuration file /etc/letsencrypt/renewal/example.com.conf is broken. Skipping

Was aber nur ein Folgefehler ist des Problems:

certbot.errors.CertStorageError: expected /etc/letsencrypt/live/example.com/cert.pem to be a symlink

Jetzt könnte man hingehen und aus den echten Dateien wieder Symlinks machen, welche in das Verzeichnis /etc/letsencrypt/archive/example.com/ und dort auf die höchste durchnummerierte certX.pem Datei zeigen. Falls Du die Lösung bevorzugst, verifizieren lässt sich das Setup im Anschluss mit certbot update_symlinks.

Meine Lösung

Einfacher ist den folgenden Befehl zu nutzen um alles loszuwerden:

rm -rf /etc/letsencrypt/{live,renewal,archive}/{example.com,example.com.conf}

Siehe auch hier.

Danach nutzt Du den von Dir bevorzugten Weg, um die Zertifikate neu auszustellen. Das könnte sowas sein wie certbot nginx oder certbot apache oder certbot certonly --webroot -w /var/www/example.com -d example.com.

P.S: Ja, rabiat aber funktional. Aber denk dran: es existiert ein hartes Limit, wie oft man seine Zertifikate in einem bestimmten Zeitraum komplett neu ausstellen lassen kann.