termique
Blog
Deep dive9 min read

SSH certificate authorities vs public key auth for teams

A practical comparison of SSH certificate authorities and public key auth for teams: how CA-based access works, when key sprawl becomes the real problem, and a decision framework f

SSH certificate authorities vs public key auth for teams

Every growing team eventually asks the same question about SSH access: keep handing out public keys and appending them to authorized_keys, or stand up an SSH certificate authority and stop trusting long-lived keys entirely. The debate over ssh certificate authorities vs public key auth for teams comes up the moment a team crosses from a few servers and a few admins into something that needs onboarding, offboarding, and an actual audit trail. Neither model is universally correct. It’s a genuine tradeoff between operational simplicity and centralized control, and picking wrong costs you either a fragile mess of forgotten keys or a CA nobody on the team understands how to run.

What is an SSH certificate authority, and how is it different from a public key?

With plain public key authentication, trust lives in two decentralized places: the private key on a user’s laptop, and a line in every server’s ~/.ssh/authorized_keys file that lists the matching public key. There’s no central authority vouching for anything. Each server independently decides which public keys it trusts, one entry at a time.

An SSH certificate authority (CA) changes the trust model. Instead of distributing raw public keys to every server, one CA signs a certificate that wraps a user’s (or host’s) public key together with an identity, a list of allowed principals (usernames or roles), and an expiration timestamp. Servers are configured once to trust the CA’s signing key, via TrustedUserCAKeys in sshd_config, and from then on they trust any certificate the CA has signed, without needing a new entry per user or per key rotation.

Why public key auth breaks down as a team grows

Public key auth is simple for one person managing three servers. It stops being simple somewhere around ten people and twenty hosts, for two structural reasons.

Key sprawl across servers and authorized_keys files

Every new hire needs their public key appended to every authorized_keys file on every server they’re allowed to touch. Every laptop replacement means finding and updating that same list again. Multiply a handful of engineers by a growing fleet of instances, bastion hosts, and staging boxes, and authorized_keys files drift: stale keys for people who left months ago, duplicate entries nobody remembers adding, and no single place to answer who can currently SSH into production. Managing multiple SSH keys across devices gets harder in direct proportion to headcount, not linearly, because each new person multiplies the number of files that need to change.

No built-in expiry, and revocation means touching every host

A raw SSH public key doesn’t expire on its own. If someone leaves the team, or a laptop gets lost, revoking access means going host by host and deleting the matching line from authorized_keys, or maintaining a separate revoked_keys file and keeping it in sync everywhere. Miss one server, and the access is still live. There’s no single command that revokes a key everywhere at once, because there was never a single place that granted it in the first place.

What SSH certificates actually fix

A CA-based model collapses that many-to-many relationship into a hub and spoke. Every server trusts one signing key instead of a list of individual users, and every certificate the CA issues carries its own expiration and identity.

Host certificates vs user certificates

step-ca and similar tools issue two kinds of SSH certificates. A user certificate is presented by the client and tells the server which principals (Linux usernames, roles) the bearer is allowed to log in as. A host certificate is presented by the server itself and lets the client verify it’s connecting to the real host, without the usual first-connection “the authenticity of host X can’t be established” prompt and the blind trust-on-first-use that comes with it.

Short-lived certs, principals, and a single trust anchor

Because certificates carry an expiration, a typical setup issues certificates valid for hours or a single working day, not indefinitely. A departing employee’s access simply expires, no cleanup required. Access changes, like adding a new principal or revoking a role, happen at the CA, not on every downstream server, and every certificate can be scoped to exactly the principals a person needs instead of a blanket key that works everywhere it’s been copied.

For the rarer case where access needs to be cut before a short-lived certificate naturally expires, sshd also supports a key revocation list via RevokedKeys in sshd_config, checked on every connection attempt in addition to the expiration timestamp. That gives a CA-based setup two independent ways to close off access: let a certificate age out on its own, or push an explicit revocation for the handful of cases that can’t wait.

How does a team set up an SSH certificate authority? A step-ca walkthrough

Running your own CA sounds like enterprise PKI, but open source tooling has made it a weekend project for a small team, not a dedicated security engineer’s job.

step ca init –ssh, TrustedUserCAKeys, and @cert-authority in known_hosts

Smallstep’s step-ca is a common starting point. Initialize it with the --ssh flag to generate both a user CA key pair, which signs certificates presented by clients, and a host CA key pair, which signs certificates presented by servers:

step ca init --ssh
step-ca $(step path)/config/ca.json

On each server, tell sshd to trust certificates signed by the user CA, and to present its own host certificate:

TrustedUserCAKeys /etc/ssh/ca_user_key.pub
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

