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

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

Why SSH certificates are cleaner than authorized_keys

Lab topology used in this tutorial

Step 1) Create a dedicated SSH user CA key

Step 2) Configure server trust + principal mapping

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

Step 4) Connect using key + certificate

Step 5) Revoke certificates when needed (KRL)

Operational pattern that works in real teams

Troubleshooting checklist

Final thought If you manage more than a handful of Linux servers, authorized_keys eventually becomes a mess: 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: This guide is hands-on and keeps the moving parts minimal. 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: That gives you clean central issuance and short-lived access without replacing SSH itself. All commands below are Linux/OpenSSH-native. Do this once, store the private key securely, and back it up safely. You will distribute only user_ca.pub to servers. On each target server: Now update /etc/ssh/sshd_config (or a drop-in under /etc/ssh/sshd_config.d/): Validate config and reload: On the user machine (or where user key is generated): On the CA host, sign that public key for specific principals and a short validity window: This creates ~/.ssh/id_ed25519-cert.pub. Inspect the certificate: SSH automatically uses *-cert.pub when paired with the private key, but explicit config is clearer: If cert principal, validity, and server policy align, login succeeds with no per-host authorized_keys entry for that user key. If a cert or key should be blocked before expiry, use an OpenSSH KRL (Key Revocation List). Add a certificate to revocation list: Tell sshd to enforce it (/etc/ssh/sshd_config): A practical baseline: This gives you fast offboarding and much cleaner audit trails than scattered authorized_keys files. 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. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 600;">sudo -weight: 500;">install -d -m 0700 /etc/ssh/ca -weight: 600;">sudo ssh-keygen -t ed25519 -f /etc/ssh/ca/user_ca -C "ssh-user-ca-2026-03" -N "" -weight: 600;">sudo chmod 600 /etc/ssh/ca/user_ca -weight: 600;">sudo chmod 644 /etc/ssh/ca/user_ca.pub -weight: 600;">sudo -weight: 500;">install -d -m 0700 /etc/ssh/ca -weight: 600;">sudo ssh-keygen -t ed25519 -f /etc/ssh/ca/user_ca -C "ssh-user-ca-2026-03" -N "" -weight: 600;">sudo chmod 600 /etc/ssh/ca/user_ca -weight: 600;">sudo chmod 644 /etc/ssh/ca/user_ca.pub -weight: 600;">sudo -weight: 500;">install -d -m 0700 /etc/ssh/ca -weight: 600;">sudo ssh-keygen -t ed25519 -f /etc/ssh/ca/user_ca -C "ssh-user-ca-2026-03" -N "" -weight: 600;">sudo chmod 600 /etc/ssh/ca/user_ca -weight: 600;">sudo chmod 644 /etc/ssh/ca/user_ca.pub -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/ssh/auth_principals -weight: 600;">sudo -weight: 500;">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' | -weight: 600;">sudo tee /etc/ssh/auth_principals/deploy >/dev/null -weight: 600;">sudo chmod 0644 /etc/ssh/auth_principals/deploy -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/ssh/auth_principals -weight: 600;">sudo -weight: 500;">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' | -weight: 600;">sudo tee /etc/ssh/auth_principals/deploy >/dev/null -weight: 600;">sudo chmod 0644 /etc/ssh/auth_principals/deploy -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/ssh/auth_principals -weight: 600;">sudo -weight: 500;">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' | -weight: 600;">sudo tee /etc/ssh/auth_principals/deploy >/dev/null -weight: 600;">sudo chmod 0644 /etc/ssh/auth_principals/deploy PubkeyAuthentication yes TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u PasswordAuthentication no PubkeyAuthentication yes TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u PasswordAuthentication no PubkeyAuthentication yes TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u PasswordAuthentication no -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh # On some distros: -weight: 600;">sudo -weight: 500;">systemctl reload sshd -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh # On some distros: -weight: 600;">sudo -weight: 500;">systemctl reload sshd -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh # On some distros: -weight: 600;">sudo -weight: 500;">systemctl reload sshd ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "[email protected]" -N "" ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "[email protected]" -N "" ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "[email protected]" -N "" ssh-keygen \ -s /etc/ssh/ca/user_ca \ -I "ali-ticket-4821" \ -n deploy,ops \ -V +8h \ -z 1001 \ ~/.ssh/id_ed25519.pub ssh-keygen \ -s /etc/ssh/ca/user_ca \ -I "ali-ticket-4821" \ -n deploy,ops \ -V +8h \ -z 1001 \ ~/.ssh/id_ed25519.pub ssh-keygen \ -s /etc/ssh/ca/user_ca \ -I "ali-ticket-4821" \ -n deploy,ops \ -V +8h \ -z 1001 \ ~/.ssh/id_ed25519.pub ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub Host prod-web-01 HostName 203.0.113.10 User deploy IdentityFile ~/.ssh/id_ed25519 CertificateFile ~/.ssh/id_ed25519-cert.pub IdentitiesOnly yes Host prod-web-01 HostName 203.0.113.10 User deploy IdentityFile ~/.ssh/id_ed25519 CertificateFile ~/.ssh/id_ed25519-cert.pub IdentitiesOnly yes Host prod-web-01 HostName 203.0.113.10 User deploy IdentityFile ~/.ssh/id_ed25519 CertificateFile ~/.ssh/id_ed25519-cert.pub IdentitiesOnly yes ssh prod-web-01 ssh prod-web-01 ssh prod-web-01 -weight: 600;">sudo ssh-keygen -k -f /etc/ssh/revoked_keys.krl -weight: 600;">sudo chmod 644 /etc/ssh/revoked_keys.krl -weight: 600;">sudo ssh-keygen -k -f /etc/ssh/revoked_keys.krl -weight: 600;">sudo chmod 644 /etc/ssh/revoked_keys.krl -weight: 600;">sudo ssh-keygen -k -f /etc/ssh/revoked_keys.krl -weight: 600;">sudo chmod 644 /etc/ssh/revoked_keys.krl -weight: 600;">sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys.krl ~/.ssh/id_ed25519-cert.pub -weight: 600;">sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys.krl ~/.ssh/id_ed25519-cert.pub -weight: 600;">sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys.krl ~/.ssh/id_ed25519-cert.pub RevokedKeys /etc/ssh/revoked_keys.krl RevokedKeys /etc/ssh/revoked_keys.krl RevokedKeys /etc/ssh/revoked_keys.krl -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh -weight: 600;">sudo sshd -t -weight: 600;">sudo -weight: 500;">systemctl reload ssh ssh-keygen -Q -l -f /etc/ssh/revoked_keys.krl ssh-keygen -Q -l -f /etc/ssh/revoked_keys.krl ssh-keygen -Q -l -f /etc/ssh/revoked_keys.krl -weight: 600;">sudo sshd -t -weight: 600;">sudo sshd -t -weight: 600;">sudo sshd -t ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub ssh -vvv deploy@server ssh -vvv deploy@server ssh -vvv deploy@server - keys copied everywhere - stale access that never gets cleaned up - painful offboarding - no easy way to force short-lived access - trust one CA public key on servers, - issue short-lived user certificates, - control access with principals, - revoke when needed. - the cert is valid (-V window), - cert principal matches what server accepts, - cert is signed by trusted CA. - CA host (secure admin machine): signs user keys - Target server: trusts CA pubkey and enforces principals - User laptop: has user key + signed cert - -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 - 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 - Check server config syntax: - Confirm cert details: - Verify principal is allowed for target user: cert principal appears in /etc/ssh/auth_principals/<user> - cert principal appears in /etc/ssh/auth_principals/<user> - Check validity window (Valid: field from ssh-keygen -L) - Increase SSH client verbosity: - cert principal appears in /etc/ssh/auth_principals/<user> - Check server logs (journalctl -u ssh -u sshd -n 100) - OpenSSH ssh-keygen(1) manual (cert signing, validity, serials, KRL): https://man.openbsd.org/ssh-keygen.1 - OpenSSH sshd_config(5) manual (TrustedUserCAKeys, AuthorizedPrincipalsFile, RevokedKeys): https://man.openbsd.org/sshd_config - Linux man-pages mirror for sshd_config(5) (distribution-friendly reference): https://man7.org/linux/man-pages/man5/sshd_config.5.html - DEV API docs (publishing endpoint and payload shape): https://developers.forem.com/api