Internal CA (step-ca)#
FalseBlue runs a private step-ca
certificate authority on ereshkigal (modules/nixos/pki/step-ca.nix).
Every NixOS/darwin host in the flake gets and auto-renews its cert over
ACME with no manual steps (tsunaminoai.pki.acme → security.acme pointed at
https://ca.falseblue.com/acme/acme/directory). You only need the commands
below for devices that aren’t in the flake — the biggest one being the
Home Assistant appliance (HAOS), which serves its own cert on :8123.
| Fact | Value |
|---|---|
| CA URL | https://ca.falseblue.com |
| Root fingerprint | 66b8732f7a4630403455da2ef05fef269ae63e390c6b5f50f82ecdf872585fe6 |
| Root cert (in repo) | pki/authorities/falseblue.crt (CN=falseblue.com, valid to 2032) |
| Interactive provisioner | tsunami@falseblue.com (JWK, password-protected) |
| ACME provisioner | acme (what the flake hosts use) |
| Cert lifetimes | default 32 days, max 90 days (--not-after up to 2160h) |
step-ca listens on :9443 and is fronted at https://ca.falseblue.com
(:443). Certificates for *.falseblue.com, *.<tailnet>, homeassistant,
and any 192.168.0.0/24 / 100.0.0.0/8 IP are permitted by policy.
Run the step client on a flake host
ereshkigal (and any NixOS host importing the PKI module) already has
step-cli on PATH and trusts the root system-wide, so it’s the easiest
place to run these commands. HAOS can’t easily run step itself — issue
the cert on ereshkigal, then copy the files onto the appliance.
One-time: bootstrap trust to the CA#
On a machine that doesn’t already trust the CA (skip on flake hosts):
step ca bootstrap \
--ca-url https://ca.falseblue.com \
--fingerprint 66b8732f7a4630403455da2ef05fef269ae63e390c6b5f50f82ecdf872585fe6
This writes ~/.step/ with the root and CA config so later step ca …
commands need no --ca-url/--root flags.
Renew a cert that is still valid#
If the certificate hasn’t expired yet, renewal is a single command — it re-authenticates with the existing cert+key (mutual TLS), no provisioner password needed:
step ca renew host.crt host.key --force
Expired certs cannot be renewed
The CA sets allowRenewalAfterExpiry = false. Once a cert is past
notAfter, step ca renew fails — you must re-issue it (next
section). This is the case for the Home Assistant cert today (it expired
before anyone noticed — the exact gap the
status page now closes).
Issue (or re-issue) a certificate#
Use the interactive JWK provisioner. You’ll be prompted for the provisioner
password (set at step ca init; kept in sops under step-ca/password-file).
Example for Home Assistant (both the homeassistant DNS name and its LAN
IP as SANs, 90-day lifetime):
step ca certificate "homeassistant" ha.crt ha.key \
--provisioner "tsunami@falseblue.com" \
--san homeassistant \
--san 192.168.0.55 \
--not-after 2160h
For a scripted / non-interactive flow, split it into a token + issuance (the token carries the authorization; hand it to whoever generates the CSR):
TOKEN=$(step ca token homeassistant \
--provisioner "tsunami@falseblue.com" \
--san homeassistant --san 192.168.0.55 \
--not-after 2160h \
--password-file /run/secrets/step-ca/password-file) # on ereshkigal
step ca certificate homeassistant ha.crt ha.key --token "$TOKEN"
step ca token homeassistant.armadillo-banfish.ts.net \
--provisioner "tsunami@falseblue.com" \
--ca-url https://ca.falseblue.com:9443 \
--root /home/tsunami/code/nix/nix-flake-final/pki/authorities/falseblue.crt \
--san 192.168.0.55 \
--san homeassistant.gensokyo \
--san homeassistant \
--san homeassistant.armadillo-banfish.ts.net
Install the cert on Home Assistant (HAOS)#
HAOS is not managed by this flake, so this part is manual:
- Copy
ha.crtandha.keyinto the appliance’s/ssl/directory (via the Samba/File-Editor add-on, orscpif SSH is enabled). - In
configuration.yaml:http: ssl_certificate: /ssl/ha.crt ssl_key: /ssl/ha.key - Restart Home Assistant (Developer Tools → YAML → Restart, or reboot).
- Verify:
openssl s_client -connect 192.168.0.55:8123 -servername homeassistant </dev/null 2>/dev/null | openssl x509 -noout -issuer -datesshould now showissuer=CN=falseblue.comand a freshnotAfter.
Automating renewal#
For a Linux host you control, run step ca renew on a timer well before
expiry (renew at ~2/3 of lifetime):
step ca renew --daemon --expires-in 20d host.crt host.key \
--exec "systemctl reload nginx"
HAOS makes long-running daemons awkward; the pragmatic path for the appliance is a calendar reminder to re-issue every ~2 months, or move HA behind the step-ca nginx edge like the other services so it never needs a device-local cert at all.
How this ties into monitoring#
- The status page probes Home Assistant with
client.insecure = true(we don’t trust its self-signed cert), and that endpoint is labelledextra-labels.cert = "external"so theProbedCertificateExpiringSoonalert skips it — an unmanaged appliance cert isn’t our renewal to watch. - Every endpoint whose cert is ours (the
*.falseblue.com/*.<tailnet>vhosts) is watched: that alert fires at <14 days remaining, catching a silently-stuck ACME renewal before browsers start erroring. - If you issue a proper FalseBlue cert for HA (above) and want it monitored,
drop
client.insecureand thecert = "external"label from its endpoint in the ereshkigal host config.
Related#
- status-page.md — the synthetic checks + cert-expiry alert.
- backups.md — the step-ca BadgerDB is backed up to voile/borg daily.