acme.sh

Siehe auch

Verwandte Artikel
Offizielle Dokumentation
Linuxfabrik

acme.sh ist ein mit Bash, dash und sh kompatibles ACME-Shell-Skript, das eine vollständige Implementierung des ACME-Protokolls bietet. Es unterstützt ECDSA-, SAN- und Wildcard-Zertifikate und kommt ohne Python-Abhängigkeiten daher. Es benötigt keinen root/sudoer-Zugang.

Installation

email='root@example.com'
curl https://raw.githubusercontent.com/acmesh-official/acme.sh/master/acme.sh | sh -s -- --install-online \
    --home /opt/acme.sh \
    --config-home /etc/acme.sh \
    --cert-home  /etc/acme.sh/certs \
    --accountemail  "$email" \
    --accountkey  /etc/acme.sh/account.key \
    --accountconf /etc/acme.sh/account.conf \
    --no-cron

Dabei können folgende Fehler ignoriert werden (es versucht, UPGRADE_HASH in der config zu speichern - das wird sowieso beim nächsten Update gemacht):

touch: cannot touch '/root/.acme.sh/account.conf': No such file or directory
grep: /root/.acme.sh/account.conf: No such file or directory
grep: /root/.acme.sh/account.conf: No such file or directory
sh: line 2249: /root/.acme.sh/account.conf: No such file or directory
grep: /root/.acme.sh/account.conf: No such file or directory

Neues Zertifikat ausstellen

Achtung

Das Let’s Encrypt API hat gewisse Rate-Limits, es empfiehlt sich also, die Konfiguration zuerst mithilfe von curl oder gegen die staging environment zu testen. Vor allem bei „Failed Validation“ sind die Limits sehr niedrig, man wird schon bei 5 Fehlschlägen pro Account, pro Hostname und pro Stunde gesperrt.

DOMAIN=www.example.com

Die von uns favorisierte Variante ist die Erstellung per Webroot. Diese stoppt den Webserver nicht, benötigt aber auf dem Webserver oder Reverse Proxy eine Konfiguration.

Am Beispiel des Apache: Config für den Webroot anlegen (unterhalb von /etc/httpd/conf.d oder passend):

Alias /.well-known/acme-challenge/ /var/www/html/letsencrypt/.well-known/acme-challenge/
<Directory "/var/www/html/letsencrypt/">
    AllowOverride None
    Options None
    Require method GET POST OPTIONS
</Directory>

Ein http-zu-https-Redirect in einem vHost sieht dann so aus:

<VirtualHost *:80>
    ServerName www.example.com

    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [redirect=301,last]
</VirtualHost>

Das Webroot-Verzeichnis für die Challenge-Antworten anlegen und eine Test-Datei hinterlegen (die Ownership muss zum HTTPD-User passen - apache auf RHEL, www-data auf Debian/Ubuntu):

# RHEL-based
install --directory --owner apache --group apache --mode 0755 \
    /var/www/html/letsencrypt/.well-known/acme-challenge

# Debian/Ubuntu
install --directory --owner www-data --group www-data --mode 0755 \
    /var/www/html/letsencrypt/.well-known/acme-challenge

echo 'it works' > /var/www/html/letsencrypt/.well-known/acme-challenge/index.html

curl http://$DOMAIN/.well-known/acme-challenge/index.html
# -> "it works"

Erst wenn die Test-Datei erreichbar ist, das Zertifikat ausstellen lassen. Für SAN-Namen werden weitere --domain-Flags angehängt:

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --issue \
    --domain "$DOMAIN" \
    --webroot /var/www/html/letsencrypt \
    --keylength 4096 \
    --server https://acme-v02.api.letsencrypt.org/directory

Anschliessend die Zertifikats-Dateien an die gewünschten Pfade kopieren und den Reload-Hook setzen. Die Linuxfabrik-Konvention für die Zieldateinamen (aus der LFOps-Rolle):

# RHEL-based
/opt/acme.sh/acme.sh --config-home /etc/acme.sh --install-cert \
    --domain "$DOMAIN" \
    --ca-file       "/etc/pki/tls/certs/$DOMAIN-chain.crt" \
    --cert-file     "/etc/pki/tls/certs/$DOMAIN.crt" \
    --fullchain-file "/etc/pki/tls/certs/$DOMAIN-fullchain.crt" \
    --key-file      "/etc/pki/tls/private/$DOMAIN.key" \
    --reloadcmd     "systemctl reload httpd"

# Debian/Ubuntu: paths /etc/ssl/certs and /etc/ssl/private, reload cmd
# "systemctl reload apache2"
  • Die Config wird in /etc/acme.sh/certs/$DOMAIN/$DOMAIN.conf gespeichert, das Le_ReloadCmd ist base64-encoded.

  • install-cert kann nur ein Target pro Aufruf setzen. Ein zweiter Aufruf überschreibt die zuvor hinterlegte Konfiguration.

  • Bei einem erfolgreichen Renewal wird das hinterlegte --reloadcmd automatisch ausgeführt - der Webserver erkennt das neue Zertifikat ohne weiteres Zutun.

