I’ve been using SSH certificates for years to connect to my servers no matter where they are, and they’ve always worked perfectly. I’ve used both client and server certificates, but since I automated the setup I sometimes forget the steps. This guide records the process for future reference.
What is SSH Certificates?
Just as HTTPS certificates verify our websites, SSH certificates confirm the identity of both server and user before any connection begins. When you first connect to a new SSH server, you’ve no doubt seen that warning about an unknown host key and clicked “yes” because you just created the machine and assume it’s safe. SSH certificates remove that guesswork. A trusted authority signs the server’s public key in advance, and your client automatically checks that signature on every connection. On the flip side, the server only accepts user keys that carry a valid signature from the same authority. Certificates can include expiration dates and be revoked if necessary, giving you precise control over who can connect and for how long. With SSH certificates in place, there’s no need to blindly trust that initial prompt.
SSH Certificate Authority
An SSH Certificate Authority (CA) is the single trusted entity that issues and manages SSH certificates for both servers and users. Instead of copying individual public keys to every machine, you rely on your CA to vouch for them all.
Key responsibilities of an SSH CA
-
Key generation
The CA creates a dedicated key pair. The private key remains securely stored. The public key becomes your root of trust. You distribute it to every SSH client and server. -
Certificate issuance
Whenever a user or host key needs certification, the CA signs that public key. The certificate embeds details such as allowed usernames, a validity period and any additional restrictions. -
Trust distribution
Servers declare the CA public key as trusted for user certificates by settingTrustedUserCAKeys
insshd_config
and accept only certificates signed by that CA. Clients add the CA public key toknown_hosts
or a global known hosts file so any host certificate issued by the CA is automatically recognised. -
Expiry and revocation
All certificates carry an expiration date. Once that date passes the certificate is invalid. If a certificate must be revoked before expiry, you maintain a revocation list that servers check to deny compromised certificates.
By using an SSH Certificate Authority you gain central control over who can connect to your servers, reduce administrative overhead and enforce time limited access. Certificates remove the need to distribute or revoke individual public keys on every machine.
For verifying both servers and users you can use a single CA or maintain separate CAs. I prefer to keep them distinct—one CA for host certificates and another for user certificates. This makes trust boundaries clear and simplifies revocation. To generate a new SSH CA key pair, run:
ssh-keygen -t ed25519 -f $HOME/ssh/ca -C "SSH Certificate Authority"
Change the path for the public and private key for the CA as needed, with an easy to recognize comment.
When prompted, pick a strong passphrase and store it in a secure location. You can leave the CA key unencrypted, but protecting it with a passphrase is strongly recommended.
The output is a private key at $HOME/ssh/ca
and public key at $HOME/ssh/ca.pub
SSH CA for Hosts
We require the SSH public key from the host we are interested in, which is stored in the /etc/ssh/ssh_host_{ecdsa,ed25519,rsa}_key
. I typically choose the ed25519
key for my host certificate as that’s my preferred key type. Alternatively, we can obtain the public key via the ssh-keyscan
command
ssh-keyscan -t ed25519 -p 10022 host.example.com
where 10022
is the post SSH is listening on and host.example.com
is the address of the host (this can be an IP address or fully qualified domain name). This approach saves us the trouble of logging into the server to get a copy of the public key.
With a copy of the host public key, we can start to create a certficate for it using the command
ssh-keygen -s $HOME/ssh/ca -I key_id -V +52w -h /path/to/host_key.pub
where
Flag | Description |
---|---|
-s | path to the CA for signing the host public key. |
-I | identifier that will appear in the logs, i usually give it the host name. |
-V +52w | how long is this certificate valid for, I typically rotate my certificates after a year (52 weeks). |
-h | indicate the public key being signed is a host key. |
The output will be named host_key-cert.pub
, where it will take the name and extension provided and append the word -cert
.
With our new cert ready, copy the contents of host_key-cert.pub
and save it to /etc/ssh/server.crt
on the host, that is readable only by the host via the chmod 0400 /etc/ssh/server.crt
. Now create a file /etc/ssh/sshd_config.d/00-cert.conf
and add
HostCertificate /etc/ssh/server.crt
This explicitly tells our SSH server to present this certificate to the client when it connects for verification. Restart the SSH service for this changes to take effect immediately.
On the client side, they need to trust the certificate offered by the host. The way to do this is to add it to the known_hosts
file and adding the line
@cert-authority * ssh-ed25519 <CA public key>
where
Flag | Description |
---|---|
@cert-authority | indicates anything following is information about the certificate authority. |
* | indicates which domain or IP to apply this CA is for, it’s the hostname defined in the ssh_config on the clients machine. |
ssh-ed25519 | key type of the CA public key. |
contents of the CA public key we generated, in this example its at $HOME/ssh/ca.pub |
Ideally, this setup would be done via Ansible or some other approach during server setup and intialization. That way, the client will never be shown the prompt on first connect. Not only that, the known_hosts
file will no longer be filled with old server signatures.
Happy SSH-ing!!