Server access with SSH certificates - deep dive
Tags: security, ssh, devops, tutorial
Published: 2024-03-30 17:07:21

There are a growing number of articles around which explain about the setup and use of SSH certificates, like [the Wikibooks Certificate-based Authentication cookbook](https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Certificate-based_Authentication). 

This article goes into detail where the others stop: Most articles describe how the principal in the certificate (`-n` argument to `ssh-keygen` when signing the user's key) needs to be set to the name of the user on the target host. There are however many more options that can be used on the server as well as in the certificate to manage access.

## Advanced principals

By default, OpenSSH requires the name of the user logging in to be among the principals in the certificate. That is, if certificate based authentication is used, 'bob' needs to be in the list of principals to be able to:

```bash
$ ssh bob@server01.example.com
```

Next to this default, servers have 3 mechanisms for setting up more advanced authentication setups using principals.

### AuthorizedPrincipalsFile

Using this directive, a global or per-user list of principals can be configured for access to the server. That is to say that if the server is configured with an `authorized_principals` file:

```plain
# /etc/ssh/sshd_config

# for a global configuration:
AuthorizedPrincipalsFile /etc/ssh/authorized_principals

# for per-user configuration, relative to $HOME:
AuthorizedPrincipalsFile .ssh/authorized_principals
```

which contains

```plain
access-any-user
```

Then, if bob has a certificate named `any_bob` with the `access-any-user` principal, he can authenticate to the server as _any_ user:

```bash
bob@client$ ssh -o CertificateFile=any_bob-cert.pub -i any_bob sshd@server01.example.com
This account is currently not available.
```

The response `This account is currently not available.` indicates the authentication was successful and the `nologin` shell was executed.

**Remark**: Using the AuthorizedPrincipalsFile directive disables the default behaviour to accept certificates listing the target username among the principals.

#### Per-principal command restriction

The authorized principals file supports the same options as the authorized keys file does. Assuming `server01` has a user named `wacky` with a regular login shell, if we change `/etc/ssh/authorized_principals` to contain:

```plain
command="/usr/bin/date" access-any-user
```

Then:

```bash
bob@client$ ssh -o CertificateFile=any_bob-cert.pub -i any_bob wacky@server01.example.com
Sat Mar 30 15:29:44 CET 2024
```

The source from which is being connected can be restricted using this method using the `from=` option. See for more options the documentation of the `authorized_keys` file.

## Certificate based restrictions

Certificates can come with their own restrictions, independent of the restrictions specified by the server. The ones to highlight here are `force-command` and `source-address`. In case a `force-command` restriction in the certificate conflicts with the `command` restriction in the authorized principals file (i.e. they are not the same string), the mechanism is considered a mismatch (and the next mechanism - e.g. password based - is tried).

## Certificate identity (`-I`) and sequence numbers (`-z`)

Although most sources hostnames for host certificates and `<user>@<host>` for user certificates, these can be any string value. They serve two purposes:

1. To identify the certificate that was presented during authentication, in the system logs (syslog or journalctl); and
2. To identify the certificate in the Key Revocation List (KRL) 

Many articles don't specify a sequence number, but specifying one is definitely desirable to be able to revoke specific certificates.

## Dynamic authorization of principals

The configuration option `AuthorizedPrincipalsCommand` is a global setting which may be used to configure dynamic authorized principal list generation using an external command:

```plain
AuthorizedPrincipalsCommand /usr/local/bin/dynamic-principals "%u %s"
AuthorizedPrincipalsCommandUser dedicateduser
```

The `sshd_config` man page lists the supported tokens. Each returned line on standard output must conform to the same requirements as the lines from a static authorized principals file.

## Prerequisites

To authenticate servers to clients:
- A host certificate authority (CA) key pair
- A host CA's **public key** _deployed to clients_; configured using the `@cert-authority` known_hosts-marker either through
  - global configuration (`/etc/ssh/ssh_known_hosts`)
  - user specific configuration (`~/.ssh/authorized_keys`)

    _In case of a user-specified CA in authorized_keys, a principals list needs to be specified with the `principals=` option._

To authenticate clients to servers:
- A client CA key pair
- A client CA's **public key** _deployed to servers_; configured through `/etc/ssh/sshd_config` using the `TrustedUserCAKeys` configuration directive

**Note**: neither the client CA nor the host CA require a certificate of their own.

## Conclusion

Although by default the target user name must be among the principals in the certificate, this is not a hard requirement: principals can be listed statically or dynamically on the server resulting in a successful authentication when a non-empty intersection between the server and certificate principals exists. This allows for a setup where principals define roles rather than login accounts.

Using the concepts presented in this article (especially the - certificate-based - command restriction) in combination with command restrictions in e.g. `sudo` opens opportunities for fine-grained control over user and admin access.