When it comes to secure shell (SSH) authentication, there is a widespread consensus that SSH certificates hold an edge over SSH keys (though SSH keys are currently more widely used). SSH keys are easy to provision, but are hard to distribute and invalidate. SSH certificates, on the other hand, are easier to use and audit, but harder to provision. Happily, Platform.sh has made provisioning certificates easier for our users.
At Platform.sh, we use Secure Shell (SSH) certificates to authenticate users into their applications’ servers. We rely on a service named “Certifier” to provide the certificates. Certifier is, in essence, pretty simple:
It exposes an endpoint with the list of the certificate authority’s public keys.
It exposes an endpoint that you can POST to with a public ssh key and then returns an SSH certificate that is valid for one hour.
The flow is mostly transparent thanks to some tricks in the ~/.ssh/config
file, such as:
Match host "*.platform.sh" exec "platform ssh-cert:load"
In this example, the platform ssh-cert:load
command is fetching a new SSH certificate.
Effectively, this lets us provide a mostly transparent workflow. Our customers run their SSH commands, and Certifier automatically grabs a new SSH certificate every time one is needed. Given that each certificate is only valid for one hour, if a customer needs to offboard a user, we just need to ensure that Certifier is no longer delivering certificates to that user.
Speaking of which, how does Certifier authenticate that user? It’s obviously not just blindly returning SSH certificates to whomever is asking for one. Certifier is relying on one of the most prominent authentication technologies on the web: OAuth2.
When a Platform.sh customer logs in or tries to SSH, they’re effectively fetching an SSH certificate while passing along an OAuth access token that they previously obtained from our Authentication service. Certifier uses that token to get information about the customer from the OAuth token introspection endpoint.
As an example, this is what the introspection endpoint can return:
$ curl https://accounts.platform.sh/oauth2/tokens/"$(platform auth:token)"
{
"amr": [
"sso",
"mfa"
],
"auth_time": 1601545043,
"exp": 1601571772,
"grant": "refresh_token",
"iat": 1601570871,
"iss": "https://auth.api.platform.sh",
"jti": "<redacted>",
"nbf": 1601570871,
"ns": "platformsh",
"sub": "<redacted>",
"active": true,
"scope": "offline_access",
"client_id": "platform-cli",
"grant_type": "refresh_token",
"mfa_verified": 1601545043,
"expires": 1601571772,
"user_uuid": "<redacted>",
"access_token": "<redacted>"
}
By using this endpoint, Certifier can put the following metadata in the SSH certificate before signing it:
With all that said, with all this information available for any system, such as the edge layer accepting SSH connections, Certifier can:
In practice, we can put even more data on the certificates; that’s what we’re doing for the Platform.sh staff. For example, when a Platformer connects with administrative privileges, the certificate embeds some information such as:
Essentially, using short-lived SSH certificates with Certifier gives Platform.sh customers the best of all worlds:
Thanks to Certifier, Platform.sh customers can have transparent, secure, and keyless SSH connections using our Platform.sh CLI. You can also mandate multi-factor authentication for all of your projects. These are all things that will make your compliance officers happy. And we are all about making people happy.
(For additional information on using keys to authenticate users and devices, please read “What is PKI?”)