SSH certificates
Issue short-lived OpenSSH user certificates to people and workloads from an environment-scoped CA, and revoke them with a Key Revocation List.
Hikyo can run an OpenSSH user certificate authority per environment and
issue short-lived certificates to the humans and machine principals you
name. Hosts trust the CA once (TrustedUserCAKeys) and refuse revoked
certificates through a Key Revocation List (RevokedKeys). There are no
per-user authorized_keys files to maintain, and access ends on its own when
a certificate expires.
Hikyo manages certificates only. It is not a remote shell, a session proxy, a bastion or a host inventory, and it does not issue host certificates.
Concepts
- CA: an environment-scoped signing authority. Its private key is generated by Hikyo or imported, sealed under the project key, and never returned by any route, verb or audit record. A staging CA can never sign for production.
- Profile: the signing policy. It fixes which certificate principals
(remote user names) may be requested, the
force-commandandsource-addresscritical options, the allowed extensions, the allowed user key algorithms, the default and maximum lifetime, and who may request. - Certificate: one issued certificate. Hikyo keeps its public record (serial, principals, validity, requester) but never a private key.
Key algorithms are a closed set: ed25519, ecdsa-p256 and RSA of at least
3072 bits (rsa-3072). RSA CAs sign with rsa-sha2-512 only.
Set up a CA and a profile
hikyo ssh-ca create prod-users --env production
# or import an existing, unencrypted key without putting it on argv:
hikyo ssh-ca create prod-users --env production --key-file ./ca_key
hikyo ssh-profile create deploy --env production --ca <ca-id> \
--principal deploy --principal ops \
--key-algorithm ed25519 --key-algorithm ecdsa-p256 \
--default-ttl 1h --max-ttl 8h \
--source-address 10.0.0.0/8 \
--extension permit-pty --extension permit-port-forwarding \
--requester <principal-id> --requester <service-account-principal-id>Managing CAs and profiles needs manage-identities on the project. Listing
CAs, profiles and certificates, and fetching the trust bundle and KRL, needs
only read on the environment, so a host can use a workload credential.
Configure hosts
Point sshd at the trust bundle and the revocation list:
# /etc/ssh/sshd_config.d/hikyo.conf
TrustedUserCAKeys /etc/ssh/hikyo_user_ca.pub
RevokedKeys /etc/ssh/hikyo_revoked.krlRefresh both on a timer with a workload credential that holds read on the
environment. Write to a temporary file and rename, so sshd never reads a
half-written file:
#!/bin/sh
set -eu
TOKEN=/etc/hikyo/host-reader.token
hikyo ssh-ca trusted-keys <ca-id> --env production --token-file "$TOKEN" > /etc/ssh/hikyo_user_ca.pub.new
mv /etc/ssh/hikyo_user_ca.pub.new /etc/ssh/hikyo_user_ca.pub
rm -f /etc/ssh/hikyo_revoked.krl.new
hikyo ssh-ca krl <ca-id> --env production --token-file "$TOKEN" --output-file /etc/ssh/hikyo_revoked.krl.new
mv /etc/ssh/hikyo_revoked.krl.new /etc/ssh/hikyo_revoked.krlsshd rereads both files on every authentication; no reload is needed. If a
refresh fails, keep the last good files: an old KRL still refuses everything
it listed. The refresh interval is your revocation latency; a minute is
typical.
Request a certificate
A requester must be on the profile’s requester list and hold read on the
environment. A human requester also completes the mint reauthentication
ceremony first (the same one dynamic-secret leases use).
Certify your own key:
hikyo ssh-cert issue --profile <profile-id> --env production \
--principal deploy --ttl 2h \
--public-key-file ~/.ssh/id_ed25519.pub --cert-file ~/.ssh/id_ed25519-cert.pub
ssh deploy@hostOr let Hikyo generate a key pair. The private key is shown exactly once,
through the same print rules as other disclosures (a terminal, a fresh 0600
--output-file, or --dangerously-print), and is never stored:
hikyo ssh-cert issue --profile <profile-id> --env production \
--principal deploy --output-file ~/.ssh/hikyo_deploy
# writes ~/.ssh/hikyo_deploy (0600) and ~/.ssh/hikyo_deploy-cert.pub
ssh -i ~/.ssh/hikyo_deploy deploy@hostA request may only narrow its profile: fewer principals, a narrower
source-address, fewer extensions, a shorter lifetime. Anything wider is
refused. There is no renewal; request a new certificate when you need one.
Each certificate’s key ID is hikyo:<environment>:<certificate>:<principal>,
so sshd logs name the Hikyo principal that asked for it.
Revocation, expiry and deletion
These are three different things, and the certificate status keeps them
apart:
| Status | Meaning | Hosts |
|---|---|---|
active | Issued, unexpired, signed by a trusted key. | Accept it. |
revoked | Cryptographically revoked. in_krl says whether the KRL still has to carry it. | Refuse it once they refresh the KRL. |
expired | Past its valid_before. Leaves the KRL: there is nothing left to refuse. | Refuse it on their own. |
untrusted | Its signing key was retired, or its CA deleted. | Refuse it once they refresh the trust bundle. |
Revocation happens when:
- the requester or a
manage-identitiesholder runshikyo ssh-cert revoke; - a requester is removed from a profile (their live certificates through it are revoked in the same write);
- a requester loses its grant or is deleted: every node runs a sweeper that re-checks each live certificate’s requester about every 30 seconds and revokes on a definite refusal;
- a profile is deleted with
--revoke-issued.
Deleting is not revoking. Deleting a profile without --revoke-issued
stops new issuance and leaves issued certificates valid until they expire.
Deleting a CA destroys its signing key, but hosts that still list its keys in
TrustedUserCAKeys keep accepting its certificates until they expire: remove
the keys from host trust. Disabling a profile stops issuance on every node
immediately and revokes nothing. An environment that still has a live SSH CA
cannot be deleted.
The KRL is bounded at 65,536 serials per CA. Past that, fetching it fails
rather than publishing a truncated list; with short lifetimes this bound is
not reachable in practice. hikyo_ssh_krl_entries reports the current size.
Rotate a CA
hikyo ssh-ca rotate <ca-id> --env production --overlap 8h
hikyo ssh-ca retire-key <ca-id> --key <old-key-id> --env production # optional: end the overlap earlyRotation creates a new signing key at once and moves the old key to
retiring: it can no longer sign (its sealed private key is destroyed in
the same transaction) but stays in the trust bundle until the overlap ends, so
certificates issued just before the rotation keep working. The overlap
defaults to the time until the last live certificate signed by the old key
expires and is capped at 30 days. Hosts pick up the new key on their next trust-bundle refresh; refresh
them before the overlap ends.
Multi-node behavior
Issuance is one transaction: it re-reads the active key, the profile’s enabled flag and the requester list, signs, and records the certificate together. A certificate leaves the server only from a committed transaction, so a node that raced a rotation, a disable or a requester removal on another node cannot hand one out. Nodes hold no cached signing key.
Restore
A restore rolls the certificate records back to the backup’s instant. A
certificate issued and revoked after the backup has no record, so it cannot be
in the KRL. After a restore, rotate every SSH CA with --overlap 0 and
refresh host trust; certificates from before the rotation then stop working.
Monitoring
/metrics exposes label-free gauges: hikyo_ssh_certificates_active,
hikyo_ssh_krl_entries, and hikyo_ssh_gauges_known (0 when the scrape could
not measure them; the two value gauges are then omitted rather than shown as
zero). Every CA, profile, issuance and revocation change is an audit event
(ssh.*); no private key ever appears in one.