SSH Host & User Certificates
Allows one SSH key to sign another SSH key, resulting in an “SSH certificate”
- OpenSSH certificates are an extension build using public keys
- Introduced in OpenSSH 5.4 (released 2010-03-08)
- does not use PEM, X.509 ASN.1 (as in SSL)
- certificate authority (CA) is a special trusted party holding own public-private key-pairs
- alleviates the need to distribute SSH public keys
- an OpenSSH CA is a regular private key
- not used for authentication, only to sign SSH certificates
- signs user keys with capabilities and expiration date
- signs host keys for a domain
- certificates include:
- nonce - unique ID to prevent signature collision attacks
- public key - associated with a private key
- yype - identifies user or host certificates
- key ID - identifies the user or host in loge messages
- (valid) principles - list of user or host names
- validity interval - start time, and expiration date
- critical options - supported client requests
- extensions - optional SSH extensions
- signature Key - CA public key, used to sign certificate (with private key)
- signature - CA issued signature of all preceding fields
- the validity interval is specified like
<start_time>:<end_time>- start time
always(no specified start time) - end time
forever(never expire) - date format is
YYYYMMDD, time formatYYYYMMDDHHMM[SS] - relative time starting with
+or-, suffixw(week)d(day)m(minute)
- start time
+1w1d # valid from node to 1 week and 1 day
-4w:+4w # valid from four weeks ago to four weeks from now
-1d:20201231 # valid from yesterday to midnight December 31st 2020
-1m:forever # valid from one minute ago and never expiringHost Certificates
- …alternative to host public key authentication
- hosts send signed SSH certificates to clients in order to enable the verification of the host’s identity
- host certificate is signed by a trusted certificate authority (CA)
- host certificate includes host name (principle) and expiration date
- clients check if a trusted CA signed the host certificate
- trusted CA public keys are listed in
known_hosts - warning on signature check failure or if a CA is not trusted
- clients don’t store public keys for every host connection
- generate a CA public key-pair to sign host keys with
ssh-keygen:- option
-fdefines the name of the (output) private key file - option
-Cprovides a comment to identify the CA key-pair
- option
ssh-keygen -f devops-host_ca-$(mktemp -u XXXXXX) -C "Host signing key for DevOps"
# the public key gets .pub appended- best practice is to have multiple sets of CA keys with different expiration dates
- allows to revoke a key if required, while maintaining access to the infrastructure
- generally it is useful to follow a naming convention like
<organisation>-<key_identifier>-<unique_id>
# the example above, would create a public and private key-pair like
devops-host_ca-Gb3t8s
devops-host_ca-Gb3t8s.pubSigning
# SSH client connection configuration
cat > ssh_config <<EOF
StrictHostKeyChecking=no
UserKnownHostsFile=/dev/null
EOF
# download the public host key from a node
scp -F ssh_config root@lxdev01:/etc/ssh/ssh_host_rsa_key.pub .Use the CA key-pair to sign the host public key using the ssh-keygen command:
- Option
-hcreates a host certificate instead of a user certificate - Option
-sspecifies a path to a CA private key file - Option
-Vspecifies a validity interval when signing a certificate - Option
-nspecifies one or more principals (host names) - Option
-Ispecifies an identification string used in log output
# sign the host key with the CA signing key
host_ca_private_key=$(ls | egrep -o 'devops-host_ca-[A-Za-z0-9-]{6}' | uniq)
ssh-keygen -h -s $host_ca_private_key \
-V -1d:+52w \
-n lxdev01,lxdev01.devops.test \
-I 'lxdev01.devops.test host certificate' \
ssh_host_rsa_key.pub
# upload the host certificate to the node
scp -F ssh_config ssh_host_rsa_key-cert.pub root@lxdev01:/etc/sshThe example above is just for illustration purpose, and not the recommended way of distributing host certificates
Inspect the host certificate:
» ssh-keygen -L -f ssh_host_rsa_key-cert.pub
ssh_host_rsa_key-cert.pub:
Type: ssh-rsa-cert-v01@openssh.com host certificate
Public key: RSA-CERT SHA256:iVBchuhVcTKvUA4XZb5ldnP2FMgiDKcqaIsWCq9ChIQ
Signing CA: RSA SHA256:zTEUXG8CJ0j9l7s8wt1couYyHD+u8gFjpawbsNmxoFk
Key ID: "lxdev01.devops.test host certificate"
Serial: 0
Valid: from 2020-01-23T11:26:51 to 2021-01-22T11:26:51
Principals:
lxdev01.devops.test
Critical Options: (none)
Extensions: (none)Host Configuration
Enable a host certificate in the sshd server configuration:
ssh -F ssh_config root@lxdev01 <<EOF
echo HostCertificate=/etc/ssh/ssh_host_rsa_key-cert.pub >> /etc/ssh/sshd_config
echo LogLevel=DEBUG3 >> /etc/ssh/sshd_config
sudo systemctl restart sshd
EOF
# follow the log information
ssh -F ssh_config root@lxdev01 -C journalctl -fu sshdCf. sshd manual page:
HostCertificatespecifies a file containing a public host certificate. The certificate’s public key must match a private host key already specified byHostKey.
Client Known Hosts
Clients use a host certificate to verify the node integrity with the CA public key. Therefore add the CA public key to known_hosts file:
echo "@cert-authority * $(cat $host_ca_private_key.pub)" > ssh_known_hostsThe resulting file uses a wildcard to match all hostnames with the CA:
@cert-authority * ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnGv...
Cf. sshd manual page:
Each line in these files contains the following fields: markers (optional), hostnames, bits, exponent, modulus, comment… The fields are separated by spaces… The marker is optional, but if it is present then it must be one of “
@cert-authority”, to indicate that the line contains a certification authority (CA) key… Hostnames is a comma-separated list of patterns (*and?act as wildcards); each pattern in turn is matched against the canonical host name In order to test the configurations enable host key checking, and use the SSH known host file create above:
cat > ssh_config <<EOF
StrictHostKeyChecking=yes
UserKnownHostsFile=ssh_known_hosts
EOF
# connect using the known hosts file
ssh -vvv -F ssh_config root@lxdev01A successful host key verification will print following log information:
debug1: Server host certificate: ... "lxdev01.devops.test host certificate" CA...
debug2: Server host certificate hostname: lxdev01
debug2: Server host certificate hostname: lxdev01.devops.test
debug3: hostkeys_foreach: reading file "ssh_known_hosts"
debug3: record_hostkey: found ca key type RSA in file ssh_known_hosts:1
debug3: load_hostkeys: loaded 1 keys from lxdev01
debug1: Host 'lxdev01' is known and matches the RSA-CERT host certificate.
debug1: Found CA key in ssh_known_hosts:1User Certificates
Generate a CA certificate used as signing keys:
ssh-keygen -f user_ca
# writes user_ca & user_ca.pubMake sure to keep these keys save, since to enable access to all nodes trusting the CA.
Server Configuration
Configure sshd to accept user certificates signed by an CA certificate:
- Copy the public certificate to the servers
/etc/sshdirectory - Configure
TrustedUserCAKeysin/etc/ssh/sshd_config
# copy the public certificate to the servers
scp user_ca.pub root@lxdev01:/etc/ssh
# make sshd trust the public CA certificate, and restart in debugging mode
ssh root@lxdev01 -C '
echo TrustedUserCAKeys /etc/ssh/user_ca.pub >> /etc/ssh/sshd_config
grep ^TrustedUserCAKeys /etc/ssh/sshd_config
systemctl stop sshd
$(which sshd) -d
'Sign User Keys
Generate user certificates to grand access priviliges:
## create a user key (no password for testing)
ssh-keygen -q -t rsa -b 2048 -N '' -f id_rsa
# create id_rsa & id_rsa.pub
## sign the user public key
ssh-keygen -U -s user_ca -V +2w -n devops -I 'SSH key for user devops' id_rsa.pub
# creates id_rsa-cert.pub
## inspect the user certificate
ssh-keygen -L -f id_rsa-cert.pubAvailable options:
-Uenablesssh-agenthost support-s $sign_keyCA user signing key-V $intervalvalidation interval (aka certificate life-time)-I $key_id“key identifier” that is logged by the server when the certificate is used for authentication
Principles are defined with option -n:
- By default, generated certificates are valid for all users or hosts.
- Generate a certificate for a specified set of principals
# allows user and root login
ssk-keygen ... -n root,devops ...Additional limitations on the validity and use of user certificates may be specified through certificate options with -O:
- Disable features of the SSH session
- Limit user to a particular source addresses
- Force the use of a specific command
# Restrict the source addresses from which the certificate is considered valid.
ssh-keygen ... -O source-address=10.1.1.1/24 ... # CIDR format
# forces execution of command instead of any shell or command specified
ssh-keygen ... -O force-command=/usr/bin/journalctl ...Using User-Certificates
Connect with an private key identity, ssh will also try to load certificate information from the filename obtained by appending -cert.pub to identity filenames:
# for debugging
cat > ssh_config <<EOF
PasswordAuthentication=no
StrictHostKeyChecking=no
UserKnownHostsFile=/dev/null
EOF
# will implicitly load id_rsa-cert.pub if present
ssh -F ssh_config -v -i id_rsa devops@lxdev01Debugging will show if the certificate is loaded:
Will attempt key: id_rsa RSA SHA256... explicit
Will attempt key: id_rsa RSA-CERT SHA256... explicit
And if the server accepted the key:
Offering public key: id_rsa RSA-CERT SHA256.... explicit
Server accepts key: id_rsa RSA-CERT SHA256... explicit
Authentication succeeded (publickey).
In case of a configuration problem following error message is emitted by sshd:
key_cert_check_authority: invalid
Certificate invalid: name is not a listed principal
Otherwise a successful login emits:
Accepted certificate ID "..." (serial 0) signed by RSA CA SHA256... via /etc/ssh/user_ca.pub
do_pam_account: called Accepted publickey for ... ssh2: RSA-CERT ID SSH key for user ... CA RSA SHA256...