How to Configure OpenSSH Certificate Authentication with an SSH CA

Tested on RHEL 10.2 as SSH server (192.168.56.116); Ubuntu 26.04 as CA/admin workstation AND SSH client (192.168.56.121)
Package openssh-server-9.9p1-25.el10_2.x86_64 / OpenSSH_10.2p1 Ubuntu
Applies to Any Linux distribution with modern OpenSSH (TrustedUserCAKeys, ssh-keygen -s)
Privilege sudo or root on the SSH server for sshd_config and reload; normal user for CA signing, client keys, and login tests
Scope OpenSSH-native user certificate authentication with an SSH CA (TrustedUserCAKeys, principals, expiry, KRL, AuthorizedPrincipalsFile). Brief host certificates section. Does not cover Vault, Smallstep, Teleport, or X.509/TLS certificates.
Related guides Fail2ban for SSH
Restrict SSH users and groups
SSH host-based authentication
Test SSH connection
Short on time?Beta Get the gist of this 22-minute read in seconds, then ask follow-up questions

In this article we will configure OpenSSH certificate authentication with an SSH CA so servers trust one CA public key instead of copying every user public key into authorized_keys. You still keep a normal SSH key pair; the CA signs the public key and the client presents the resulting user certificate at login.

I ran the full path with Ubuntu 26.04 holding both the CA private key and the SSH client, and RHEL 10.2 as the SSH server: create a user CA, install only the CA public key, set TrustedUserCAKeys, sign a key with principals and a short validity window, log in, expire and restrict the certificate, revoke it with a KRL, then map a role principal with AuthorizedPrincipalsFile. Host certificates appear later as a short follow-on.


What is OpenSSH certificate authentication?

Traditional SSH key authentication usually means copying each user's public key onto every host that should accept them:

User key
   |
   +---> Server 1 ~/.ssh/authorized_keys
   +---> Server 2 ~/.ssh/authorized_keys
   +---> Server 3 ~/.ssh/authorized_keys

That works for a handful of boxes. It hurts when you have many users, many servers, or frequent join and leave events.

With OpenSSH certificate authentication, servers trust a certificate authority instead of every individual user key:

           SSH CA
             |
          signs key
             |
             v
     User SSH certificate
             |
   +---------+---------+
   v         v         v
Server 1  Server 2  Server 3
   |         |         |
   +-- trust the same CA --+

In practice:

  • The user still has an SSH key pair (private key stays on the client).
  • The CA signs the user's public key and produces an OpenSSH user certificate.
  • Each SSH server trusts the CA public key through TrustedUserCAKeys.
  • The certificate can name which accounts it may use (principals) and when it expires.
IMPORTANT
OpenSSH certificates are not X.509 or TLS certificates. They use OpenSSH's own certificate format and are created with ssh-keygen. Do not expect openssl x509 or a browser trust store to validate them.

SSH certificates vs normal SSH keys

A certificate does not replace the key. It is signed metadata wrapped around a public key.

Feature Normal SSH key SSH certificate
Server stores or trusts Individual public key in authorized_keys CA public key (TrustedUserCAKeys)
User onboarding Copy the public key to each server CA signs the user's public key
Expiration Not built into a normal public key Built into the certificate
Allowed usernames Usually controlled by which account's authorized_keys holds the key Certificate principals (and optional AuthorizedPrincipalsFile)
Central revocation Remove the key from every host Short-lived certs and/or a KRL
Native OpenSSH support Yes Yes

If you already know how public-key login works, think of the certificate as a time-bound, CA-signed permission slip for that same key.


User certificates vs host certificates

These solve different problems. Many tutorials mix them; keep them separate.

User certificate

Used when a user authenticates to an SSH server. The server trusts a user CA through TrustedUserCAKeys.

Host certificate

Used when a client verifies the identity of an SSH server. Clients trust a host CA, usually with a @cert-authority line in known_hosts.

This article focuses first on user certificate authentication, because that is what replaces large-scale authorized_keys management. Host certificates come later as a shorter section.


Lab topology and roles

In this lab, one Ubuntu host does two jobs: it holds the CA private key and it runs the SSH client. The RHEL host is only the SSH server. That split matters when you copy files, because the CA private key must never land on the SSH server.

Ubuntu 26.04 (192.168.56.121)
  ├─ CA / admin workstation
  └─ SSH client
RHEL 10.2 (192.168.56.116)
  └─ SSH server
