An (Open)SSH Certificate Authority is sort of unlimited and sort of not

September 4, 2023

One of the things that people like to recommend for SSH authentication is that instead of individual host and user keys that people have to trust and manage one by one, you set up a SSH certificate authority and trust it, with it signing user keys, host keys, or both. However, just like OpenSSH signed certificates aren't X.509 certificates, an OpenSSH 'certificate authority' is not like a traditional CA. This especially matters when it comes to the interesting question of whether or not you can put name constraints on your OpenSSH cert-authority the way you can put name constraints on TLS CA root certificates.

One critical difference between OpenSSH cert-authorities and TLS CAs is what you trust. In TLS, you trust a specific self-signed X.509 certificate, and the certificate is identified by its X.509 Subject Name plus (in effect) its public key. As covered in sshd(8)'s section on the authorized keys file format and the known-hosts format, OpenSSH cert-authorities are identified by their public key, and this is all of the cert-authority provided information that you have to use. So unlike TLS CAs, an OpenSSH cert-authority can't generate a 'CA' that can intrinsically only be used to sign things under a certain domain.

As covered in ssh-keygen(1)'s section on certificates, the OpenSSH certificates generated with an OpenSSH cert-authority may have constraints. If you're setting up an organizational cert-authority, you'll presumably never sign a host or user certificate without restricting what host (or user) it can be used for or by. As far as I can see, each type of certificate can only be restricted for that type of thing; a user certificate can have a list of target users it's valid for (probably normally only one name), and a host certificate can have a list of hosts (again, probably one name). This comes both from ssh-keygen's usage and PROTOCOL.certkeys, which documents the certificate format.

(OpenSSH certificates are either host certificates or user certificates; you can't have one certificate that's both.)

For host certificates, the sshd known-hosts format allows the machine accepting these keys to limit what domains they'll be accepted for:

@cert-authority *.dev.example.com ssh-ed25519 ...

For accepting user certificates in your authorized keys file, you can require that certain 'principals' (user names) be present in the certificate, via the principals= option. I believe this must be literal text, so you would have to write your expected user name there:

cert-authority,principals="cks" ssh-rsa ....

If you instead set TrustedUserCAKeys in your system wide sshd_config(5), by default sshd will only accept user certificates with the target user as a principal (as covered in AuthorizedPrincipalsFile). Because TrustedUserCAKeys is one of the options that may appear in a Match block, you can make it so that your server only accepts a particular OpenSSH cert-authority's keys for certain users (or users in certain groups).

By playing games with token substitution in an AuthorizedPrincipalsFile, you could probably make it so that OpenSSH certificates for users on particularly sensitive hosts had to have specially formatted user names in order to be accepted. For example, you could require not just the username, but a 'prod:<user>' username (and if you signed the user certificate with only that principal, it couldn't be used on other machines that didn't know about this special username format). Or you could simply have a different OpenSSH cert-authority for access to these special machines.

(At one level an OpenSSH cert-authority is just a keypair. At another level it's a carefully operated and guarded collection of security procedures, possibly with, for example, hardware keys. It's easy to have one more on-disk keypair, it's not so straightforward to have one more of all the rest of it.)

As far as I can see, this allows for relatively good control over what certificates any particular machine will accept in practice, at the cost of requiring specific configuration on each machine. In other words, the machines validating the OpenSSH certificates control the restrictions, not the OpenSSH cert-authority, and the cert-authority is at the mercy of people setting up those machines. If you set up a 'production' machine with your general 'dev free for all' cert-authority accepted, well, that's what you get.

There's also the unfortunate possibility to make dangerous mistakes with accepting user certificates, because the system-wide default is different from the per-user default. If you want to enable your organizational certificate on an unrelated machine (or another login on a normal machine), you can't just take the organization's cert-authority key from the TrustedUserCAKeys file and add it to your .ssh/authorized_keys file as 'cert-authority ssh-rsa ...'. Because your personal authorized keys file doesn't restrict the principals allowed, this would allow anyone with a user certificate from your organizational cert-authority to log in as you, which is almost certainly not what you intended. To properly restrict things, you need a 'principals="..."' restriction to your (organizational) login, and so you need to remember to add it.

Written on 04 September 2023.
« TLS CA root certificate name constraints for internal CAs
Having ClamAV reject email using the Malwarepatrol database seems unwise »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Sep 4 22:51:16 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.