On the client side, instead of trusting host keys on first connection, add the host CA’s public key to known_hosts once, prefixed with @cert-authority, and every host certificate that CA has signed is trusted automatically:

@cert-authority * ecdsa-sha2-nistp256 AAAA...hostCAkey...

Issuing a short-lived user certificate for a login session looks like:

step ssh certificate --provisioner devops alice@company.com ~/.ssh/id_ecdsa.pub --principal alice --not-after 8h

Teams already standardized on HashiCorp Vault can reach the same result through Vault’s SSH secrets engine, using vault write ssh/config/ca, vault write ssh/roles/<role>, and vault write ssh/sign/<role>, which issues certificates through Vault’s existing auth and policy layer instead of running a separate CA.

When is a CA overkill, and public key auth for teams still holds up

None of this is free. Running a CA means a new piece of infrastructure to keep available, since if it’s down nobody can get a fresh certificate, a provisioner and policy model to learn, and a migration that touches every server’s sshd config. For a five-person team on a handful of hosts, that overhead can outweigh the problem it solves.

What public key auth done well actually looks like

Public key auth for teams stays workable, well past the size most people assume, if it’s paired with actual discipline instead of everyone just pasting their key in: one key per person per device, never shared, a real rotation schedule instead of set once and forget forever, credentials and keys encrypted at rest rather than sitting as plaintext files in a home directory, and a per-command audit log so who ran what, on which host, when has an answer without a CA in the middle. A password and key manager built for this, rather than a shared spreadsheet or a folder of .pem files, closes most of the gap between the two models for a team that isn’t yet running dozens of hosts.

SSH certificate authorities vs public key auth for teams: a decision framework for 2026

The honest answer to ssh certificate authorities vs public key auth for teams depends on three things: how many hosts you manage, how often people join or leave, and whether you already run infrastructure, like Vault or an internal PKI, that makes standing up a CA closer to free.

  • Under roughly 15 hosts with low turnover: public key auth with per-user keys, rotation, and an audit log is usually enough.
  • Frequent host churn, such as autoscaling groups, ephemeral VMs, or CI runners: a CA pays for itself fast, since new hosts trust the CA on boot instead of needing a manual authorized_keys push.
  • Frequent people churn, such as contractors, interns, or on-call rotations: a CA’s short-lived certificates remove the question of whether a key was actually revoked everywhere.
  • Already running Vault or a similar secrets platform: use its SSH secrets engine rather than standing up a second, separate CA.
  • A compliance requirement for provable, time-bound access: certificates with expiration built in are easier to defend in an audit than a static key list.

Most teams don’t pick one model forever. A common path is public key auth with strong hygiene early on, then a CA once host count or turnover crosses a threshold that makes manual authorized_keys maintenance the actual bottleneck.

Migrating from authorized_keys to a CA without locking yourself out

The failure mode worth planning around is locking yourself out mid-migration. Do it in stages, not a single cutover:

  • Stand up the CA and issue certificates, but leave existing authorized_keys entries in place as a fallback.
  • Add TrustedUserCAKeys to one non-critical host first and confirm certificate-based login works end to end before touching production.
  • Roll HostCertificate out host by host, verifying @cert-authority trust from a client before removing the old host key prompt.
  • Only after every host accepts certificates reliably, start removing individual keys from authorized_keys, oldest and least-used first.
  • Keep at least one out-of-band access path, such as console access or a break-glass key held offline, until the CA has been the sole path for a full billing cycle.

Treat the CA’s signing key itself as the highest-value secret in the whole setup: back it up, decide who’s authorized to run provisioner commands against it, and rotate it on the same discipline you’d apply to a root credential. When a login fails during the migration, ssh -vvv against the target host shows whether the client even offered a certificate, which is usually faster than guessing at a misconfigured TrustedUserCAKeys path.

Keeping team SSH access auditable, whichever model you pick

Whichever side of ssh certificate authorities vs public key auth for teams you land on, the actual goal is the same: know who can reach which host, right now, and be able to prove it later. A CA gets you there through centralized, expiring identity. Disciplined public key auth gets you there through per-user keys, rotation, and logging. Both fall apart without visibility into what’s actually happening on each session.

This part is easy to skip regardless of which authentication model you pick. termique’s per-command audit log records what ran, on which host, and when, without requiring a CA to get that visibility, and its SSH key vault stores private keys encrypted at rest instead of as plaintext files scattered across a laptop. For a team weighing a CA migration against tightening up plain public key auth, that kind of session-level accountability is worth having either way, before deciding which trust model sits underneath it.

Try termique free.

SSH manager with end-to-end encrypted credentials, AI assistant, and cross-device sync.

Download free

Keep reading

All articles ⟶