Host Role What lives there
Ubuntu 192.168.56.121 CA and SSH client User CA private key, user SSH key, signed *-cert.pub
RHEL 192.168.56.116 SSH server CA public key only, sshd drop-ins, local Linux accounts
WARNING
Keep the CA private key off SSH servers. Servers only need the CA public key in TrustedUserCAKeys. Anyone who steals the CA private key can mint certificates your fleet will accept.

Check your OpenSSH version

OpenSSH certificate authentication has been available for many years. Confirm versions on both sides so you know which client and server you are debugging.

On the RHEL SSH server, print the daemon version:

bash
sshd -V
output
OpenSSH_9.9p1, OpenSSL 3.5.5 27 Jan 2026

Confirm the installed packages as well:

bash
rpm -q openssh openssh-server
output
openssh-9.9p1-25.el10_2.x86_64
openssh-server-9.9p1-25.el10_2.x86_64

On the Ubuntu client and CA workstation, check the client version:

bash
ssh -V
output
OpenSSH_10.2p1 Ubuntu-2ubuntu3.6

Modern packages on RHEL, Rocky Linux, AlmaLinux, Fedora, Ubuntu, and Debian all support this feature. Older hosts may still work; check sshd -V instead of guessing from the distro name alone.


Create an SSH certificate authority

Generate a dedicated CA key pair on the Ubuntu CA workstation. Prefer a strong passphrase in production so a stolen CA file is not immediately usable.

Create an Ed25519 user CA and enter a passphrase when ssh-keygen prompts:

bash
ssh-keygen -t ed25519 -f ~/ssh-user-ca -C lab-user-ca
output
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/deepak/ssh-user-ca
Your public key has been saved in /home/deepak/ssh-user-ca.pub
The key fingerprint is:
SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk lab-user-ca
IMPORTANT
For disposable lab screenshots only, you can use an empty passphrase (-N '') so non-interactive re-signing is easy. Do not treat an empty CA passphrase as a production practice.

You get two files:

File Role
ssh-user-ca Private CA key (signs certificates; protect it)
ssh-user-ca.pub Public CA key (distribute to SSH servers)

Show the public key you will install on the server:

bash
cat ~/ssh-user-ca.pub
output
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMaOnLsLUQtvuKYScyVANFIz1aEx2l1srmj1OYS4boMU lab-user-ca

Do not copy the private CA key onto ordinary SSH servers. Only the .pub file belongs there.


Transfer the CA public key to the SSH server

Copy only the public CA key from Ubuntu to the RHEL SSH server. Use the scp command so the private key never leaves the CA workstation.

On Ubuntu, upload the public key to a temporary path on the server:

bash
scp ~/ssh-user-ca.pub deepak@192.168.56.116:/tmp/user_ca.pub

scp prints a progress line and exits when the transfer finishes. On the RHEL server, install the file with mode 0644 under /etc/ssh/:

bash
sudo install -m 0644 /tmp/user_ca.pub /etc/ssh/user_ca.pub

install prints nothing when it succeeds. Confirm the installed file matches the CA public key from Ubuntu before you point sshd at it.


Configure TrustedUserCAKeys

Prefer a drop-in under /etc/ssh/sshd_config.d/ so the main config stays clean. Put only TrustedUserCAKeys in this file; later steps add separate drop-ins for revocation and principals.

On the RHEL server, create the CA trust drop-in:

bash
sudo tee /etc/ssh/sshd_config.d/50-user-ca.conf >/dev/null <<'EOF'
TrustedUserCAKeys /etc/ssh/user_ca.pub
EOF

TrustedUserCAKeys is the file that lists CA public keys allowed to sign user certificates that sshd will accept. This setting does not disable authorized_keys. Existing public-key entries keep working unless you change other authentication settings.

Validate the config before reloading:

bash
sudo sshd -t

A clean sshd -t prints nothing. Reload the daemon on RHEL-family hosts with systemctl:

bash
sudo systemctl reload sshd

On Ubuntu and Debian servers the unit is often named ssh, so use systemctl reload ssh there. Confirm the effective setting:

bash
sudo sshd -T | grep trustedusercakeys
output
trustedusercakeys /etc/ssh/user_ca.pub

Lowercase names in sshd -T are normal. The path should match the public CA file you installed.


Create or reuse a user SSH key

If the account already has a key, reuse it. Certificates expire; keys do not need to be regenerated every time you re-sign. For flag details beyond this guide, see generate SSH key.

