This repo contains a showcase of how to use SSH certificates (for hosts & users) generated by step-ca
using the step CLI's
ssh
sub-command.
If you haven't already you should read our blog post on why SSH certificates are better than SSH public keys for authentication and how you can achieve de facto SSH Single Sign-on while doing away with pesky public key management across your server fleet.
This document describes:
- how to provision
step-ca
to issue SSH host & user certificates. - how
sshd
is configured to accept user certificates for client authentication using a CA key. - how
sshd
is configured to present a host certificate for host authentication on the client-side. - how to configure a user's
ssh
to accept host certificate signed by a CA key. - how to configure a user's
ssh
to present a user certificate for authentication on the server-side.
The code in this repo comes with a pre-generated PKI. You will need step
v0.13.3+ (installation docs)
and Vagrant (plus a provider like
VirtualBox) installed locally.
We're going to run a CA in your local environment, and we'll use ssh
to
connect to a Vagrant VM
(representing a remote host) that has sshd
pre-configured to accept
user certificates signed by our CA.
With Vagrant installed, run the following commands inside the repo:
$ vagrant up
Bringing machine 'testhost' up with 'virtualbox' provider...
==> testhost: Importing base box 'ubuntu/bionic64'...
[...]
==> testhost: Preparing network interfaces based on configuration...
testhost: Adapter 1: nat
testhost: Adapter 2: hostonly
==> testhost: Forwarding ports...
testhost: 22 (guest) => 2222 (host) (adapter 1)
==> testhost: Running 'pre-boot' VM customizations...
==> testhost: Booting VM...
==> testhost: Waiting for machine to boot. This may take a few minutes...
testhost: SSH address: 127.0.0.1:2222
testhost: SSH username: vagrant
testhost: SSH auth method: private key
testhost: VirtualBox Version: 6.0
[...]
==> testhost: Setting hostname...
==> testhost: Configuring and enabling network interfaces...
==> testhost: Mounting shared folders...
testhost: /keys => /Users/sourishkrout/dev/src/smallstep/code/src/github.com/smallstep/step-examples/ssh-example/keys
testhost: /vagrant => /Users/sourishkrout/dev/src/smallstep/code/src/github.com/smallstep/step-examples/ssh-example
==> testhost: Running provisioner: shell...
testhost: Running: inline script
testhost: Add following line to your local hosts ~/.ssh/known_hosts file to accept host certs
testhost: @cert-authority * ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJJM+jkIdieQvdPb8DwnfnJudEc9PgVBqLDWHKgvqoIiMXhuIyGstQ9ULOBMdJkqxMjkRTFZp1iFvIk+iU6hwTA=
testhost: Add a /etc/hosts file entry `testhost` to resolve to 192.168.0.101
testhost: Check out README.md to learn how to grab user ssh certs to log into testhost
Go ahead and follow the instructions printed by Vagrant. This will enable your
local SSH client to accept SSH host certificates (signed by the root SSH host
private key). The following command will append the SSH host CA key
(root SSH host public key corresponding to the root SSH host private key) to
your local known_hosts
file:
me@local:~$ echo "@cert-authority * ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJJM+jkIdieQvdPb8DwnfnJudEc9PgVBqLDWHKgvqoIiMXhuIyGstQ9ULOBMdJkqxMjkRTFZp1iFvIk+iU6hwTA=" >> ~/.ssh/known_hosts
You can also find the root SSH host CA key stored at
step/certs/ssh_host_key.pub
in this repo.
Certificates bind names to public keys. This SSH host certificate has the
identity testhost
which is why the following entry must be added to the
local /etc/hosts
file on the VM:
vagrant@testhost:~$ tail -n 1 /etc/hosts
192.168.0.101 testhost
Vagrant has already configured sshd
on testhost
, the VM
generated by Vagrant. Please note that for demo purposes the PKI is shared with
the VM using a shared directory mount. Below you can see the relevant lines
from the testhost
VM's sshd_config
:
vagrant@testhost:~$ tail -n 5 /etc/ssh/sshd_config
# PermitTTY no
# ForceCommand cvs server
TrustedUserCAKeys /keys/ssh_user_key.pub
HostKey /keys/ssh_host_ecdsa_key
HostCertificate /keys/ssh_host_ecdsa_key-cert.pub
- TrustUserCAKeys: The root SSH user public key used to verify SSH user certificates.
- HostKey: The SSH private key specific to this host.
- HostCertificate: The SSH public certificate that uniquely identifies this host (signed by the root SSH host private key).
A valid user certificate is required to log into the testhost
VM. Using the
step
CLI we will authenticate with our SSH-enabled CA and fetch a new SSH
certificate.
In one terminal window run the following command to startup your CA (password
is password
):
me@local:~$ export STEPPATH=`pwd`/step
me@local:~$ step-ca step/config/ca.json
Please enter the password to decrypt step/secrets/intermediate_ca_key: password
Please enter the password to decrypt step/secrets/ssh_host_key: password
Please enter the password to decrypt step/secrets/ssh_user_key: password
2019/09/11 22:59:01 Serving HTTPS on :443 ...
In another terminal window run:
me@local:~$ export STEPPATH=`pwd`/step
me@local:~$ step ssh certificate testuser testuser_ecdsa --ca-url https://localhost --root step/certs/root_ca.crt
✔ Provisioner: admin (JWK) [kid: ux6AhkfzgclpI65xJeGHzNqHCmdCl0-nWO8YqF1mcn0]
✔ Please enter the password to decrypt the provisioner key: password
✔ CA: https://localhost
Please enter the password to encrypt the private key: your-own-password
✔ Private Key: testuser_ecdsa
✔ Public Key: testuser_ecdsa.pub
✔ Certificate: testuser_ecdsa-cert.pub
✔ SSH Agent: yes
NOTE:
step-ca
enforces authentication for all certificate requests and uses the concept of provisioners to carry out this enforcement. Provisioners are configured instep/config/ca.json
. Authenticating as one of the sanctioned provisioners indicates tostep-ca
that you have the right to provisione new certificates. In the above invocation ofstep ssh certificate
we have authenticated our request using a JWK provisioner, which simply requires a password to decrypt a private key. However, there are a handful of supported provisioners, each with it's own authentication methods. The OIDC provisioner is particularly interesting for SSH user certificates because it enables Single Sign-On SSH.
Conveniently, step ssh certificate
adds the new SSH user certificate to your
local ssh agent
. The default lifetime of an SSH certificate from step-ca
is
4hrs. The lifetime can be configured using command line options (run step ssh certificate -h
for documentation and examples).
me@local:~$ ssh-add -l
256 SHA256:xt5VeMEG8uf+SBlddauylJHv9+Bl0E6H+46AV94+Its testuser (ECDSA-CERT)
me@local:~$ ssh testuser@testhost
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-55-generic x86_64)
[...]
testuser@testhost:~$
Boom! As you can see the testhost
VM will welcome you with a matching
testuser@testhost
prompt.
Learn how to use OAuth OIDC proviers like Gsuite or Instance Identity Documents
to bootstrap SSH host and user certificates in our blog post If you’re not using SSH
certificates you’re doing SSH
wrong or check out the
step
CLI reference at
https://smallstep.com/docs/cli/ssh/.
This example repo includes a pre-generated SSH host certificate and key. To replace it or generate SSH certificates for other hosts running following command:
vagrant@testhost:~$ step ssh certificate --host --principal testhost \
--principal testhost.internal \
testhost ssh_host_ecdsa_key
Where --principal
identifies the hostname(s) (ideally FQDNs) for the machine.
For a single principal you can short cut the command to:
vagrant@testhost:~$ step ssh certificate --host testhost ssh_host_ecdsa_key
We recommend using your own PKI for usage outside of this example. You can
initialize your step-ca
with
both X509 and SSH certificates using the following command:
$ export STEPPATH=/tmp/mystep
$ step ca init --ssh
✔ What would you like to name your new PKI? (e.g. Smallstep): Smallstep
✔ What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
✔ What address will your new CA listen at? (e.g. :443): :443
✔ What would you like to name the first provisioner for your new CA? (e.g. you@smallstep.com): admin
✔ What do you want your password to be? [leave empty and we'll generate one]:
Generating root certificate...
all done!
Generating intermediate certificate...
Generating user and host SSH certificate signing keys...
all done!
✔ Root certificate: /tmp/mystep/certs/root_ca.crt
✔ Root private key: /tmp/mystep/secrets/root_ca_key
✔ Root fingerprint: d601c93a6256080e42cf02087fdc737f1429226ada6c040bac6494332e01527e
✔ Intermediate certificate: /tmp/mystep/certs/intermediate_ca.crt
✔ Intermediate private key: /tmp/mystep/secrets/intermediate_ca_key
✔ SSH user root certificate: /tmp/mystep/certs/ssh_user_key.pub
✔ SSH user root private key: /tmp/mystep/secrets/ssh_user_key
✔ SSH host root certificate: /tmp/mystep/certs/ssh_host_key.pub
✔ SSH host root private key: /tmp/mystep/secrets/ssh_host_key
✔ Default configuration: /tmp/mystep/config/defaults.json
✔ Certificate Authority configuration: /tmp/mystep/config/ca.json
Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
Now you can launch your instance of
step-ca
with your own PKI like
so:
$ step-ca $(step path)/config/ca.json
Please enter the password to decrypt /tmp/mystep/secrets/intermediate_ca_key:
Please enter the password to decrypt /tmp/mystep/secrets/ssh_host_key:
Please enter the password to decrypt /tmp/mystep/secrets/ssh_user_key:
2019/09/11 23:34:13 Serving HTTPS on :443 ...
Please note that after you regenerate ssh_host_key.pub
and ssh_user_key.pub
you will have to reconfigure ssh
and sshd
for clients and hosts to accept
the new CA keys. Check out this host bootstrapping script for
configuration examples.