# SSH certificate CA runbook — step-ca on tsys-ca

> Ticket [#385](https://projects.knownelement.com/issues/385) · parent
> [#697](https://projects.knownelement.com/issues/697)/[#800](https://projects.knownelement.com/issues/800)
> Lane index https://community.turnsys.com/t/297

## Live state (2026-09-05)

- SSH CA enabled on the prod step-ca (same endpoint as ACME:
  https://100.102.96.24:8443). Separate ED25519 signing keys:
  `ssh_host_ca_key` + `ssh_user_ca_key` (in /opt/stepca-prod/data/secrets,
  never leave the host). Public CA: `ssh-ca/fleet_user_ca.pub` in this repo
  + artifact mirror.
- Provisioner: JWK `admin` with `claims.enableSSHCA=true`.
- **VERIFIED e2e**: user cert (principal root, 24h) issued and used to SSH
  into sectestbed 5104 — passwordless, no static key. PLAYBOOK RECORD in
  journal of #385.

## Host onboarding (one file + one reload)

    install -m 644 fleet_user_ca.pub /etc/ssh/fleet_user_ca.pub
    echo "TrustedUserCAKeys /etc/ssh/fleet_user_ca.pub" > /etc/ssh/sshd_config.d/99-fleet-ca.conf
    systemctl reload sshd

Rollout to the fleet = KNELIAC role (queued; sectestbed wave first, then
prod hosts under GLPI CR).

## Client flow (operators + automation)

    step ssh certificate root-cert ./id_root --principal root \
      --provisioner admin --provisioner-password-file <pw> \
      --ca-url https://100.102.96.24:8443 --root fleet-root.crt \
      --not-after 24h
    ssh -i ./id_root -o CertificateFile=./id_root-cert.pub root@<host>

Better UX once keyed hosts trust the CA: `step ssh login` (agent-managed,
`step ssh` known-hosts propagation via `--ssh` host templates — queued).

## Hardening TODO (before prod wave)

- host certificates (`--host`) + sshd HostCertificate so CLIENTS also
  verify servers (kills TOFU).
- JWK admin provisioner → per-operator provisioners; automation gets a
  dedicated provisioner with restricted principals + short TTLs.
- Enable `--acme-strict-fqdn`-style hygiene: review principals allowed.
