Christian Lehnert — Linux, Hacking & Faith

SSH Certificates - The authorized_keys Habit You Should Have Dropped Years Ago

Christian Lehnert2026-08-04~8 min read

SSH Certificates

Where Everyone Stops

Every engineer learns the same SSH story. Generate a key pair, put
the public key in ~/.ssh/authorized_keys on the server, log in
without a password. It works, it feels secure, and that is where
almost everyone stops learning.

That stopping point is the problem. The authorized_keys model is
the part of SSH that does not scale, cannot be revoked cleanly, and
quietly accumulates risk on every server you run. It is fine for one
laptop and one box. It falls apart the moment you have a fleet, a
team, or a memory longer than the last person who left.

SSH has had the fix built in since 2010: a certificate authority. It
is in stock OpenSSH, needs no extra software, and solves the exact
problems authorized_keys creates. Almost nobody uses it. This is
the post arguing you should.

What authorized_keys Actually Costs

Think about what the model requires. Every server holds its own list
of every key allowed to enter it. Access is the set of files scattered
across your whole fleet, and there is no central view of it. To answer
"who can log into this box" you read a file on that box. To answer "what
can this person reach" you read a file on every box.

Three specific failures follow from that shape.

It does not scale. Ten servers and five people is manageable. A
hundred servers and twenty people is a distribution problem you will
solve badly, with a script that copies keys around and drifts out of
sync the first week.

It has no expiry. An SSH key never expires on its own. The key you
added for a contractor in 2019 still works today unless someone
remembered to remove it, on every server, by hand. Nobody remembered.
I find these in engagements constantly: a stale authorized_keys
entry for someone who left years ago, still granting a shell.

It cannot be revoked cleanly. Killing access means finding and
deleting that key from every file on every server it was ever added
to. Miss one and the access lives on. There is no single place to say
"this key is done."

The root issue is that access is stored on the servers, as a pile of
files, with no identity, no expiry, and no central control. That is a
fragile way to run the front door to everything you own.

What a Certificate Changes

An SSH certificate is a normal SSH public key with signed metadata
attached: who it identifies, how long it is valid, and what it is
allowed to do. The signature comes from a certificate authority,
which is just another SSH key pair you designate as trusted.

The shift is where trust lives. Instead of every server holding a
list of every allowed key, every server holds one thing: the public
key of the CA, and the instruction "trust anything this CA signed."
A user presents a certificate, the server checks the signature
against the CA it trusts, and if it is valid and unexpired, the login
proceeds. No authorized_keys entry required. The server never needed
to know the user's key in advance. It only needs to know the CA.

That one change fixes all three failures at once.

Onboarding is signing a certificate, not editing files on a hundred
servers. The new person gets a signed certificate and can reach every
server that trusts the CA, immediately, with nothing copied anywhere.

Offboarding is not signing new ones. If certificates are short-lived,
access ends on its own when the current certificate expires. Stop
signing and the person is locked out within hours, everywhere, with no
cleanup.

Expiry is built in. A certificate carries a validity window. Issue
them for eight or twenty-four hours and a leaked or forgotten
credential stops working on its own. The 2019-contractor problem
cannot exist, because no certificate lives that long.

Principals: Who You Are Allowed To Be

A certificate carries principals, which are the usernames it is
allowed to log in as. This is the piece that makes certificate access
safe rather than just convenient, and the piece people most often get
wrong by ignoring it.

Without restriction, any certificate the CA signed can log in as any
account, including root. That is too much. Principals bind a
certificate to specific accounts. A deploy certificate is valid for
the deploy user and nothing else. An admin certificate might be valid
for a named admin account and, deliberately, not for root. The server
can also decide per machine which principals it accepts, so a
certificate that works as deploy on the web tier does not
automatically work as deploy on the database tier.

This is least privilege applied to login identity, expressed in the
certificate itself. It is the difference between "this person is
trusted" and "this person is trusted to be this specific account on
these specific machines for the next eight hours."

Host Certificates Kill the TOFU Prompt

Everything so far authenticates users to servers. The same mechanism
runs the other direction, and this half is criminally underused.

You know the prompt. "The authenticity of host cannot be established,
are you sure you want to continue." That is Trust On First Use: SSH
has never seen this server, cannot verify it, and asks you to
eyeball a fingerprint you have never checked and will click through.
Everyone clicks through. It is a security control that has been
trained into a reflex, and a machine-in-the-middle on first connection
is exactly the case it fails to catch.

Host certificates end it. Sign each server's host key with a host CA,
tell your clients to trust that CA, and every server now proves its
identity cryptographically. The prompt disappears, not because it was
suppressed, but because the server can actually be verified. Roll a
server's host key and there is no scary warning on every client,
because trust is in the CA, not in a fingerprint pinned in a thousand
known_hosts files.

User certificates plus host certificates give you mutual
authentication with no TOFU on either side. The client proves who it
is, the server proves what it is, and neither one is trusting a file
that drifted out of date.

The Honest Costs

This is not free, and pretending otherwise is how people end up with a
CA they cannot trust.

The CA private key is now the crown jewel. Anyone who holds it can
sign a certificate for anything. It must not sit on an internet-facing
server. It belongs offline, or on dedicated signing infrastructure, or
backed by a hardware key or HSM so the private key never exists in the
open. If the CA key leaks, every server that trusts it is compromised
at once. You concentrated the risk in exchange for control, and you
have to guard the thing you concentrated it into.

Certificates only beat authorized_keys if you automate the signing.
A CA where developers email you a public key and wait for a manual
signature is worse than what it replaced. The workflow has to issue
short-lived certificates on demand against whatever identity you
already trust. Without that automation you built a bottleneck, not a
system.

And short-lived is doing real work in all of this. Long-lived
certificates bring back the revocation problem you adopted the CA to
escape. If a certificate is good for a year, you are back to needing a
revocation list to kill it early. If it is good for eight hours,
expiry is your revocation for almost every case, and the revocation
list becomes the rare exception rather than the routine tool. Short
validity is not a detail. It is the mechanism.

When To Bother

For a single laptop and one or two personal boxes, authorized_keys
is fine and a CA is overkill. Be honest about that. The machinery
earns its keep when you have a fleet, a team, onboarding and
offboarding that actually happen, or a homelab grown past the point
where you can name every key in every file from memory.

That last one is the tell for people like us. The moment you cannot
answer "what keys are in my authorized_keys files and who do they
belong to" without going and looking, you have outgrown the model. A
CA gives you back the single source of truth that a pile of files
never had.

The Point

Everyone learns SSH keys. Almost nobody takes the next step, and the
next step is where SSH stops being a pile of trusted files on every
server and becomes an actual identity system with expiry and central
control. The certificate authority has been sitting in OpenSSH for
over a decade. It solves the problems authorized_keys creates rather
than working around them.

Move trust to a CA, bind certificates to principals, issue them
short-lived, sign your host keys to kill TOFU, and guard the CA key
like it unlocks everything, because it does. The authorized_keys
file was where you learned SSH. It should not be where you still keep
your access.

Tagged:
#linux #ssh #security
← Back to posts