On the Ubuntu client, create an Ed25519 key when needed and enter a passphrase when prompted:

bash
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519

ssh-keygen writes the key pair under ~/.ssh/ and prints a fingerprint summary when it finishes. The private key stays on the client. An empty passphrase is fine for some automation keys, but it is not a requirement for certificates. In production, send only ~/.ssh/id_ed25519.pub to the CA operator for signing. Do not generate a new key every time a certificate expires; re-sign the same public key with a new validity window.


Sign the user public key

Before you sign: in this lab, Ubuntu is both the CA workstation and the SSH client, so the CA private key and the user key sit on the same host. In production, the user sends only their .pub file to the CA operator and gets *-cert.pub back. The CA private key never travels with the user.

Sign for account deepak with an eight-hour window and a readable key ID:

bash
ssh-keygen -s ~/ssh-user-ca -I deepak-2026-lab -n deepak -V +8h ~/.ssh/id_ed25519.pub
output
Signed user key /home/deepak/.ssh/id_ed25519-cert.pub: id "deepak-2026-lab" serial 0 for deepak valid from 2026-09-22T09:50:00 to 2026-09-22T17:51:29

Important options:

Option Meaning
-s Path to the CA private key
-I Certificate identity (shows up in logs; use a clear label)
-n Principals (accounts or roles the cert may assert)
-V Validity interval
Final argument Path to the user's .pub file

The signed certificate is written beside the key as ~/.ssh/id_ed25519-cert.pub. OpenSSH automatically looks for id_ed25519-cert.pub next to id_ed25519 when you connect with that identity.


Understand SSH certificate principals

Principals control which account names the certificate may authenticate as, subject to server policy.

A single principal matching the Linux username:

text
-n deepak

Multiple principals:

text
-n deepak,admin

When TrustedUserCAKeys is set and you have not configured AuthorizedPrincipalsFile, the login username must normally appear in the certificate's principal list. If you try to log in as deepak with a certificate that only lists developers, authentication fails until you either re-sign or add AuthorizedPrincipalsFile mapping (covered later).

IMPORTANT
A certificate does not create Linux users. The local account must already exist on the SSH server. Principals only authorize which certificate identities may be used for an existing account.

Principals are not Linux groups by themselves. They are strings the CA puts in the certificate; the server decides how those strings map to local accounts.


Inspect the certificate

Always inspect what you signed before you rely on it in production.

bash
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
output
/home/deepak/.ssh/id_ed25519-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
        Signing CA: ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk (using ssh-ed25519)
        Key ID: "deepak-2026-lab"
        Serial: 0
        Valid: from 2026-09-22T09:50:00 to 2026-09-22T17:51:29
        Principals: 
                deepak
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc

Read each field:

  • Type — user certificate (not a host certificate).
  • Public key — fingerprint of the certified key material.
  • Signing CA — must match the CA you configured in TrustedUserCAKeys.
  • Key ID — the -I string; useful in sshd logs.
  • Serial — numeric serial (default 0 unless you set -z). Use a non-zero -z value if you plan to revoke by serial or serial range later. Use a non-zero -z value if you plan to revoke by serial or serial range later.
  • Valid — hard start and end times.
  • Principals — names the cert may assert.
  • Critical Options — restrictions such as force-command or source-address.
  • Extensions — optional capabilities such as PTY and forwarding.

If principals or validity look wrong, re-sign before you chase sshd configuration.


Log in with the certificate

From the Ubuntu client, connect as the principal you signed. The ssh command pairs the private key with the matching -cert.pub automatically.

bash
ssh deepak@192.168.56.116

A successful login drops you into a shell on the RHEL server. If you need to force the identity file, pass -i or set IdentityFile in ssh_config:

bash
ssh -i ~/.ssh/id_ed25519 deepak@192.168.56.116

For proof that the certificate (not a bare key from authorized_keys) was used, raise verbosity and keep only the useful lines:

bash
ssh -vv deepak@192.168.56.116
output
debug1: Offering public key: /home/deepak/.ssh/id_ed25519 ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
debug1: Server accepts key: /home/deepak/.ssh/id_ed25519 ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
Authenticated to 192.168.56.116 ([192.168.56.116]:22) using "publickey".

ED25519-CERT in the offering and accept lines is the signal you want. Plain ED25519 without -CERT would mean a normal key path.


Verify acceptance in sshd logs

