ADR-0007: Three CA keys behind a Signer trait, with KMS in production
- Status: Accepted (owner approved 2026-10-13)
- Date: 2026-10-13
Decision
Section titled “Decision”- Keys per purpose:
- internal (mTLS between components)
- user-x509 (user client certs)
- user-ssh (OpenSSH user certs)
- later, an optional host-ssh key for target host certs
limen_ca::Signertrait backends:FileSigner(dev and tests, PKCS#8 on disk with mode 0600) andKmsSigner(production:ECC_NIST_P256,SIGN_VERIFY,ECDSA_SHA_256,MessageType=RAW).- KMS returns DER ECDSA signatures (ANSI X9.62 / RFC 3279). X.509 uses DER directly. For OpenSSH certs we convert to the
ecdsa-sha2-nistp256signature format (string "ecdsa-sha2-nistp256",string (mpint r ‖ mpint s)), and test againstssh-keygen -Land real sshd. - X.509 cert building uses
rcgenwith a remote-key signing adapter. If rcgen can’t sign through an external signer cleanly, the fallback is building the TBSCertificate with the RustCryptox509-certcrate. Spike in Phase 1. - Rotation: a CA has states
activeandadditional_trusted. New keys are trusted first and used for signing later. The old key stays trusted for at leastmax_cert_ttlplus clock skew (5 min). For SSH,TrustedUserCAKeyslists both keys during the overlap. kms:Signis granted only to the control plane task role, with a key-policy condition onkms:SigningAlgorithm.
Amendment (Phase 2, 2026-10-14): two SSH CAs, per-session certificates
Section titled “Amendment (Phase 2, 2026-10-14): two SSH CAs, per-session certificates”- SSH user CA (
user_ssh): trusted by targets (TrustedUserCAKeys). It signs only per-session user certificates, at the request of a worker presenting a valid proxy grant. Each certificate has one principal (the granted login), lives for 1 minute (plus 1 minute of back-dating), carries key IDlimen:<session>:<user>, and grants onlypermit-pty. It’s issued once per grant. - SSH host CA (
host_ssh): trusted by the CLI through@cert-authorityin the profile’s known_hosts. It signs per-session host certificates for the worker’s ephemeral host key, with principals<resource>and<resource>.limen, valid for 5 minutes. - No SSH certificate at login. The original design issued one, signed by the CA that targets trust. Anyone holding it could have reached targets directly when the network allowed, skipping the proxy, the recording, and the audit trail. The user’s SSH client authenticates to the worker with
noneon a stream that mTLS and the grant already authenticate. - Both SSH CAs use the
Signertrait, so they can live in KMS. KMS’s DER ECDSA signature is converted to the SSHmpint(r) || mpint(s)form (limen_ca::ssh::der_to_ssh_ecdsa), and this is tested against local-kms.
Consequences
Section titled “Consequences”- + CA private keys never exist outside KMS in production.
- − Signing costs one KMS call per cert. At the target scale (500 users × a few certs an hour, plus one SSH certificate per session) that’s far below the quotas. Check the current KMS quota for asymmetric
Signin Phase 1. - − KMS being down blocks logins and new SSH sessions (see ADR-0011).