Self-hosting PKI with Step-CA
April 23, 2024This article will receive updates in the next few weeks
There are many reasons to self-host your PKI instead of just using Letsencrypt or the like. Maybe your infrastructure doesn't have direct access to the internet (and DNS0 challenges can be messy to set up), or maybe you just don't want the world to know what's on your network. It's easy to forget that every publicly issued certificate ends up in a public Certificate Transparency log, like this.
Luckily it's pretty easy to self-host your PKI these days. You can easily set up an ACME-server and continue to use the modern tools you've gotten used to, like certbot, dehydrated, or tinyacme.
The solution I've gone for is step-ca
from Smallstep. Smallstep delivers solutions for certificate management, and step-ca
is their open source solution for this.
step-ca
is an online Certificate Authority (CA) for secure, automated X.509 and SSH certificate management. It's the server counterpart to step CLI. It is secured with TLS, and it offers several configurable certificate provisioners, flexible certificate templating, and pluggable database backends to suit a wide variety of contexts and workflows. It employs sane default algorithms and attributes, so you don't have to be a security engineer to use it securely.
Installation
Installation is pretty straight forward, and they support a variety of platforms.
I've done this on OpenBSD, but I'll add inn steps for Debian/Ubuntu later.
We start off by installing step-ca
and step-cli
.
$ doas pkg_add -i step-ca step-cli
And that's it. step-ca
is now installed. There's an rc
-config in
/etc/rc.d
, and a new user (_step-ca
) was automagically added.
Configuration
Setting things up now could be as easy as running step ca init
as the right
user, but this sets up a root certificate that is valid for ten years.
I think ten years might be the worst expirationtime one can choose. It's long enough that you've definitely forgotten how you set things up, and short enough that things are probably still using it when it expires. Either choose something short enough that you'll easily be able to rotate it when it expires, or something long enough that you won't have to worry. I chose the latter, and went for 100+ years.
First cd into /var/step-ca
, create folders for secrets and certificates, and
create a random password for the intermediate certificate that will be used to
sign other certificates.
$ doas su -l -s /bin/sh _step-ca
$ cd /var/step-ca
$ mkdir /var/step-ca/{certs,secrets}
$ chmod 700 /var/step-ca/{certs,secrets}
$ tr -cd '[:alnum:]_-' < /dev/urandom | fold -w 64 | head -n1 > secrets/intermediate_ca_pw
$ chmod 640 secrets/intermediate_ca_pw
I've placed the intermediate certificate password in a file so that I don't have to type it in every time I start the ACME-server. The root certificate will have a different password, and will have to be typed in any time you want to use it.
Now, let's create the root certificate.
$ STEPPATH=/var/step-ca step certificate create --not-after 999999h --profile root-ca my-CA certs/root_ca.crt secrets/root_ca_key
Type in a good password, and write it down somewhere!
$ export STEPPATH=/var/step-ca
$ step ca init --root certs/root_ca.crt --key secrets/root_ca_key
- password from above
- Select "standalone"
- name the CA
- hostname for ACME server
- IP and port to bind to. I chose 127.0.0.1:1025
- Name "first provisioner". Dunno what this is. chose jwk
- password for intermediate key - copy from secrets/intermediate_ca_pw
- would you like to overwrite? yes
The last question where it asks if you'd like to overwrite is about the root
certificate. It's asking if it can copy the root-certificate into the
certs/
-folder, where it already is. Just answer yes.
Now it's time to set up some provisioners. This is various ways for step-ca
to
sign your certificates, like ACME. I'm setting up these to issue certificates
that's valid, by default, for 60 days, and max 90 days.
$ step ca provisioner add acme --type ACME"
$ step ca provisioner update jwk --x509-max-dur=2160h --x509-default-dur=1440h"
$ step ca provisioner update acme --x509-max-dur=2160h --x509-default-dur=1440h"
The jwk
-one is for manual signing.
That's pretty much it. Now we just have to enable the service, and start it.
$ exit # <- if you're still logged in as _step-ca
$ doas rcctl enable step_ca
$ doas rcctl set step_ca flags --password-file=secrets/intermediate_ca_pw
$ doas rcctl start step_ca
And install the new root-certificate
$ doas sh -c "echo '\n### step-ca local root\n' >> /etc/ssl/cert.pem"
$ doas sh -c "openssl x509 -in /var/step-ca/certs/root_ca.crt -text >> /etc/ssl/cert.pem"
Oh, and you might have to create a certificate for your ACME-server. This is
where that jwk
-certificate comes in handy!
# doas -u _step-ca sh -c "env STEPPATH=/var/step-ca step ca certificate --offline acme.dnns.red acme.dnns.red.crt acme.dnns.red.key"
Adding the root certificate to your devices
Add certificate on linux:
- copy
/var/step-ca/certs/root_ca.crt
to/usr/local/share/ca-certificates/nameofroot.crt
- run
update-ca-certificates
On macOS:
- Copy certificate to disk. maybe ~/Downloads/my-CA.crt
- Open "Keychain Access.app"
- Choose login keychain in left menu
- Choose "Certificates" tab
- Drag & Drop certificate
- new cert was added, but is untrusted. double-clock it, expand "Trust", choose Always Trust
- close and enter password
On iOS:
- copy certificate to phone. Maybe by mail.
- find certificate on phone, open, and choose to "install profile"
- Go to settings app and review profile. Choose install again.
- Then go to General -> About -> Certificate Trust Settings -> and activate trust for the certificate.
Using the new ACME-server
certbot certonly --server https://YOUR-ACME-SERVER.example.com/acme/acme/directory --noninteractive --agree-tos --email root@example.com -d domain.example.com
Debian / Ubuntu
There aren't repo packages for Debian/Ubuntu, so there you'll have to download the package and install manually. Luckily, this isn't complicated:
$ wget https://dl.smallstep.com/cli/docs-ca-install/latest/step-cli_amd64.deb
$ sudo dpkg -i step-cli_amd64.deb
$ wget https://dl.smallstep.com/certificates/docs-ca-install/latest/step-ca_amd64.deb
$ sudo dpkg -i step-ca_amd64.deb
More is coming here.