Kurz gesagt. Bei authorized_keys führt jeder Server jeden öffentlichen Schlüssel auf, der sich anmelden darf, und der Zugang besteht, bis jemand die Zeile löscht. Bei OpenSSH-Zertifikaten vertrauen die Server einer einzigen Benutzer-CA (TrustedUserCAKeys). Benutzer lassen ihren eigenen Schlüssel mit ssh-keygen -s signieren; dabei kommen eine Identität (-I), die Konten, bei denen er sich anmelden darf (Principals, -n), und ein Gültigkeitszeitraum (-V) hinzu. Clients vertrauen einer einzigen Host-CA (@cert-authority in known_hosts), statt den Fingerabdruck jedes Hosts bei der ersten Verbindung zu akzeptieren. Mit kurzlebigen Zertifikaten erledigt sich das Offboarding von selbst, und den Rest übernehmen Sperrlisten, die Key Revocation Lists (RevokedKeys). Der Preis: Du betreibst jetzt eine CA und musst ihren Schlüssel schützen.
Was authorized_keys nicht leistet
~/.ssh/authorized_keys ist einfach und hat keine beweglichen Teile, deshalb ist es überall im Einsatz. Seine Grenzen zeigen sich, sobald mehr Leute und mehr Server dazukommen:
- Zugang läuft nie ab. Ein Schlüssel, der 2023 für einen externen Dienstleister eingetragen wurde, funktioniert 2026 noch immer auf jedem Server, auf den er kopiert wurde, sofern ihn niemand entfernt. (
expiry-time=gibt es pro Zeile, aber jemand muss es setzen.) - Keine Identität. Die Zeile besteht aus einem Schlüssel und einem Kommentar. Zu welcher Person, welchem Team oder welchem Job sie gehört, steht in einer Tabelle, wenn überhaupt irgendwo.
- Verteilung ist dein Problem. Wer jemanden hinzufügt oder entfernt, muss jeden Server anfassen, per Konfigurationsmanagement oder von Hand.
- Host-Schlüssel: Vertrauen beim ersten Kontakt (Trust on First Use). Jeder Client akzeptiert den Fingerabdruck jedes Servers beim ersten Mal, und wird ein Server neu aufgesetzt, erscheint überall
REMOTE HOST IDENTIFICATION HAS CHANGED.
OpenSSH-Zertifikate gibt es seit OpenSSH 5.4 (2010), und sie lösen alle vier Probleme. X.509 sind sie nicht: Das Format stammt von OpenSSH selbst, ein öffentlicher Schlüssel plus Identität und Einschränkungen, signiert von einem CA-Schlüssel, der selbst ein gewöhnlicher SSH-Schlüssel ist. 2025 wanderte die Formatbeschreibung aus der Datei PROTOCOL.certkeys in den OpenSSH-Quellen in einen Internet-Draft, heute draft-ietf-sshm-cert in der IETF-Arbeitsgruppe SSHM.
Zwei CAs, zwei Richtungen
Du brauchst zwei getrennte Signaturschlüssel, denn sie beantworten unterschiedliche Fragen:
- Die Benutzer-CA beantwortet die Frage „Darf sich dieser Schlüssel als dieses Konto anmelden?“ Server vertrauen ihr.
- Die Host-CA beantwortet die Frage „Ist das wirklich
web01?“ Clients vertrauen ihr.
$ ssh-keygen -q -t ed25519 -f user_ca -C "Example user CA" -N ""
$ ssh-keygen -q -t ed25519 -f host_ca -C "Example host CA" -N ""
-N "" hält den Testaufbau einfach. Ein echter CA-Schlüssel gehört in ein HSM, ein Hardware-Token oder einen Signaturdienst (die SSH Secrets Engine von HashiCorp Vault, smallstep, Teleport und entsprechende Dienste der Cloud-Anbieter leisten das alle). Wer den Schlüssel der Benutzer-CA besitzt, kann sich als jeder anmelden, den die Server zulassen.
Benutzerzertifikate
Signiere Alices vorhandenen öffentlichen Schlüssel für acht Stunden, für die Konten alice und deploy:
$ ssh-keygen -s user_ca -I alice@example.test -n alice,deploy -V -5m:+8h -z 1001 id_ed25519.pub
Signed user key /home/alice-laptop/id_ed25519-cert.pub: id "alice@example.test" serial 1001 for alice,deploy valid from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
-Iist die Schlüssel-ID (Key ID). sshd schreibt sie bei jeder Anmeldung ins Log, also wähle eine Person oder einen Job, keinen Hostnamen.-nlistet die Principals auf, also die Namen, unter denen dieses Zertifikat auftreten darf. Laut Handbuch von ssh-keygen sind Zertifikate ohne Principals „für alle Benutzer oder Hosts gültig“, aber sshd 10.0 lehnt ein solches Benutzerzertifikat unterTrustedUserCAKeysab (Certificate lacks principal list). Setze-nimmer.-V -5m:+8hsetzt die Gültigkeit von vor fünf Minuten (als Puffer für abweichende Uhren) bis in acht Stunden.-zsetzt eine Seriennummer, auf die sich ein Widerruf beziehen kann.
ssh-keygen -L zeigt, was signiert wurde:
/home/alice-laptop/id_ed25519-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s
Signing CA: ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60 (using ssh-ed25519)
Key ID: "alice@example.test"
Serial: 1001
Valid: from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
Principals:
alice
deploy
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
Standardmäßig gewährt ein Benutzerzertifikat alle fünf Erweiterungen. -O clear entfernt sie, danach fügst du nur wieder hinzu, was du brauchst, zum Beispiel -O clear -O permit-pty. Kritische Optionen schränken das Zertifikat ein. force-command ersetzt jeden Befehl, den der Client anfordert, und source-address begrenzt, von wo aus es verwendet werden darf. Mit source-address=10.0.0.0/8 wird eine Anmeldung von 127.0.0.1 abgelehnt. Kommt die Anmeldung von einer erlaubten Quelle, läuft der erzwungene Befehl, ganz gleich, was der Client angefordert hat:
Critical Options:
force-command echo backup only
source-address 10.0.0.0/8
$ ssh deploy@web01.example.test
deploy@web01.example.test: Permission denied (publickey).
cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).
Refused by certificate options
...
$ ssh deploy@web01.example.test "rm -rf /tmp/anything"
backup only
Starting session: forced-command (key-option) 'echo backup only' for deploy from 127.0.0.1 port 48976 id 0
Der Client ssh findet das Zertifikat automatisch, wenn es als id_ed25519-cert.pub neben dem privaten Schlüssel liegt. Andernfalls verweist du mit CertificateFile darauf. ssh-add lädt beides in den Agenten.
sshd konfigurieren
Die gesamte Serverseite besteht aus ein paar Zeilen. Diese Drop-in-Datei hat sshd -t bestanden und wurde für jeden Test auf dieser Seite verwendet:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked_keys
PasswordAuthentication no
LogLevel VERBOSE
TrustedUserCAKeysenthält einen oder mehrere öffentliche Schlüssel von CAs. Während einer CA-Rotation trägst du beide ein.AuthorizedPrincipalsFileordnet Konten Principals zu. Hier enthält/etc/ssh/auth_principals/deployeine einzige Zeile,deploy, sodass sich nur Zertifikate, die den Principaldeployaufführen, alsdeployanmelden dürfen. Ohne diese Option verlangt sshd, dass der Kontoname selbst unter den Principals steht. Die Zuordnung ist flexibler: Ein Principal wieoncalllässt sich für viele Konten zulassen, undAuthorizedPrincipalsCommandkann ihn in einem Verzeichnisdienst nachschlagen.RevokedKeysverweist auf eine Sperrliste (siehe unten).LogLevel VERBOSEprotokolliert bei jeder Anmeldung die Schlüssel-ID und die Seriennummer des Zertifikats. Das ist der Audit-Trail, denauthorized_keysnie hatte.
Eine Anmeldung mit Alices Zertifikat, von beiden Seiten gesehen:
Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:IuzvI4QsLmSx1+6p3yrvlRx2wdl+LUCY1zXqSzssLAc, serial 0 ID "web01" CA ssh-ed25519 SHA256:+iie/YHPulJReHN2GQ8UvrorBVrtayD2T1MGxeg/QCQ valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Host 'web01.example.test' is known and matches the ED25519-CERT host certificate.
Offering public key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Server accepts key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Authenticated to web01.example.test ([127.0.0.1]:22) using "publickey".
Accepted publickey for deploy from 127.0.0.1 port 57432 ssh2: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s ID alice@example.test (serial 1001) CA ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60
Wenn du sshd_config nicht bearbeiten kannst, kann ein einzelnes Konto einer CA über seine eigene Datei authorized_keys vertrauen. Die Option principals= funktioniert wie die Principals-Datei:
cert-authority,principals="deploy-team" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK63...
$ ssh deploy@web01.example.test 'echo logged in as $(whoami)'
logged in as deploy
Host-Zertifikate
Signiere den Host-Schlüssel jedes Servers mit der Host-CA. -h macht daraus ein Host-Zertifikat, und die Principals sind die Namen, unter denen sich Clients verbinden:
$ ssh-keygen -s host_ca -I web01 -h -n web01.example.test,web01 -V -5m:+52w /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "web01" serial 0 for web01.example.test,web01 valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Lass dann HostCertificate auf das Ergebnis zeigen (wie in der sshd-Datei oben) und gib den Clients eine einzige Zeile in known_hosts (oder /etc/ssh/ssh_known_hosts) statt eines Fingerabdrucks pro Host:
@cert-authority *.example.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtSsqoqxwcl31DTJg3kTPsv9eLuuw5gU1BcvYybODlu Example host CA
Neue und neu aufgesetzte Server gelten dann schon bei der ersten Verbindung ohne Rückfrage als vertrauenswürdig, sofern ihr Zertifikat den Namen aufführt, den der Client verwendet hat. Genau diese Bedingung sorgt regelmäßig für Überraschungen. Verbindet sich der Client unter einem Namen, der kein Principal ist, fällt er auf einfache Host-Schlüssel zurück, und bei strikter Prüfung bricht er ab:
$ ssh deploy@db01.example.test
Certificate invalid: name is not a listed principal
No ED25519 host key is known for db01.example.test and you have requested strict checking.
Host key verification failed.
Führe jeden Namen auf, den Clients verwenden, auch Kurznamen, CNAMEs, IP-Adressen, falls sich Leute per IP verbinden, und Namen von Load Balancern. ssh-keyscan -c ruft das Zertifikat eines Servers zur Untersuchung ab, und Zeilen mit @revoked in known_hosts weisen einen bestimmten Host-Schlüssel oder eine CA zurück.
Wie Fehler aussehen
Bei Problemen mit Benutzerzertifikaten meldet der Client immer nur Permission denied (publickey). Den Grund findest du im Log des Servers, bei LogLevel VERBOSE:
| Situation | sshd-Log (OpenSSH 10.0p2) |
|---|---|
| Konto nicht unter den Principals des Zertifikats (nach der Zuordnung) | Certificate does not contain an authorized principal |
Zertifikat ohne -n ausgestellt, kein AuthorizedPrincipalsFile | Certificate lacks principal list |
Zertifikat nach Ablauf seines Gültigkeitszeitraums (-V) | Certificate invalid: expired |
| Seriennummer oder Schlüssel in der KRL | Authentication key ED25519-CERT SHA256:... revoked by file /etc/ssh/revoked_keys |
source-address passt nicht | cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1). |
Zwei Dinge, die wie Zertifikatsprobleme aussehen, aber keine sind:
- Abweichende Uhren. Die Gültigkeit wird gegen die Uhr des Servers geprüft. Stelle Zertifikate aus, deren Gültigkeit ein paar Minuten in der Vergangenheit beginnt (
-V -5m:...), und halte die Server per NTP synchron. - Strafzeiten nach wiederholten Fehlschlägen. Seit OpenSSH 9.8 belegt sshd Adressen, deren Authentifizierung immer wieder scheitert, mit Strafzeiten (
PerSourcePenalties, standardmäßig aktiv). Beim Testen von Zertifikaten von einem einzigen Rechner aus führte ein fehlgeschlagener Versuch dazu, dass die nächste Verbindung noch vor der Authentifizierung getrennt wurde:
srclimit_penalise: 127.0.0.1/32: activating ipv4 penalty of 19 seconds for penalty: failed authentication
$ ssh deploy@web01.example.test
Connection closed by 127.0.0.1 port 22
drop connection #0 from [127.0.0.1]:55080 on [127.0.0.1]:22 penalty: failed authentication
Meldet ssh direkt nach einer Reihe von Fehlschlägen Connection closed, warte die Strafzeit ab, bevor du deine Konfiguration erneut änderst.
Widerruf mit KRLs
Kurze Gültigkeit ist der wichtigste Widerrufsmechanismus: Ein Zertifikat, das acht Stunden gilt, erledigt sich von selbst. Für den Rest hat OpenSSH Key Revocation Lists (KRLs, seit OpenSSH 6.2), Sperrlisten in einem kompakten Binärformat, die nach Seriennummer, Schlüssel-ID oder Schlüssel widerrufen können. So widerrufst du Alices Zertifikat mit der Seriennummer 1001:
$ printf 'serial: 1001\n' > /tmp/krl-spec
$ ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca.pub /tmp/krl-spec
Revoking from /tmp/krl-spec
$ ssh-keygen -Q -f /etc/ssh/revoked_keys id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub (alice@laptop): REVOKED
sshd liest RevokedKeys bei jeder Authentifizierung, die nächste Anmeldung scheitert also ohne Neustart. Mit -u aktualisierst du eine vorhandene KRL, statt sie zu ersetzen. Verteile die KRL mit denselben Tools wie den CA-Schlüssel. Ein Server mit veralteter KRL akzeptiert alles, von dessen Widerruf er nichts weiß. Das ist ein Grund mehr, Zertifikate kurzlebig zu halten und die KRL als Sicherheitsnetz für die verbleibenden Stunden zu betrachten.
Im direkten Vergleich
| authorized_keys | Zertifikate | |
|---|---|---|
| Zugang gewähren | Schlüssel auf jeden Server kopieren | Schlüssel einmal signieren |
| Zugang entziehen | Auf jedem Server löschen | Ablaufen lassen oder in die KRL eintragen |
| Ablauf | Optional pro Zeile (expiry-time=) | Jedes Zertifikat hat einen Gültigkeitszeitraum |
| Identität in Logs | Fingerabdruck des Schlüssels | Schlüssel-ID und Seriennummer |
| Einschränkungen | from=, command=, restrict pro Zeile | source-address, force-command, Erweiterungen pro Zertifikat |
| Vertrauen in Hosts | Fingerabdruck pro Host (TOFU) | Eine Zeile @cert-authority |
| Kritisches Geheimnis | Keines an zentraler Stelle | Der private Schlüssel der CA |
| Bewegliche Teile | Keine | Ausstellung, Schutz der CA, Verteilung der KRL |
Die Einschränkungen pro Zeile in authorized_keys funktionieren tatsächlich, getestet auf demselben Server:
restrict,from="10.0.0.0/8",command="echo backup only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
/home/deploy/.ssh/authorized_keys:1: Authentication tried for deploy with correct key but not from a permitted host (host=127.0.0.1, ip=127.0.0.1, required=10.0.0.0/8).
expiry-time="20261001" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
Für eine Handvoll Server und Leute reicht authorized_keys mit restrict und expiry-time völlig aus. Zertifikate lohnen sich, sobald Onboarding, Offboarding und Neuaufbauten von Hosts jede Woche anfallen oder wenn du wissen musst, wer sich angemeldet hat, nicht nur mit welchem Schlüssel.
Umstieg von authorized_keys
Du brauchst keinen Stichtag. sshd prüft authorized_keys und vertrauenswürdige CAs nebeneinander, sodass während der Umstellung beide Arten der Anmeldung funktionieren:
- Rolle zuerst das Vertrauen in die CA aus. Verteile
TrustedUserCAKeys, die Principals-Dateien und eine leere KRL auf alle Server und lassauthorized_keysunangetastet. - Stelle einem ersten Team Zertifikate aus und lass es sie ein paar Wochen lang nutzen. Jede Anmeldung erscheint jetzt im Log mit
ID ... (serial ...). - Finde die Nachzügler. Eine Anmeldung über
authorized_keyswird ohne Zertifikats-ID protokolliert, zum BeispielAccepted publickey for deploy from 127.0.0.1 port 58770 ssh2: ED25519 SHA256:tT5X32Wds5InYaLSh7mV+QRqAtxA4KXwlKV3hkf0YkI. Meist steckt Automatisierung dahinter: CI-Runner, Backup-Jobs, Monitoring. Gib jedem davon ein eigenes kurzlebiges oder eng eingeschränktes Zertifikat. - Schalte die Schlüsseldateien ab, sobald nichts Legitimes sie mehr nutzt: mit
AuthorizedKeysFile none. Halte einen Notfallzugang bereit, etwa Konsolenzugriff oder eine separate Notfall-CA, die offline aufbewahrt wird, für den Tag, an dem der Signaturdienst ausfällt. - Kümmere dich separat um die Host-Seite. Signiere die Host-Schlüssel und trage die Zeile
@cert-authorityin die globale/etc/ssh/ssh_known_hostsder Clients ein. Die alten Einträge pro Host inknown_hostskönnen bleiben. Diese funktionieren weiter, bis sich die Host-Schlüssel ändern.
Betrieb in der Praxis
- Stelle Zertifikate über deinen Identity Provider aus. Ein Signaturdienst prüft die SSO-Anmeldung des Benutzers (OIDC) und gibt ein Zertifikat mit dessen Principals zurück, das ein paar Stunden gilt. Niemand hantiert mit dem CA-Schlüssel, und wer das Unternehmen verlässt, hat spätestens am nächsten Tag keinen SSH-Zugang mehr, ohne dass jemand einen Server anfasst.
- Halte den Benutzerschlüssel nach Möglichkeit in Hardware.
ssh-keygen -t ed25519-skerzeugt einen FIDO2-gestützten Schlüssel. Zertifikate funktionieren damit wie mit jedem anderen Schlüssel. - Rotiere die CA mit Überlappung. Trage die neue Benutzer-CA als zweite Zeile in
TrustedUserCAKeysein oder die neue Host-CA als zweite Zeile mit@cert-authority. Stelle Zertifikate mit der neuen CA aus und entferne dann die alte. - Behalte auch die Host-Seite im Blick. Signiere Host-Schlüssel neu, bevor ihre Zertifikate ablaufen. Ein abgelaufenes Host-Zertifikat lässt Clients auf einfache Host-Schlüssel zurückfallen, die sie meist nicht haben:
Certificate invalid: expired
No ED25519 host key is known for web01.example.test and you have requested strict checking.
Host key verification failed.
Willst du eine Schlüssel- oder Zertifikatsdatei untersuchen, ohne etwas auszuführen, dekodiert der SSH-Schlüsselinspektor OpenSSH-Schlüssel und zeigt Typ, Länge und Fingerabdruck. Das SSH-Server-Audit prüft, was ein Server anbietet, einschließlich seiner Algorithmen für Host-Schlüssel und Zertifikate und des Post-Quanten-Schlüsselaustauschs.
Quellen
- OpenSSH-Manpages: ssh-keygen(1) (Abschnitte CERTIFICATES und KEY REVOCATION LISTS), sshd_config(5) (
TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys,HostCertificate,PerSourcePenalties), sshd(8) (Formate von authorized_keys und known_hosts). - draft-ietf-sshm-cert: SSH Certificate Format (der Nachfolger von
PROTOCOL.certkeysaus OpenSSH). PROTOCOL.krl: das KRL-Format. - Release Notes von OpenSSH: 5.4 (Zertifikate), 6.2 (KRLs), 9.8 (
PerSourcePenalties).
Alle Ausgaben auf dieser Seite wurden am 20. Oktober 2026 mit OpenSSH 10.0p2 (Pakete von Debian 13) in debian:trixie-20261005-slim erzeugt, mit Testschlüsseln, die im Container generiert wurden.