SSH Certificate Authority: Retire authorized_keys - 夜莺博客

SSH Certificate Authority: Retire authorized_keys

The default SSH trust model has two problems that only become visible at scale: every new host requires a human to accept a fingerprint, and every new user requires editing authorized_keys on every server they might touch. An SSH certificate authority fixes both. One user CA signs engineers' public keys, and hosts trust that CA; one host CA signs servers' public keys, and clients trust that CA instead of prompting. Access becomes a property of a signed certificate with a validity window and a set of principals, which turns offboarding from an inventory exercise into a calendar event. This guide covers the setup of both directions, the revocation model, and the constraints worth using.

The two directions of trust

Direction Signs Trusted by Config location
User CA User public keys Servers TrustedUserCAKeys in sshd_config
Host CA Host public keys Clients @cert-authority line in known_hosts

Create the CAs and sign keys

# one CA key pair per direction; protect the private keys properly
ssh-keygen -f user_ca -t ed25519 -C "ops user CA"
ssh-keygen -f host_ca -t ed25519 -C "ops host CA"

# sign a user key: principals + short validity
ssh-keygen -s user_ca -I alice@example.com -n alice,ops -V +8h -z 1001 ~/alice_id_ed25519.pub
# -> Signed user key alice_id_ed25519-cert.pub: id "alice@example.com" serial 1001 for alice,ops valid from ...

# sign a host key
ssh-keygen -s host_ca -I "web-01" -h -n web-01.example.com -V +52w /etc/ssh/ssh_host_ed25519_key.pub

-n is the important flag: for a user certificate it lists the principals (usernames) the certificate may log in as, and for a host certificate it lists the hostnames it is valid for. Short validity is the mechanism that makes revocation rare — a certificate that expires at the end of the workday needs no revocation process for a routine departure.

Server side: trust the user CA

# /etc/ssh/user_ca.pub  (public key only; private key never leaves the CA host)
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... ops user CA

# /etc/ssh/sshd_config.d/10-ca.conf
TrustedUserCAKeys /etc/ssh/user_ca.pub
HostKey /etc/ssh/ssh_host_ed25519_key
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

# validate before reloading - this catches typos before you lock yourself out
sshd -t && systemctl reload ssh
sshd -T | grep -i trustedusercakeys

