Publish OCSP snapshots through Redis

This commit is contained in:
2026-10-28 20:27:07 +00:00
parent a7cfda4167
commit df98ada177
24 changed files with 634 additions and 2460 deletions
+20 -19
View File
@@ -193,33 +193,34 @@ only as SHA-256 digests, and cannot mint/revoke tokens.
## OCSP responder
KCA can publish full per-CA certificate-status snapshots to any S3-compatible
bucket and ships a standalone Rust/Axum responder on port `2560`.
KCA publishes full per-CA certificate-status snapshots to the same Redis
instance and logical database used for sessions, and ships a standalone
Rust/Axum responder on port `2560`.
- KCA publishes `ocsp/<KCA CA UUID>.json` whenever certificates are issued,
renewed, revoked, deleted or imported. Revocation reasons are
included; this is a full status snapshot, not a revoked-only list.
- The responder has no Postgres/Redis/KCA master-key access. It only reads the
configured S3 prefix and uses a delegated OCSP signing certificate/key per
CA.
- KCA publishes `ocsp:<KCA CA UUID>` whenever certificates are issued, renewed,
revoked, deleted or imported. Revocation reasons are included; this is a full
status snapshot, not a revoked-only list. OCSP keys are separate from session
keys through their prefix.
- The responder has no Postgres or KCA master-key access. It reads only OCSP
snapshots from Redis and uses a delegated OCSP signing certificate/key per
CA. Where Redis ACLs are available, restrict the responder to `ocsp:*` so it
cannot read session entries.
- Each CA snapshot is cached for one second with a single-flight refresh, so
concurrent requests cause at most one S3 `HeadObject` plus conditional
`GetObject` sequence per CA per second. The ETag condition keeps the body and
expiry metadata from different object versions from being combined.
concurrent requests share one atomic Redis `GET` + `PTTL` lookup per CA.
- The publisher refreshes unchanged snapshots every 30 seconds by default and
sets each object's `Expires` header 60 seconds in the future. The responder
rejects expired objects even when S3 is readable and returns `tryLater` when
no unexpired snapshot is available. S3 does not delete objects at `Expires`.
sets each key's expiry to 60 seconds. A missing or expired snapshot produces
`unknown`; Redis failures produce `tryLater` when no unexpired cached
snapshot remains.
- OCSP `thisUpdate` is the response time and `nextUpdate` never extends beyond
the snapshot's hard expiry.
the snapshot's Redis expiry.
- Gitea Actions publishes `gitea.woggioni.net/woggioni/kca-ocsp` on the same
`release/*` tags as the server image. The responder image is a statically
linked musl executable running `FROM scratch` with only the CA bundle.
Enable publishing in KCA with `KCA_OCSP_S3_BUCKET` and the other
`KCA_OCSP_*` variables in `server/.env.example`. Run the responder with a
configuration derived from `ocsp-responder/responder.example.yaml`, setting
each issuer's `ca_id` to its CA certificate UUID in KCA, or with Compose:
Configure `REDIS_URL` and the other `KCA_OCSP_*` variables in
`server/.env.example`. Run the responder with a configuration derived from
`ocsp-responder/responder.example.yaml`, setting each issuer's `ca_id` to its
CA certificate UUID in KCA and using the same Redis URL, or with Compose:
```sh
docker compose --profile ocsp up --build