Every fleet reaches a point where authorized_keys files have drifted and nobody can say which key belongs to whom. Certificates fix this: hosts trust one CA, and access is granted by signing a key with an expiry.
1. Create a CA and trust it
ssh-keygen -t ed25519 -f user_ca -C 'user CA'
# on every host
echo "TrustedUserCAKeys /etc/ssh/user_ca.pub" >> /etc/ssh/sshd_config
2. Sign a key with an expiry
ssh-keygen -s user_ca -I 'chris@laptop'
-n chris,deploy -V +8h
-O clear -O permit-pty -O permit-agent-forwarding
id_ed25519.pub
-V +8h is the whole point. An eight-hour certificate cannot be found on an old laptop two years later and used. -O clear drops every default permission so the ones granted are explicit.
3. Verify what you issued
ssh-keygen -L -f id_ed25519-cert.pub
Read the principals and the validity window before handing it over. A certificate with permit-port-forwarding left on is a tunnel into the network that outlives the session it was issued for.
4. Host certificates too
The same mechanism removes the “unknown host key” prompt that everyone types yes to without reading. Sign the host keys with a host CA, put @cert-authority * ssh-ed25519 ... in known_hosts, and a genuine man-in-the-middle warning becomes a signal rather than noise.
5. Revocation
Short lifetimes are the primary control, but keep a revocation list for the case that cannot wait:
ssh-keygen -k -f revoked_keys -s user_ca id_ed25519.pub
# sshd_config: RevokedKeys /etc/ssh/revoked_keys