Skip to content
Go back

SSH CA: Finally Ditch authorized_keys

By SumGuy 5 min read
SSH CA: Finally Ditch authorized_keys

The Moment You Realize You’ve Lost Control

It’s 2 AM. Your manager asks: “Do we still have SSH access for the contractor who left three months ago?” You SSH into your jump box and run cat ~/.ssh/authorized_keys on each of your 12 servers. Six servers later, you still don’t know the answer. Half the keys have comments from 2019. One just says # removed? maybe?. You find a key with Bob’s name on it, but was that Bob the DevOps guy or Bob the intern?

This is life without an SSH Certificate Authority.

You’ve been copy-pasting SSH keys across your infrastructure like it’s 2005. Every new person means updating authorized_keys on every server. Every person leaving means hoping you remember which key was theirs. Want to rotate keys? Enjoy visiting 15 machines. Need to revoke access instantly? Tough luck, you’re touching every box anyway.

There’s a better way, and it’s been in OpenSSH for over a decade.

What an SSH Certificate Authority Actually Is

An SSH CA is stupidly simple: you create a special key that signs other SSH keys, and your servers trust anything signed by that CA key. Instead of scattering public keys across authorized_keys files, you sign a user’s key once, they get a certificate valid for whatever you say, and every server accepts it automatically.

Here’s the magic: you don’t touch authorized_keys again. Ever. You manage access by:

  1. Deciding who can sign keys (just you, or a small trusted team)
  2. Deciding when certificates expire (could be 8 hours, could be a year)
  3. Revoking entire certificates if someone leaves (optional: just don’t re-sign their key)

It’s certificate-based authentication, like HTTPS for SSH.

Setting Up Your SSH CA

First, create the CA keypair. Keep this safe, this is your master key.

Terminal window
ssh-keygen -t ed25519 -f ca_key -C "my-infrastructure-ca" -N ""
# Generates ca_key (private) and ca_key.pub (public)

Now tell your servers to trust this CA. Edit /etc/ssh/sshd_config:

/etc/ssh/sshd_config
# Trust user certificates signed by this CA
TrustedUserCAKeys /etc/ssh/ca_key.pub
# Optional: keep using authorized_keys as a fallback
PubkeyAuthentication yes

Copy ca_key.pub to every server at /etc/ssh/ca_key.pub, then reload SSH:

Terminal window
scp ca_key.pub user@server:/tmp/
ssh user@server "sudo mv /tmp/ca_key.pub /etc/ssh/ && sudo systemctl reload sshd"

That’s it. Now every server trusts certificates signed by your CA.

Signing a User’s Key

When someone joins the team and gives you their public key (alice_key.pub), sign it:

Terminal window
ssh-keygen -s ca_key \
-I "alice-user@company" \
-n "alice,developers" \
-V "+52w" \
alice_key.pub
# Generates alice_key-cert.pub (valid for 52 weeks)

Breaking that down:

Give Alice two files: her private key (alice_key) and the certificate (alice_key-cert.pub). She puts both in ~/.ssh/:

Terminal window
ls -la ~/.ssh/
-rw------- alice_key
-rw-r--r-- alice_key-cert.pub

SSH will automatically use the certificate if both files exist. Magic.

Short-Lived Certificates: The Killer Feature

Here’s where SSH CAs become dangerous to your previous life. Instead of signing keys for a year, sign them for 8 hours:

Terminal window
ssh-keygen -s ca_key \
-I "alice-daily-$(date +%s)" \
-n "alice,developers" \
-V "+8h" \
alice_key.pub

Every morning, you (or a cron job) re-sign the key. Alice’s certificate expires in 8 hours. If she leaves and you stop signing, her access vanishes automatically. No revocation lists, no cleanup, no guessing whether Bob still has access.

You can also mix principals: sign Bob’s key for only the web principal, so he can only SSH as the web user, not root or admin.

Reading a Certificate

Want to see what’s inside a certificate? SSH has you covered:

Terminal window
ssh-keygen -L -f alice_key-cert.pub

Output:

Type: user certificate
Public key: ED25519-CERT SHA256:abc123...
Key ID: "alice-user@company"
Valid: from 2026-04-12T10:00:00 to 2027-04-11T10:00:00
Principals:
alice
developers
Critical Options: (none)
Extensions:
permit-pty
permit-port-forwarding
permit-agent-forwarding
permit-X11-forwarding
permit-user-rc
Signature: ...

No mysteries. You know exactly when it expires, who can use it, and what they can do.

Optional: Host Certificates (Bonus)

Tired of The authenticity of host 'server.com' can't be established? Sign your server keys with the same CA:

Terminal window
ssh-keygen -s ca_key \
-h \
-I "my-server.com" \
-n "server.com,192.168.1.10" \
-V "+365d" \
/etc/ssh/ssh_host_ed25519_key.pub

Add a line to your ~/.ssh/known_hosts that pastes in the contents of ca_key.pub (not a file path, known_hosts wants the actual key blob):

@cert-authority server.com,*.my-domain.com ssh-ed25519 AAAAC3Nza...your-ca-public-key... my-infrastructure-ca

Now you’ll never see that scary authenticity prompt again. Your client trusts the CA, so it trusts the server.

Why This Actually Matters

You’ve just replaced scattered, unversioned authorized_keys files with auditable, time-bound, principal-restricted certificates. You know:

No more “I think Bob left?” SSH dives at 2 AM. No more manual key rotations across 20 servers. No more authorization sprawl.

Start small: create a CA, sign certs for your dev team, and watch the chaos disappear.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
RAG on a Budget: Building a Knowledge Base with Ollama & ChromaDB
Next Post
Docker BuildKit: Stop Waiting for Your Images to Build

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts