With plain SSH keys, every server keeps an authorized_keys list, and every client keeps a known_hosts list. With a few servers that’s fine. With dozens of servers and a changing team, it turns into sprawl: old keys never removed, new people waiting for their key to be copied everywhere, and users clicking “yes” on unknown host key warnings. SSH certificates solve both problems with a certificate authority (CA) that OpenSSH trusts natively. No extra software is needed.
How it works
- You create a user CA key pair. Servers trust it with one line in
sshd_config. - To give someone access, you sign their public key with the CA. The signed certificate states who they are (principals), and until when it’s valid.
- Servers accept any valid certificate signed by the CA for an allowed principal. There’s no per-user
authorized_keys.
Likewise, a host CA signs server host keys, so clients trust every server signed by it, with no more “authenticity of host can’t be established” prompts.
1. Create the user CA
On a secure machine, ideally offline or a dedicated admin box, not on the servers:
ssh-keygen -t ed25519 -f ssh_user_ca -C "user CA"
ssh_user_ca is the private CA key. Protect it like the keys to every server, because that’s what it is. ssh_user_ca.pub goes to the servers.
2. Trust the CA on each server
sudo cp ssh_user_ca.pub /etc/ssh/ssh_user_ca.pub
echo 'TrustedUserCAKeys /etc/ssh/ssh_user_ca.pub' | sudo tee /etc/ssh/sshd_config.d/20-ca.conf
sudo sshd -t && sudo systemctl reload ssh
Automate this across servers with Ansible or cloud-init. See Ansible playbooks.
3. Sign a user’s key
The user sends you their public key (id_ed25519.pub). You sign it:
ssh-keygen -s ssh_user_ca -I [email protected] -n deploy,alice -V +8h id_ed25519.pub
-I— the certificate ID, shown in server logs (who used it).-n— principals: the Unix usernames this certificate may log in as.-V +8h— valid for 8 hours. Short lifetimes mean expired access needs no revocation.
This creates id_ed25519-cert.pub. The user puts it next to their key. ssh picks it up automatically:
ssh deploy@web1
Inspect a certificate:
ssh-keygen -L -f id_ed25519-cert.pub
Server logs show the certificate ID:
Accepted publickey for deploy from 198.51.100.7 port 51234 ssh2: ED25519-CERT SHA256:… ID [email protected] (serial 0) CA ED25519 SHA256:…
Control who may log in as whom
By default, the principal must equal the target username. For more control, map principals per user on the server:
# /etc/ssh/sshd_config.d/20-ca.conf
TrustedUserCAKeys /etc/ssh/ssh_user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/principals/%u
echo -e "ops\nalice" | sudo tee /etc/ssh/principals/deploy
Now any certificate with principal ops can log in as deploy. Issue ops certificates to the ops team, dev certificates to developers, and grant groups of people access per server role.
Restrict certificate abilities at signing time:
ssh-keygen -s ssh_user_ca -I ci-deploy -n deploy -V +1h \
-O no-port-forwarding -O no-pty -O force-command=/usr/local/bin/deploy ci.pub
Host certificates: trusted servers
Create a host CA and sign each server’s host key:
ssh-keygen -t ed25519 -f ssh_host_ca -C "host CA"
# on/for each server:
ssh-keygen -s ssh_host_ca -I web1 -h -n web1.example.com,web1,203.0.113.10 -V +52w /etc/ssh/ssh_host_ed25519_key.pub
On the server, in sshd_config:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
On clients, in ~/.ssh/known_hosts (or /etc/ssh/ssh_known_hosts):
@cert-authority *.example.com ssh-ed25519 AAAA…contents-of-ssh_host_ca.pub…
Clients now trust every server signed by the host CA. A rebuilt server with a new host key doesn’t cause scary warnings, as long as it’s re-signed.
Revocation
Short-lived certificates usually make revocation unnecessary. For longer ones, maintain a key revocation list:
ssh-keygen -k -f /etc/ssh/revoked_keys -s ssh_user_ca.pub compromised-cert.pub
echo 'RevokedKeys /etc/ssh/revoked_keys' | sudo tee -a /etc/ssh/sshd_config.d/20-ca.conf
Making it practical
Signing by hand works for small teams. Larger teams automate issuance with tools that check identity (SSO) and issue short-lived certificates: step-ca (Smallstep), HashiCorp Vault’s SSH secrets engine, Teleport, or cloud IAM-based SSH.
Quick reference
| Task | Command |
|---|---|
| Create CA | ssh-keygen -t ed25519 -f ssh_user_ca |
| Trust CA (server) | TrustedUserCAKeys /etc/ssh/ssh_user_ca.pub |
| Sign user key | ssh-keygen -s ca -I id -n user -V +8h key.pub |
| Inspect cert | ssh-keygen -L -f key-cert.pub |
| Principals per user | AuthorizedPrincipalsFile /etc/ssh/principals/%u |
| Host cert | ssh-keygen -s host_ca -h -I host -n names key.pub |
| Trust host CA (client) | @cert-authority *.example.com ssh-ed25519 … |
Related: ssh-keygen, YubiKey SSH and key-only SSH.