After a successful client login, read the recent sshd journal on the RHEL server. On Ubuntu or Debian servers, swap the unit name to ssh. Unit filtering and time windows are covered in the journalctl reference.

bash
sudo journalctl -u sshd -n 20 --no-pager
output
Sep 22 09:51:31 vm1.lab.example sshd-session[34894]: Accepted publickey for deepak from 192.168.56.121 port 58130 ssh2: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU ID deepak-2026-lab (serial 0) CA ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk

The server log confirms that sshd accepted the certificate and shows which CA signed it. The Key ID from -I and the serial appear on the same line.

Certificate auth rides on the public-key path. It does not require a separate AuthenticationMethods value by itself, and other OpenSSH authentication methods can still be enabled in sshd_config.


Set certificate expiration

Expiration is one of the strongest reasons to prefer certificates over permanent authorized_keys entries. A short-lived certificate reduces the window in which a stolen key plus its currently issued certificate can be used.

Sign with a short window. A small negative start offset helps when client and server clocks differ slightly:

bash
ssh-keygen -s ~/ssh-user-ca -I deepak-short -n deepak -V -5m:+1m ~/.ssh/id_ed25519.pub

Inspect the new validity range:

bash
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub

Confirm the Valid: line shows the intended start and end. After the end time, the server rejects the certificate even if the CA is still trusted and the private key is intact:

bash
ssh deepak@192.168.56.116
output
deepak@192.168.56.116: Permission denied (publickey,password).

Issue a fresh certificate against the same user key when access should continue. For many environments, short-lived certificates reduce how often you need explicit revocation.


Restrict certificate capabilities

By default, a freshly signed user certificate includes several extensions: PTY allocation, agent forwarding, port forwarding, X11 forwarding, and permit-user-rc. Those defaults are convenient for interactive admin use. They are too broad for automation accounts or break-glass keys.

-O clear removes those default extensions first. After clear, you add only the options you want, such as force-command or selective permit-* flags. The user cannot strip a critical option from a signed certificate.

Clear defaults and force a fixed command string:

bash
ssh-keygen -s ~/ssh-user-ca -I deepak-forced -n deepak -V +8h -O clear -O force-command='echo FORCED_ONLY; /usr/bin/id' ~/.ssh/id_ed25519.pub

Inspect the certificate and confirm Critical Options list force-command while Extensions is empty:

bash
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
output
        Critical Options: 
                force-command echo FORCED_ONLY; /usr/bin/id
        Extensions: (none)

Connect. Instead of an interactive shell, the forced command runs and exits:

bash
ssh deepak@192.168.56.116
output
FORCED_ONLY
uid=1012(deepak) gid=1012(deepak) groups=1012(deepak) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Useful related options include source-address and selective permit flags. Start with one controlled restriction like force-command before you layer many options.


Revoke a certificate with a KRL

When you must cut access before a certificate expires, build a Key Revocation List (KRL) and point sshd at it with RevokedKeys. This section covers the shortest create-and-enforce path. For serial ranges, Key IDs, fingerprints, plain-key revoke, and fleet distribution, use the dedicated OpenSSH KRL revocation guide.

Copy the certificate you want to revoke onto the RHEL server (for example under /tmp/), or build the KRL on the CA host and copy the KRL file. On the server, create a new KRL from the CA public key and the certificate. Do not use -u on the first create:

bash
sudo ssh-keygen -k -f /etc/ssh/revoked_keys -s /etc/ssh/user_ca.pub /tmp/id_ed25519-cert.pub

ssh-keygen -k writes the KRL file and prints nothing on success when the path is new. Later, when you need to add another certificate to an existing KRL, update it with -u:

bash
sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys -s /etc/ssh/user_ca.pub /tmp/another-cert.pub

Point sshd at the KRL in a separate drop-in. Do not overwrite 50-user-ca.conf:

bash
sudo tee /etc/ssh/sshd_config.d/70-user-cert-revocation.conf >/dev/null <<'EOF'
RevokedKeys /etc/ssh/revoked_keys
EOF

Validate the drop-in before you reload:

bash
sudo sshd -t

A clean sshd -t prints nothing. Reload so RevokedKeys is active:

bash
sudo systemctl reload sshd

Query the KRL before you test login (run this where the certificate file is available):

bash
ssh-keygen -Q -f /etc/ssh/revoked_keys /tmp/id_ed25519-cert.pub
output
/tmp/id_ed25519-cert.pub REVOKED

