ACME-Clients
Was gleich bleibt wie Let’s Encrypt
Wenn du jemals ein Let’s-Encrypt-Zertifikat bezogen hast, kannst du die interne CA bereits benutzen. Das Protokoll ist identisch — ACME RFC 8555. Die Clients, die du schon kennst, funktionieren ohne Modifikation.
Gleich:
- HTTP-01- und DNS-01-Challenges.
- Account-Registrierung und Schlüsselverwaltung.
- 90-Tage-Default-Zertifikatslaufzeit.
- Automatische Erneuerung bei 60 Tagen.
- Wildcard-Zertifikate via DNS-01.
Was anders ist
Zwei kleine Unterschiede, beide einmalige Setup-Schritte:
- Die ACME-Directory-URL zeigt auf die interne CA, nicht auf
https://acme-v02.api.letsencrypt.org/directory. - Das Root-Zertifikat der internen CA muss vom anfragenden Client als vertrauenswürdig akzeptiert werden. Das wird in der Regel via Konfigurations-Management (Ansible, Puppet, MDM) beim Host-Provisioning eingerichtet.
Das ist der gesamte Unterschied. Alles andere ist derselbe Let’s-Encrypt-Workflow, den du schon kennst.
Client auf die interne CA zeigen
Der interne ACME-Endpoint ist unter einer stabilen internen URL
veröffentlicht (typischerweise
https://acme.example.internal/acme/acme/directory, aber der
genaue Pfad hängt vom Deployment ab). Hier folgen Rezepte für
gängige Clients.
certbot
certbot --server https://acme.example.internal/acme/acme/directory \
--email ops@example.internal \
--no-eff-email \
--agree-tos \
certonly --standalone -d service.example.internalBeim Renewal wird das --server-Flag in renewal/*.conf
persistiert, sodass der periodische certbot renew Cron es
automatisch übernimmt.
acme.sh
acme.sh --register-account \
--server https://acme.example.internal/acme/acme/directory \
-m ops@example.internal
acme.sh --issue \
--server https://acme.example.internal/acme/acme/directory \
--standalone \
-d service.example.internallego
lego --server=https://acme.example.internal/acme/acme/directory \
--email=ops@example.internal \
--domains=service.example.internal \
--http runCaddy
In deinem Caddyfile:
{
acme_ca https://acme.example.internal/acme/acme/directory
email ops@example.internal
}
service.example.internal {
reverse_proxy localhost:8080
}Caddy holt und erneuert das Zertifikat automatisch.
Traefik
In deiner dynamischen Konfiguration:
certificatesResolvers:
internal:
acme:
caServer: "https://acme.example.internal/acme/acme/directory"
email: ops@example.internal
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: webcertResolver: internal in den Router-Definitionen referenzieren.
Root-Zertifikat auf den Clients installieren
Damit die von der CA ausgestellten Zertifikate ohne Warnung vertraut werden, muss der CA-Root im System-Truststore jedes Hosts liegen, der die Zertifikate sieht.
Linux (Debian-/Devuan-Familie)
sudo cp internal-ca-root.crt /usr/local/share/ca-certificates/internal-ca-root.crt
sudo update-ca-certificatesLinux (RHEL-Familie)
sudo cp internal-ca-root.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extractOpenBSD / FreeBSD
# OpenBSD
sudo install -m 0644 internal-ca-root.crt /etc/ssl/internal-ca-root.crt
sudo cat /etc/ssl/cert.pem internal-ca-root.crt > /etc/ssl/cert.pem.new
sudo mv /etc/ssl/cert.pem.new /etc/ssl/cert.pemmacOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain internal-ca-root.crtWindows
certutil.exe -addstore -f "ROOT" internal-ca-root.crtFür Browser, die eigene Truststores pflegen (Firefox), muss das Root zusätzlich über Einstellungen → Datenschutz → Zertifikate → Zertifizierungsstellen importiert werden.
Diese Installation wird in der Regel durch Konfigurations- Management übernommen. Per Hand ist es für einen Einzelfall okay, skaliert aber nicht für eine Flotte.
Wildcard-Zertifikate
Wildcards werden über die DNS-01-Challenge ausgestellt — der Client weist Kontrolle über eine Domain nach, indem er einen TXT-Record anlegt. Das funktioniert für rein interne Domains, weil die interne CA TXT-Records auf jedem DNS-Server validieren kann, den du kontrollierst.
Beispiel mit acme.sh und dem BIND-nsupdate-Provider:
acme.sh --issue \
--server https://acme.example.internal/acme/acme/directory \
--dns dns_nsupdate \
-d '*.example.internal' \
-d 'example.internal'Der nsupdate-Provider muss gegen deinen internen DNS konfiguriert
sein — siehe die acme.sh-Dokumentation für die spezifischen
Umgebungsvariablen.
Erneuerung
Erneuerung funktioniert genauso wie bei Let’s Encrypt: der Client prüft die verbleibende Gültigkeit und erneuert, wenn der Schwellwert unterschritten ist (60 Tage für ein 90-Tage-Zert).
Für die meisten Deployments funktioniert der bestehende
Renewal-Cron einfach (certbot renew, acme.sh --cron, Caddys
automatische Erneuerung).
Bei Renewal-Fehlern:
- Ist die Directory-URL noch erreichbar? Eine Netzwerkänderung kann den Pfad vom Client zur CA gebrochen haben.
- Ist das Root-Zertifikat abgelaufen oder rotiert worden? Roots sind typischerweise 10 Jahre gültig; Intermediates kürzer. Eine Rotation, die nicht zu allen Clients propagiert ist, äußert sich als Renewal-Fehler.
- Ist der Challenge-Type noch angemessen? Ein Service, der früher für HTTP-01 erreichbar war, kann jetzt hinter einem Load-Balancer sitzen, der die Challenge-Antwort bricht.
Limits und Policies
Die interne CA setzt Per-Account-Policies durch — welche Domains jedes Konto Zertifikate anfragen darf, welche maximale Laufzeit erlaubt ist, welche Challenge-Types akzeptiert werden.
Wenn ein Request, der gestern funktioniert hat, plötzlich mit
policy denied-Fehler scheitert, hat sich die Policy geändert
(oder dein Account-Binding). Wende dich an deinen CA-Operator.
Der
Betrieb-Service ist der
Eskalationspfad, falls dein CA-Operator wir sind.