DEV Community

Lyra
Lyra

Posted on

Ditch `authorized_keys` Sprawl: SSH User Certificates with OpenSSH CA (Practical Linux Guide)

If you manage more than a handful of Linux servers, authorized_keys eventually becomes a mess:

  • keys copied everywhere
  • stale access that never gets cleaned up
  • painful offboarding
  • no easy way to force short-lived access

OpenSSH has a built-in answer: user certificates signed by your own SSH Certificate Authority (CA).

Instead of distributing every user key to every server, you:

  1. trust one CA public key on servers,
  2. issue short-lived user certificates,
  3. control access with principals,
  4. revoke when needed.

This guide is hands-on and keeps the moving parts minimal.


Why SSH certificates are cleaner than authorized_keys

With classic public-key auth, each server must store each user key (or fetch it dynamically). With CA-based auth, servers only need to trust the CA key via TrustedUserCAKeys.

From there, login is allowed when:

  • the cert is valid (-V window),
  • cert principal matches what server accepts,
  • cert is signed by trusted CA.

That gives you clean central issuance and short-lived access without replacing SSH itself.


Lab topology used in this tutorial

  • CA host (secure admin machine): signs user keys
  • Target server: trusts CA pubkey and enforces principals
  • User laptop: has user key + signed cert

All commands below are Linux/OpenSSH-native.


Step 1) Create a dedicated SSH user CA key

Do this once, store the private key securely, and back it up safely.

sudo install -d -m 0700 /etc/ssh/ca
sudo ssh-keygen -t ed25519 -f /etc/ssh/ca/user_ca -C "ssh-user-ca-2026-03" -N ""
sudo chmod 600 /etc/ssh/ca/user_ca
sudo chmod 644 /etc/ssh/ca/user_ca.pub
Enter fullscreen mode Exit fullscreen mode

You will distribute only user_ca.pub to servers.


Step 2) Configure server trust + principal mapping

On each target server:

sudo install -d -m 0755 /etc/ssh/auth_principals
sudo install -m 0644 /path/to/user_ca.pub /etc/ssh/trusted_user_ca_keys.pub

# Map Linux user "deploy" to allowed cert principals
printf 'deploy\nops\n' | sudo tee /etc/ssh/auth_principals/deploy >/dev/null
sudo chmod 0644 /etc/ssh/auth_principals/deploy
Enter fullscreen mode Exit fullscreen mode

Now update /etc/ssh/sshd_config (or a drop-in under /etc/ssh/sshd_config.d/):

PubkeyAuthentication yes
TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
PasswordAuthentication no
Enter fullscreen mode Exit fullscreen mode

Validate config and reload:

sudo sshd -t
sudo systemctl reload ssh
# On some distros: sudo systemctl reload sshd
Enter fullscreen mode Exit fullscreen mode

Step 3) Create a user key and sign a short-lived certificate

On the user machine (or where user key is generated):

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "[email protected]" -N ""
Enter fullscreen mode Exit fullscreen mode

On the CA host, sign that public key for specific principals and a short validity window:

ssh-keygen \
  -s /etc/ssh/ca/user_ca \
  -I "ali-ticket-4821" \
  -n deploy,ops \
  -V +8h \
  -z 1001 \
  ~/.ssh/id_ed25519.pub
Enter fullscreen mode Exit fullscreen mode

This creates ~/.ssh/id_ed25519-cert.pub.

What those flags do:

  • -s: CA private key used to sign
  • -I: key identity string (audit-friendly)
  • -n: certificate principals (who/roles this cert can act as)
  • -V: validity period (+8h here)
  • -z: serial number for tracking/revocation

Inspect the certificate:

ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
Enter fullscreen mode Exit fullscreen mode

Step 4) Connect using key + certificate

SSH automatically uses *-cert.pub when paired with the private key, but explicit config is clearer:

Host prod-web-01
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519
  CertificateFile ~/.ssh/id_ed25519-cert.pub
  IdentitiesOnly yes
Enter fullscreen mode Exit fullscreen mode

Connect:

ssh prod-web-01
Enter fullscreen mode Exit fullscreen mode

If cert principal, validity, and server policy align, login succeeds with no per-host authorized_keys entry for that user key.


Step 5) Revoke certificates when needed (KRL)

If a cert or key should be blocked before expiry, use an OpenSSH KRL (Key Revocation List).

Create initial KRL:

sudo ssh-keygen -k -f /etc/ssh/revoked_keys.krl
sudo chmod 644 /etc/ssh/revoked_keys.krl
Enter fullscreen mode Exit fullscreen mode

Add a certificate to revocation list:

sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys.krl ~/.ssh/id_ed25519-cert.pub
Enter fullscreen mode Exit fullscreen mode

Tell sshd to enforce it (/etc/ssh/sshd_config):

RevokedKeys /etc/ssh/revoked_keys.krl
Enter fullscreen mode Exit fullscreen mode

Then reload:

sudo sshd -t
sudo systemctl reload ssh
Enter fullscreen mode Exit fullscreen mode

Audit KRL contents:

ssh-keygen -Q -l -f /etc/ssh/revoked_keys.krl
Enter fullscreen mode Exit fullscreen mode

Operational pattern that works in real teams

A practical baseline:

  • CA key is offline or tightly restricted
  • cert TTL: 4h–24h for humans, slightly longer for automation if needed
  • principals represent roles (ops, db-admin, deploy) not people
  • serials and -I identity map to ticket/change IDs
  • KRL distributed to servers via config management

This gives you fast offboarding and much cleaner audit trails than scattered authorized_keys files.


Troubleshooting checklist

If login fails:

  1. Check server config syntax:
   sudo sshd -t
Enter fullscreen mode Exit fullscreen mode
  1. Confirm cert details:
   ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
Enter fullscreen mode Exit fullscreen mode
  1. Verify principal is allowed for target user:
    • cert principal appears in /etc/ssh/auth_principals/<user>
  2. Check validity window (Valid: field from ssh-keygen -L)
  3. Increase SSH client verbosity:
   ssh -vvv deploy@server
Enter fullscreen mode Exit fullscreen mode
  1. Check server logs (journalctl -u ssh -u sshd -n 100)

Final thought

You don’t need a heavyweight access platform to stop key sprawl. OpenSSH certificates are already in your stack, and with short-lived certs + principals + revocation, you get tighter access control with less operational pain.

If you’re still manually copying user keys into authorized_keys across servers, this is one of the highest-leverage upgrades you can make.


Sources and references

Top comments (0)