How I use SSH with SSO

I have set up step ca to issue SSH certificates to my clients using SSO with keycloak so I can use sigle sign-on from all my devices to SSH into my servers.

A while ago I watched a video about WPA3 Enterprise authentication with EAP-TLS. It mentioned step ca, a certificate authority that can issue X.509 and SSH certificates. As I find certificates and public key authentication generally interesting, I read a few of their (actually very good) blog articles.
One article described how to authenticate to SSH servers using SSO. I think this concept is really interesting and as I thought about setting up a CA for mTLS client certificate authentication anyway, I tried it out.

Setup

The initial setup of step ca is quite simple. You can follow their getting started guide and production guide. You need to pass the --ssh flag to enable SSH certificates and the --remote-management flag to allow for remote management via the cli.

To issue a certificate, step ca uses "provisioners". These are different methods to authenticate clients. Webservers may use ACME and we will use the OIDC provisioner.
I already had a Keycloak instance running for SSO, so I created a new client for step ca. The default configuration is mostly enough; you just need to add the valid redirect URI http://127.0.0.1and enable OAuth 2.0 Device Authorization Grant if you want to provision a certificate from a machine without a browser.

After creating the client, you can create the OIDC provisioner:step ca provisioner add keycloak --type oidc \
--client-id step-ca --client-secret YOUR_CLIENT_SECRET \
--configuration-endpoint https://YOUR_KEYCLOAK_DOMAIN/realms/YOUR_REALM/.well-known/openid-configuration

Custom SSH Principals

By default, step ca will use your email and the part before the @ as SSH principals.
So for example@bekanntefreunde.de you would be able to use the certificate to authenticate as exmaple orexample@bekanntefreunde.de.

While this is fine for most use cases, I usually have a dot in my email address but not in my Unix username.
But as you can customize user attributes in Keycloak, I just added a new ssh_principals attribute to my realm.

To do this, you need to go to the Realm settings and add an attribute under the User profile tab. You probably want to make this attribute only editable by admins, as this gives the user permission to log into your servers as arbitrary Unix users.
To make this attribute available in the OpenID token, you need to add a new Client scope. Go to Client scopes and create a new scope, e.g. ssh_principals. Also switch the Include in token scope toggle on.
Next you need to add a mapper to the scope to link it to the user's attribute. This can be done in the Mappers tab. Create a mapper by configuration and choose User Attribute as mapper type.
Chose a name and select your ssh_principals attribute the User attribute dropdown.

Finally you just need to add the new client scope to your step ca client (Client > step-ca > Client scopes > Add client scope) and tell step ca to use it:step ca provisioner update keycloak --scope oidc --scope email --scope ssh_principalsThen, you need to create a template for SSH certificates that uses the new attribute:{ "type": {{ toJson .Type }}, "keyId": {{ toJson .KeyID }}, "principals": {{ toJson ((concat .Principals ((splitList " " (default "" .Token.ssh_principals)) | compact)) | uniq) }}, "extensions": {{ toJson .Extensions }}, "criticalOptions": {{ toJson .CriticalOptions }} }This is the same as the default template, only the principals line is modified to include the new attribute split by spaces and merged with the default principals (e.g. email and username).

Update the provisioner to use the new template:step ca provisioner update keycloak --ssh-template ./cert.tpl

Setup of SSH hosts

I will add this later, I promise :)

Ok, but how does it work?

Because the CA knows which servers have an active host certificate (you can actually check that with step ssh hosts) SSH checks if the host you are SSHing into has said certificate.
If so, a step proxy command is invoked, which ensures there is a valid SSH certificate in the SSH agent. If not, it issues a new one with the configured provisioner. In this case it opens a browser with Keycloak. Keycloak authenticates you and redirects you to a local webserver from step.
Since OAuth 2.0 Device Authorization Grant is also enabled, it is also possible to authenticate to Keycloak with a key you input on another device/manually.

The server also authenticates to the client with the host certificate. As this certificate is signed by our certificate authority, the client can check it with the following line in the known_hosts file: @cert-authority * SOMEKEY

I think this method of authenticating to SSH servers is quite cool.
Previously I have used FIDO2 keys stored on my YubiKey, but now I don't necessarily have to touch/have it with me anymore.
If you think this is still not enough, there are still possibilities for further customization. As an article on SSH Certificate Templates details, you can embed a ForceCommand, which limits access to only a specific shell/command, you can restrict SourceAddresses or limit x11 forwarding, etc.

I hope this was helpful somehow!
See you in the next post! <3

Build by Tristan Jockel :D

Source available on tristanjodsalz/personal-website