Die so abgelegten Zertifikate lassen sich direkt in Apache referenzieren:

SSLEngine on
SSLCertificateFile      /etc/pki/tls/certs/www.example.com.crt
SSLCertificateKeyFile   /etc/pki/tls/private/www.example.com.key
SSLCertificateChainFile /etc/pki/tls/certs/www.example.com-chain.crt

Auf Debian/Ubuntu liegen die Dateien unter /etc/ssl/certs bzw. /etc/ssl/private:

SSLEngine on
SSLCertificateFile      /etc/ssl/certs/www.example.com.crt
SSLCertificateKeyFile   /etc/ssl/private/www.example.com.key
SSLCertificateChainFile /etc/ssl/certs/www.example.com-chain.crt

Zertifikate mit acme.sh verwalten

Alle Kommandos unten gehen davon aus, dass acme.sh wie oben installiert wurde (/opt/acme.sh als Home, /etc/acme.sh als Config-Home, --no-cron). Das vereinfachende Shell-Alias, das der Installer per Default in ~/.bashrc ablegt, entfällt beim Einsatz von --no-cron bzw. wird auf Nicht-Login-Sessions ohnehin nicht aktiv - daher der vollständige Pfad in allen Beispielen.

Alle Zertifikate anzeigen lassen:

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --list

Renew einer Domain:

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --renew --domain "$DOMAIN"

Renew forcieren:

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --renew --domain "$DOMAIN" --force

acme.sh selbst auf den neuesten Stand bringen:

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --upgrade

Zertifikat löschen (anschliessend die Zertifikate von Hand aus den Zielpfaden entfernen und den Webserver reloaden):

/opt/acme.sh/acme.sh --config-home /etc/acme.sh --revoke --domain "$DOMAIN"
/opt/acme.sh/acme.sh --config-home /etc/acme.sh --remove --domain "$DOMAIN"

Automatisches Renewal per systemd-Timer

Die Linuxfabrik-Konvention (aus der LFOps-Rolle) ist ein wöchentlicher systemd-Timer. Der Service macht vor jedem Renewal-Lauf ein Self-Upgrade von acme.sh (ExecStartPre):

/etc/systemd/system/acme-sh.service
[Unit]
Description=acme.sh Service

[Service]
ExecStartPre=/opt/acme.sh/acme.sh --upgrade --home /opt/acme.sh --config-home /etc/acme.sh
ExecStart=/opt/acme.sh/acme.sh --cron --home /opt/acme.sh --config-home /etc/acme.sh
Type=oneshot
User=root

[Install]
WantedBy=basic.target
/etc/systemd/system/acme-sh.timer
[Unit]
Description=acme.sh Timer

[Timer]
OnCalendar=Sun 01:17:00
Unit=acme-sh.service

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now acme-sh.timer

Der vom acme.sh-Installer angebotene --install-cronjob-Mechanismus wird bewusst nicht verwendet, damit Service-Definition und Ausführungszeit über die oben gezeigten Unit-Dateien reproduzierbar gepflegt werden.

Remote-Deployment per SSH

Soll ein frisch ausgestelltes Zertifikat nicht lokal, sondern auf einem anderen Host installiert werden (typisch: ACME-Host in der DMZ, Webserver oder Loadbalancer im internen Netz), nutzt acme.sh seinen --deploy --deploy-hook ssh-Mechanismus. Die Ziel-Hostparameter kommen über Umgebungsvariablen:

DEPLOY_SSH_USER='root' \
DEPLOY_SSH_SERVER='proxy01.example.com' \
DEPLOY_SSH_CERTFILE='/etc/pki/tls/certs/www.example.com.crt' \
DEPLOY_SSH_KEYFILE='/etc/pki/tls/private/www.example.com.key' \
DEPLOY_SSH_FULLCHAIN='/etc/pki/tls/certs/www.example.com-fullchain.crt' \
DEPLOY_SSH_CAFILE='/etc/pki/tls/certs/www.example.com-chain.crt' \
DEPLOY_SSH_REMOTE_CMD='systemctl reload haproxy' \
/opt/acme.sh/acme.sh --config-home /etc/acme.sh --deploy \
    --domain 'www.example.com' \
    --deploy-hook ssh

Voraussetzung: SSH-Key-basiertes Login für den konfigurierten DEPLOY_SSH_USER auf dem Zielhost. Die LFOps-Rolle setzt das Deployment bei definiertem acme_sh__deploy_to_host automatisch um.

Troubleshooting

Siehe ACME und Let’s Encrypt, Abschnitt Troubleshooting. Dort sind die typischen ACME-Fehler unabhängig vom Client-Tool beschrieben (Rate-Limit, fehlgeschlagene Authorization, Port-80-Erreichbarkeit, nicht eingezogener Reload).