A login attempt with that certificate should fail:

bash
ssh deepak@192.168.56.116
output
Permission denied (publickey,password).

Confirm the server rejected the credential because of the KRL:

bash
sudo journalctl -u sshd -n 20 --no-pager
output
error: Authentication key ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU revoked by file /etc/ssh/revoked_keys

Leave revoked certificates in the KRL. Issue a new certificate with a new serial when access should return, rather than trying to resurrect the old credential.


Configure AuthorizedPrincipalsFile

Until now the basic path used -n deepak so the certificate principal matched the login username. AuthorizedPrincipalsFile moves that mapping onto the server so a role name such as developers can authorize one or more local accounts.

Add a separate drop-in. Do not fold this into the CA or revocation files:

bash
sudo tee /etc/ssh/sshd_config.d/60-user-principals.conf >/dev/null <<'EOF'
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
EOF

Create the principals directory. %u expands to the target username, so the file for this account is /etc/ssh/auth_principals/deepak:

bash
sudo mkdir -p /etc/ssh/auth_principals

mkdir -p prints nothing when the directory already exists or was created. Write the allowed principal string into the account file:

bash
echo developers | sudo tee /etc/ssh/auth_principals/deepak
output
developers

Validate the updated drop-in before you reload:

bash
sudo sshd -t

A clean sshd -t prints nothing. Reload so the principals file takes effect:

bash
sudo systemctl reload sshd

OpenSSH consults AuthorizedPrincipalsFile only for certificates authenticated via a CA listed in TrustedUserCAKeys.

If you still hold a certificate whose only principal is deepak, login fails once the principals file requires developers. Re-sign the user key with the role principal the file allows:

bash
ssh-keygen -s ~/ssh-user-ca -I deepak-dev-role -n developers -V +8h ~/.ssh/id_ed25519.pub

After that, ssh deepak@192.168.56.116 succeeds again because the certificate carries developers and /etc/ssh/auth_principals/deepak lists that string. The journal shows the new Key ID:

bash
sudo journalctl -u sshd -n 20 --no-pager
output
Accepted publickey for deepak from 192.168.56.121 port 48684 ssh2: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU ID deepak-dev-role (serial 0) CA ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk

Without AuthorizedPrincipalsFile

The certificate principal normally matches the login username (-n deepak to log in as deepak).

With AuthorizedPrincipalsFile

The server maps certificate principals to local accounts. The same principal, such as developers, can be authorized for multiple local accounts by listing it in each account's principals file.


SSH CA vs authorized_keys

Topic authorized_keys SSH CA
Trust model Individual public keys CA public key
Adding a user Update servers Issue a certificate
Removing access Remove the key everywhere Let a short-lived cert expire, or revoke
Expiration Manual or external process Native in the certificate
Principals Limited Native
Large fleets Harder to keep consistent Easier once the CA is trusted
One user, one server Usually simpler Often unnecessary overhead

SSH certificates pay off when you manage multiple users or multiple servers. For a single admin key on a single host, plain authorized_keys is usually enough. Remember that enabling TrustedUserCAKeys does not turn off authorized_keys by itself.


SSH certificates vs X.509

Keep this distinction clear when searching or talking to colleagues:

  • OpenSSH certificates are not TLS or SSL certificates.
  • There is no X.509 chain, CSR, or web PKI involved.
  • You create them with ssh-keygen -s, not with openssl req.
  • Only OpenSSH (and tools that speak its certificate format) understand them.

If a product asks for an X.509 client certificate for HTTPS or LDAP, that is a different system. This guide stays on the OpenSSH-native path.


Host certificates

Host certificates solve a different problem: clients trust a host CA instead of learning every server host key through TOFU and known_hosts sprawl. Keep this short and sign off the SSH server.

Generate a separate host CA on the Ubuntu CA workstation (do not reuse the user CA). Enter a passphrase when prompted, the same way you protected the user CA:

bash
ssh-keygen -t ed25519 -f ~/ssh-host-ca -C lab-host-ca

Show the host CA public key you will trust on clients:

bash
cat ~/ssh-host-ca.pub
output
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGd6rD+vUVC3vp4BBRy3lKsC1k1OMyI3rzgFlV4L3ybT lab-host-ca

Copy the server's host public key from RHEL to Ubuntu. Sign on Ubuntu. Never place the host CA private key on the SSH server.

