Part 1 was technically correct, but turns out that it’s too manual to be used by me:
- you have to do it only once a while (once a year, because certs might have a 1 year validity time)
- you don’t do it if it’s a lot of extra manual work
So here is Part 2 because I found something easier: Step CLI and Step CA.
The main difference to the openssl method (which continues to work): this CA runs as a service. So on the client side, you just need once to connect and then you can get certificates from a single place.
Get the releases and install, either as Debian package or tar file.
Extra step on ARMv7 (and possible all 32 bit architectures): replace “badger” with “badgerv2” in .step/config/ca.json. Also add those “claims” section under “authority” key unless you like the defaults:
"authority": {
"claims": {
"minTLSCertDuration": "5m",
"maxTLSCertDuration": "168h",
"defaultTLSCertDuration": "24h",
"disableRenewal": false,
"minHostSSHCertDuration": "5m",
"maxHostSSHCertDuration": "168h",
"minUserSSHCertDuration": "5m",
"maxUserSSHCertDuration": "24h"
},
"provisioners": [
Then run your CA by
harald@opz3:~$ step-ca .step/config/ca.json --password-file step-ca-pw.txt
2020/11/19 18:57:55 Serving HTTPS on :8443 …
To set up the client side, install step-cli and then do:
step ca bootstrap --ca-url https://opz3.lan:8443 \
--fingerprint 8d9345ed9c8f84729fb82005cf36b9e595c7a40efe2db52bee114c2dbdabd63d
The fingerprint you got during ca initialization, or get the root certificate and run a fingerprint:
# on CA server
> step ca root root.crt
> step certificate fingerprint root.crt
Once done, back on the client, getting a certificate is simple:
❯ step ca certificate m75q.lan m75q.crt m75q.key --provisioner-password-file ./pass.txt
and renew like this (–force to overwrite without asking):
❯ step ca renew --force m75q.crt m75q.key
# Or automated and restarting nginx:
❯ step ca renew --daemon --exec "nginx -s reload" m75q.crt m75q.key
Proper docs for step CLI and step CA. There’s also nice examples for mTLS
Update: See https://github.com/smallstep/certificates/discussions/427 which gave me some hints how to decouple the key encryption passwords for the 3 different keys:
- the root CA key
- the intermediate CA key
- the provisioner key
To change the passphrases:
❯
cd $(step path)
❯
cd secret/
❯
cp intermediate_ca_key intermediate_ca_key.original
❯
openssl ec -in intermediate_ca_key.original | openssl ec -out intermediate_ca_key -aes256
read EC key
read EC key
Enter PEM pass phrase: <OLD PASSPHRASE>
writing EC key
writing EC key
Enter PEM pass phrase: <NEW_PASSPHRASE>
Verifying - Enter PEM pass phrase: <NEW_PASSPHRASE>
Now when you start your CA (via
step-ca .step/config/ca.json
it’ll ask for the passphrase of the intermediate certificate. Repeat for the root certificate. You can remove it to another place as it’s not used.
The JWK provisioner’s encryptedKey (see $(step path)/config/ca.json) still has the original passphrase from the initial setup. Let’s fix this:
❯ cd $(step path)/config
❯
jq -r '.authority.provisioners[] | select(.type=="JWK") | .encryptedKey' ca.json | step crypto jwe decrypt > k.json
Please enter the password to decrypt the content encryption key:
{"use":"sig","kty":"EC","kid":"cnUEPau6NgopLjZHrsL3v3PXvF14EIAe-PIHmjA5fQQ","crv":"P-256","alg":"ES256","x":"xxxxxxxxxx","y":"yyyyyyyyyy","d":"dddddddddd"}
On the clients side you can now use the decrypted k.json like this:
❯ TOKEN=$(step ca token foo --provisioner "provisioner@name" --key k.json)
✔ Provisioner: provisioner@name
(JWK) [kid: -6pq-22r6yaQg]
❯ step ca certificate foo foo.crt foo.key --token $TOKEN
Update 2020-11-21: This is working so well, I made an Ansible Playbook to make (re-)installs much easier for me: https://github.com/haraldkubota/step-ca
Renewing certificates is quite simple too (more details here):
❯
step ca renew --daemon --exec "nginx -s reload" internal.crt internal.key