paulgorman.org/technical

SSH Authentication by Certificate Authority

(July 2018)

SSH offers three forms of authentication, in ascending order of desirability: password-based, key-based, and certificate authority (CA).

How is key-based authentication better than password-based authentication?

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.

How does key-based authentication work?

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.

  1. A client connects to the server.
  2. The server tells the client which protocol versions it supports, and identifies itself by sending its public key.
  3. Client and server negotiate a symmetric session key using Diffie-Hellman.
  4. With the session encrypted, authentication begins. The client sends its public key.
  5. The server checks if the key sent by the client is found in the user account’s authorized_keys file.
  6. If the server finds that the key is authorized, it uses the key to encrypt a random number, which it sends to the client.
  7. If the client legitimately has the matching private key, it decrypts the random number, combines it with the session key, and returns a hash of the result to the server.
  8. The server also hashes the combined session key and random number. It compares this with the value returned by the client. If they hashes match, authentication has succeeded.

How is CA-based authentication better than key-based authentication?

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.

How does CA-based authentication work?

Much like key-based authentication, except principals exchange certificates instead of keys.

Set Up the SSH Certificate Authority

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 Host Certificates

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 User Certificates

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.

Examine a Certificate

$  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

Revoking a Certificate

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.

References