On the RHEL server, stage the host public key where the CA operator can fetch it:

bash
sudo cp /etc/ssh/ssh_host_ed25519_key.pub /tmp/

From Ubuntu, pull that public key onto the CA workstation:

bash
scp deepak@192.168.56.116:/tmp/ssh_host_ed25519_key.pub ~/

Sign it as a host certificate with hostname and address principals:

bash
ssh-keygen -s ~/ssh-host-ca -I vm1-lab -h -n vm1.lab.example,192.168.56.116 -V +52w ~/ssh_host_ed25519_key.pub

Copy only the resulting certificate back to the server:

bash
scp ~/ssh_host_ed25519_key-cert.pub deepak@192.168.56.116:/tmp/

On RHEL, install the host certificate and point sshd at it:

bash
sudo install -m 0644 /tmp/ssh_host_ed25519_key-cert.pub /etc/ssh/ssh_host_ed25519_key-cert.pub

Create a host-certificate drop-in:

bash
sudo tee /etc/ssh/sshd_config.d/50-host-cert.conf >/dev/null <<'EOF'
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
EOF

Validate the host-certificate drop-in:

bash
sudo sshd -t

A clean sshd -t prints nothing. Reload so sshd presents the host certificate:

bash
sudo systemctl reload sshd

On the Ubuntu client, trust the host CA in ~/.ssh/known_hosts. Scope the @cert-authority line to the lab hosts, not a bare *:

text
@cert-authority vm1.lab.example,192.168.56.116 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGd6rD+vUVC3vp4BBRy3lKsC1k1OMyI3rzgFlV4L3ybT lab-host-ca

A *.lab.example pattern is also fine when every host in that DNS zone presents certificates from this host CA. Connect and confirm the client accepts the host certificate:

bash
ssh -o StrictHostKeyChecking=yes deepak@192.168.56.116 'echo HOST_CERT_OK'
output
HOST_CERT_OK

Inspect a host certificate the same way as a user certificate (ssh-keygen -L); the type line says host certificate, and principals list vm1.lab.example and 192.168.56.116.


User CA vs host CA

Keep signing keys separate.

User CA
   |
   +-- signs user certificates (TrustedUserCAKeys)

Host CA
   |
   +-- signs host certificates (HostCertificate / @cert-authority)

Keep the two CA keys separate. If the user CA is compromised, an attacker should not also gain the ability to create trusted host certificates.


Distro differences

The directives are OpenSSH-standard. Distro differences are mostly packaging and unit names.

Item RHEL / Rocky Linux / AlmaLinux Ubuntu / Debian
Config /etc/ssh/sshd_config and /etc/ssh/sshd_config.d/ Same layout on modern releases
Service unit sshd Often ssh
Logs journalctl -u sshd journalctl -u ssh
Core directives TrustedUserCAKeys, AuthorizedPrincipalsFile, RevokedKeys, HostCertificate Same OpenSSH names

TrustedUserCAKeys, principals, ssh-keygen -s, and ssh-keygen -L are OpenSSH features, not RHEL-only features. Fedora follows the same pattern as other modern OpenSSH packages.


Troubleshooting

Problem Likely cause Fix
Permission denied (publickey) Principal does not match the login user or principals file Run ssh-keygen -L -f *-cert.pub and re-sign with the right -n
Certificate ignored CA not trusted on the server sshd -T | grep trustedusercakeys and confirm the file contents
Certificate expired Validity window ended Re-sign with a new -V window
Certificate not offered Filename does not sit beside the private key Keep id_ed25519-cert.pub next to id_ed25519
Key works, certificate does not TrustedUserCAKeys or principals mismatch Use ssh -vv on the client and journalctl on the server
Certificate does not contain an authorized principal Missing from AuthorizedPrincipalsFile Add the principal string for that account, or re-sign
Revoked cert still works RevokedKeys missing or not reloaded Set the directive in its own drop-in, run sshd -t, reload
Reload fails Syntax error in a drop-in Fix the file using sshd -t output

If failed attempts trigger temporary refusals on newer OpenSSH, check OpenSSH PerSourcePenalties so a noisy test client is not blocking itself while you debug certificates. Pair that with the usual steps to prevent brute-force SSH attacks when you expose SSH on the network.