Distro differences are worth checking once: Debian, Ubuntu and RHEL 9 include /etc/ssh/sshd_config.d/*.conf from the top of sshd_config, while older releases may not — verify with grep -i ^Include /etc/ssh/sshd_config. The service is ssh on Debian family, sshd on RHEL family. AuthorizedPrincipalsFile decouples certificate principals from Unix account names: rather than making the certificate principal equal the local username, you list which principals may become a given account, so role changes stay a property of the certificate.

Client side: trust the host CA

# /etc/ssh/ssh_known_hosts - ship this with configuration management
@cert-authority *.example.com,10.0.5.* ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...

# after this, no more fingerprint prompts, and new laptops trust the fleet on day one
ssh -o CertificateFile=~/.ssh/alice_id_ed25519-cert.pub alice@web-01.example.com

Keep the host pattern after @cert-authority restrictive — a bare * means the CA can vouch for any hostname on the internet, which is a much larger grant than intended. Deploy it in /etc/ssh/ssh_known_hosts so it also applies to users who never configure SSH themselves.

Revocation and constraints

# constraints at signing time
ssh-keygen -s user_ca -I "contractor" -n deploy -V +30d \
  -O force-command=/usr/local/bin/deploy.sh \
  -O source-address=10.20.0.0/24 contractor.pub

# revocation list (KRL)
ssh-keygen -k -f revoked.krl -s user_ca id_ed25519-cert.pub
# sshd_config: RevokedKeys /etc/ssh/revoked.krl

force-command and source-address are the constraints that make certificates attractive for automation accounts: a deploy key that can only run one script from one subnet has a blast radius you can state precisely. For revocation, a signed key revocation list beats editing files on hundreds of hosts; but if your validity windows are measured in hours or days, you will need it rarely, which is the point.

Operating the CA: where the key lives and who can use it

The CA private key is the single most valuable secret in this design: whoever holds it can mint a certificate that logs into any host that trusts the CA. That shapes the operational model more than any technical detail. Keep the user CA key on a hardened host — ideally an offline or narrowly accessible machine — with signing performed either there or through a small signing service that enforces policy and logs every issuance.

Decision Weak option Stronger option
Where the CA key lives Someone's laptop Dedicated host, restricted access, key never leaves
How signing happens Engineer runs ssh-keygen locally Signing service with policy and audit log
Validity One year by default Hours to days, scaled to the role
Unattended automation Long-lived certificate in CI Short-lived certificate issued per job

A signing script that wraps the policy is worth the twenty lines: it validates that the requested principals are ones the person or service is allowed to have, sets a validity appropriate to the role, and records the request. That log is what turns "who signed this certificate" from an investigation into a lookup.

Principal design: matching certificates to accounts

# name the principals after roles, not after people
ssh-keygen -s user_ca -I "alice@example.com" -n ops,deployer -V +12h alice.pub

# per-account mapping on the host: which principals may become this user
/etc/ssh/auth_principals/ubuntu:
ops
sre

/etc/ssh/auth_principals/root:
sre-breakglass

AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

Two design rules keep this maintainable. First, principals should describe capability, so that adding a capability to a person is a change to their certificate and not to files on four hundred servers. Second, the mapping file on each host should stay tiny: a list of principals per account, ideally identical across hosts of a class, which lets you audit access by reading one file and comparing it across the fleet. Avoid using per-person principals for shared accounts — the certificate identifies a person, and the account should be a role they are permitted to hold.

Rollout, and the day the CA key leaks

Roll out in this order: sign host certificates and pin the host CA in ssh_known_hosts via configuration management first, because a host-side change that breaks connectivity is worse than no change at all. Then enable TrustedUserCAKeys on a pilot group of hosts while leaving authorized_keys intact — the two mechanisms coexist, so the pilot is a non-event. Verify with a certificate login on a host where the private key is not in authorized_keys. Only then retire the old files, and consider keeping a break-glass local account with a known password on console-only access for the case where the certificate path is broken.

# prove the certificate path works while authorized_keys still exists
ssh -i ~/.ssh/alice_id_ed25519 alice@pilot-host 'echo cert-login-ok'
ssh-keygen -L -f ~/.ssh/alice_id_ed25519-cert.pub   # inspect principals and validity
ssh -o PreferredAuthentications=publickey -v alice@web-01 2>&1 | grep -i "certificate"

If the CA key is compromised, the response is not a revocation list — it is a fleet-wide change of trust: generate a new CA, replace TrustedUserCAKeys everywhere, and accept that during the changeover both keys must be trusted simultaneously. This is precisely why the CA key should be protected as tightly as it is, and why a signing service that never exposes the key is worth its complexity in any environment where an SSH CA is considered worth having at all.

FAQ

Do certificates work with SSH agent forwarding and jump hosts? Yes, and better than key distribution does: the certificate travels with the key, so a bastion jump with agent forwarding presents the certificate without extra configuration.

How do I handle contractors? Issue a certificate with a short validity and constraints: -O force-command to limit what runs, -O source-address to limit where it can be used, and principals scoped to one account. Expiry then handles the offboarding automatically.

Can I keep passwords for some hosts? You can, but mixing models is what makes access reviews hard. If a host cannot be managed by configuration management, at least keep its authorized_keys in one place and generate it from the same source of truth as the certificate policy.

What about SELinux or AppArmor? Nothing in this design depends on them, and nothing in it bypasses them; a certificate login becomes the local account and is subject to exactly the same confinement as a password login.

Quick reference: sign, trust, verify, revoke

# sign a user key for a role, valid for a working day
ssh-keygen -s user_ca -I alice@example.com -n ops -V +12h alice.pub

# host trusts the user CA
TrustedUserCAKeys /etc/ssh/user_ca.pub

# clients trust the host CA
@cert-authority *.example.com ssh-ed25519 AAAA...

# inspect what a certificate actually grants
ssh-keygen -L -f alice-cert.pub | grep -E "Type|Principals|Valid|Extensions"

# revoke one certificate by serial
ssh-keygen -k -f revoked.krl -s user_ca alice-cert.pub

Keep the host pattern after @cert-authority as narrow as your naming allows, and check the Extensions block when auditing: a certificate carrying permit-pty and unrestricted commands is a full interactive login, while one carrying only force-command is a script runner. That single line in the certificate is often the clearest statement of what an access grant actually means.

Audit and compliance angles

An SSH CA changes what an access review looks like. Instead of enumerating authorized_keys files across hosts, you review two things: who can ask the CA to sign, and what constraints the signing service applies. The certificate serial and ID fields give you the audit trail, and the signing log gives you the who-requested-what record. That is a materially better position than a file-based review, where the honest answer to "who has access to this host" is often "we would have to check every account on it".

Two records are worth retaining for compliance purposes: the issuance log (identity, principals, validity, requesting host) and a periodic export of the certificates currently in circulation from the signing service. Together they let you answer, for any past date, which certificates were valid — the question an auditor actually asks, and one that file-based SSH access cannot answer reliably at all.

Related Reading

Related reading: OpenSSH Server Hardening: sshd_config Guide, SSH Tunneling: Local, Remote and Dynamic Port Forwarding, TACACS+ vs RADIUS for Network Device AAA: How to Choose

原文链接:https://liw.fi/sshca