(July 2018)
SSH offers three forms of authentication, in ascending order of desirability: password-based, key-based, and certificate authority (CA).
Users don’t always choose passwords strong enough to prevent guessing by attackers. Keys are consistently stronger. Also, using an ssh-agent saves users from having to frequently type passwords.
Key-based auth both authenticates the client to the server, and the server to the client.
With symmetric encryption, single key (a “shared secret”) can encrypt and decrypt data. The SSH client and server agree this key without publicly exposing the secret (using Diffie-Hellman or a similar method). SSH uses symmetric encryption (e.g., AES) to secure exchanged session data, but not for authentication.
For authentication, SSH uses asymmetric encryption.
authorized_keys
file.Key-based auth requires copying every user key to every server. That doesn’t scale well. Using a certificate authority simplifies key management across a multitude of servers and/or for many users. The client also doesn’t get asked to accept server keys.
When we talk about CA certs for SSH, we don’t mean the type of x509 certs used with web certificate authorities. SSH certs are simpler.
We create a CA key pair, and use the CA private key to generate certs for hosts and users by signing the public keys of the hosts and users. Distribute the CA public key on all servers and clients.
Some sources suggest using two CA key pairs: one for hosts and another for users. OK, but why? I guess it splits risk in case one key get compromised. It may also be practical if the admin group signing user certs is different from the admins signing host certs.
When signing user or host certs, ssh-keygen
can limit the resulting certs in a number ways, like restricting them to various principals (users or hosts) or setting an expiration time for the cert.
Each generated user and host cert gets a serial number.
These serial numbers can be added to a Key Revocation List (specified with RevokedKeys
in sshd_config
).
Note: as of 2017, PuTTY couldn’t use OpenSSH certificates.
Much like key-based authentication, except principals exchange certificates instead of keys.
Generate two CA key — one for hosts and one for users:
$ ssh-keygen -t rsa -b 4096 -f ./ca_user_key
$ ssh-keygen -t rsa -b 4096 -f ./ca_host_key
$ chmod 600 ./ca_*_key
$ chmod 644 ./ca_*_key.pub
# chown root:root ./ca_*_key*
To each host that will use the CA to authenticate users, copy the public user key:
$ scp ca_user_key.pub myhost.example.com:/etc/ssh/
…and add this to the host’s /etc/ssh/sshd_config
(and restart sshd to activate the change):
TrustedUserCAKeys /etc/ssh/ca_user_key.pub
(Individual accounts can instead add a cert-authority
directive to their ~/.ssh/authorized_keys
file.)
The user also needs to trust the host key.
Add a line like this with the ca_host_key.pub
to either ~/.ssh/known_hosts
or /etc/ssh/ssh_known_hosts
:
@cert-authority *.example.com ssh-rsa AAAAB3Nm1yc2EAAAADAQABAAACAQDct…
Generate a certificate for each host, like:
$ ssh-keygen -s ca_host_key -h \
-I myhost.example.com \
-n "$(hostname),$(hostname --fqdn),$(hostname -I|tr ' ' ',')" \
-V +56w \
ssh_host_rsa_key.pub ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub
This produces the cert file ssh_host_rsa_key-cert.pub
.
Think carefully about the whether or not to set an expiration on the certificate with -V
.
Specifying principals with -n
is a good practice, but not required.
Copy the cert file to the hosts /etc/ssh/
directory.
Configure the SSH daemon on the host to serve the new certificate by editing /etc/ssh/sshd_config
(and restart sshd):
$ HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
Generate a user certificate like:
$ ssh-keygen -s ca_user_key \
-I paulgorman \
-n paulgorman \
-V +1d \
id_rsa.pub
…and add the resulting id_rsa-cert.pub
file to the user’s ~/.ssh/
directory.
Remember that the user need only supply their public key for signing. Keep the private key private!
As with host certificates, we can set an expiration on users certificates, or even a window in the future (e.g., allow access for two hours next Tuesday). Consider also a workflow with very short-lived certificates, perhaps one day.
$ ssh-keygen -L -f ./id_rsa-cert.pub
./id_rsa-cert.pub:
Type: ssh-rsa-cert-v01@openssh.com user certificate
Public key: RSA-CERT SHA256:3i3d/WMBr3bYSluMXcHi58+5P/H9qc0ySEoFPuG8e0s
Signing CA: RSA SHA256:2vYPWzDg0c2sYA+4niNtAFfCYsENi+XcWbzTD0bafW4
Key ID: "paulgorman"
Serial: 0
Valid: from 2018-07-05T21:31:00 to 2018-07-12T21:32:33
Principals:
paulgorman
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
First, in some cases, we don’t have to be as worried about revoking certs if we create them with short expiration times.
If we do need to quickly revoke a cert, the SSH daemon takes a RevokedKeys
directive in sshd_config
.
The revocation file uses one of two possible formats: either a text file with one key per line, or a binary KRL (Key Revocation List) generated by ssh-keygen
.
Certificates can be revoked by serial number (-z
).
$ ssh-keygen -k -u -f revoked_keys id_rsa-cert.pub
$ ssh-keygen -Q -f revoked_keys id_rsa-cert.pub
id_rsa-cert.pub (paulgorman@myhost.example.com): REVOKED
Of course, the revocation file must be distributed to each host.
ssh-keygen(1)
sshd(8)
ssh(1)