Security recommendations

  • Protect the CA private key; treat it like a root password for your SSH fleet.
  • Prefer signing on a dedicated admin host, not on every SSH server.
  • Use short certificate lifetimes; add a KRL when you must revoke early.
  • Put meaningful values in -I, and set a non-zero serial with -z when you expect to revoke by serial or serial range later.
  • Keep user CA and host CA keys separate.
  • Use separate sshd drop-ins for CA trust, principals, and revocation so one change does not erase another.
  • Always run sshd -t before reload when you change authentication.
  • Keep a second recovery session open while you change remote sshd authentication settings.
  • Scope @cert-authority lines to real hostnames or address lists, not a bare *.

This is enough to run OpenSSH-native certificates safely at small and medium scale. Full offline CA ceremonies and hardware tokens are a larger operations topic.


Summary

OpenSSH certificate authentication lets each server trust one CA public key through TrustedUserCAKeys instead of collecting every user public key in authorized_keys. You create a CA on an admin workstation, copy only the CA public key to servers, sign user public keys with principals and a validity window, and the client presents the resulting *-cert.pub automatically beside the private key.

Principals decide which identities a certificate may assert. Short lifetimes reduce exposure; a KRL with RevokedKeys handles early revocation. AuthorizedPrincipalsFile moves role mapping onto the server so names like developers can authorize existing local accounts. Host certificates are a separate trust path for server identity and should use a different CA, with signing done off the SSH server.

The main pitfall is treating OpenSSH certificates like X.509, or copying the CA private key onto the servers that only need the public key. Start with one user CA, one TrustedUserCAKeys drop-in, one signed key, and a journal line that shows ED25519-CERT with your Key ID. Expand expiry, restrictions, revocation, and principals once that path is solid.


References

  • sshd_config(5) — TrustedUserCAKeys, AuthorizedPrincipalsFile, RevokedKeys, HostCertificate
  • ssh-keygen(1) — CA creation, -s signing, -L inspect, -k / -Q KRL

Frequently Asked Questions

What is an SSH certificate authority?

An SSH CA is an OpenSSH key pair whose private key signs user or host certificates and whose public key is trusted by servers (TrustedUserCAKeys) or clients (@cert-authority). It is not a separate daemon; it is a carefully protected ssh-keygen key used for signing.

Is an SSH certificate the same as an SSL certificate?

No. OpenSSH certificates use OpenSSH's own format. They are not X.509, are not used by HTTPS browsers, and are created with ssh-keygen, not with a typical TLS CSR workflow.

Does SSH certificate authentication replace authorized_keys?

It can replace per-user public-key distribution at scale. Servers still authenticate public-key material; the trust anchor becomes the CA instead of each user's line in authorized_keys. Enabling TrustedUserCAKeys does not disable authorized_keys by itself. Small single-host setups often keep plain authorized_keys.

How long should SSH certificates be valid?

As short as your operations can support. Hours to a day is a common starting point for interactive user access. A short-lived certificate reduces the window in which a stolen key plus its currently issued certificate can be used. Use a KRL when you must revoke before expiry.

What is TrustedUserCAKeys?

It is an sshd setting that points at a file of CA public keys. Certificates signed by those CAs may be accepted for user authentication, subject to principals, validity, revocation, and other sshd policy.

What are SSH certificate principals?

Principals are names embedded in the certificate with ssh-keygen -n. They declare which identities the certificate may assert. Without AuthorizedPrincipalsFile, they usually must match the login username; with that file, the server maps principals to local accounts. A certificate does not create Linux users; the account must already exist.

Can one certificate authenticate to multiple Linux usernames?

Yes, if you list multiple principals and the server accepts them, or if several accounts' AuthorizedPrincipalsFile entries share a common principal string such as developers. The same principal can be authorized for multiple local accounts by listing it in each account's principals file.

How do I revoke an SSH certificate?

Prefer letting short-lived certificates expire. For immediate revocation, create a KRL with ssh-keygen -k (add -u only when updating an existing KRL), configure RevokedKeys in its own drop-in, reload sshd, and confirm with ssh-keygen -Q.

Can I use the same SSH CA for users and hosts?

You can technically sign both types with one key, but you should not. Keep the two CA keys separate. If the user CA is compromised, an attacker should not also gain the ability to create trusted host certificates.

Do RHEL, Ubuntu and Debian support OpenSSH certificates?

Yes, on modern OpenSSH packages. The feature is part of OpenSSH itself. Check sshd -V and sshd -T on the host you manage; service unit names may differ (sshd versus ssh).

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)