Files
kca/ocsp-responder

KCA OCSP responder

Standalone Rust/Axum responder. It reads one full JSON status snapshot per CA from S3-compatible object storage, caches it for one second with a single-flight refresh, and signs OCSP responses with a delegated responder certificate.

It has no KCA database access, no Redis access, and no KCA master key. It only needs:

  • s3:GetObject on the configured bucket/prefix;
  • the CA certificate for each issuer;
  • a delegated OCSP signer certificate and private key for each CA. The responder currently accepts RSA-SHA256 and ECDSA P-256 signers in PKCS#8, PKCS#1 RSA, or SEC1 EC PEM form.

Snapshot layout

KCA publishes objects as:

<prefix>/<CA certificate SHA-256>.json

KCA publishes these objects when KCA_OCSP_S3_BUCKET is configured. The JSON contains all certificates issued by that CA, both valid and revoked. The responder never infers status from a partial revoked-only list.

Run locally

  1. Create a config based on responder.example.yaml.
  2. Put your CA certificates and delegated signer files where the config points.
  3. Export standard AWS credentials / endpoint variables as needed.
  4. Make the signer key readable by the container user (ocsp, uid/gid 100:101 in the provided image), or mount it via a Docker secret.
  5. Run:
OCSP_CONFIG=./responder.yaml cargo run

The default listen address is 0.0.0.0:2560.

Verify with OpenSSL

For a CA certificate ca.crt and leaf certificate leaf.crt:

openssl ocsp \
  -issuer ca.crt \
  -cert leaf.crt \
  -url http://127.0.0.1:2560/ocsp \
  -resp_text \
  -noverify

-noverify skips local trust setup; the response still shows the signature algorithm and included responder certificate. For production verification, build a trust store containing the CA or validate with your normal PKI tooling.

Endpoints

  • POST /ocsp — application/ocsp-request
  • GET /ocsp/<base64url-request> — optional GET form
  • GET /healthz
  • GET /readyz

Cache and availability

  • Per-CA cache TTL: s3.cache_ttl_ms (default 1000 ms).
  • Concurrent requests for the same CA share one S3 GetObject.
  • If S3 fails, a previously cached snapshot is served for s3.stale_grace_seconds; otherwise the OCSP response is tryLater.
  • With multiple CAs, the TTL is per CA. If you need a global one-request-per- second limit across all CAs, that requires a shared rate limiter.