mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 14:11:31 +00:00
crl/cache: schema + repository for crl_cache + crl_generation_events
Phase 1 of the CRL/OCSP responder bundle. Adds:
* migration 000019 — crl_cache (one row per issuer; pre-generated CRL DER,
monotonic crl_number per RFC 5280 §5.2.3, this_update/next_update,
generation duration metric, revoked_count) + crl_generation_events
(append-only audit log of every regeneration attempt, succeeded
+ error fields for ops grep)
* internal/domain/crl_cache.go — CRLCacheEntry + IsStale helper +
CRLGenerationEvent (raw DER omitted from JSON to avoid bloating
admin responses; CRLDERBase64 field for explicit transit shaping)
* internal/repository/interfaces.go — CRLCacheRepository interface
(Get / Put / NextCRLNumber / RecordGenerationEvent /
ListGenerationEvents)
* internal/repository/postgres/crl_cache.go — Postgres impl with
SERIALIZABLE-isolated NextCRLNumber to defeat the monotonicity
race between concurrent generations of the same issuer
* internal/repository/postgres/crl_cache_test.go — testcontainers
suite (round-trip, overwrite, monotonicity, event recording,
failure-event-with-error)
No behavior change at the HTTP layer yet — Phase 3 wires the cache into
GetDERCRL via a new CRLCacheService + crlGenerationLoop.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
-- 000019_crl_cache.up.sql
|
||||
--
|
||||
-- CRL cache + generation event log for the scheduler-driven CRL
|
||||
-- pre-generation work (CRL/OCSP responder bundle).
|
||||
--
|
||||
-- Before this migration the CRL endpoint at /.well-known/pki/crl/{issuer_id}
|
||||
-- regenerated the entire CRL on every HTTP request — every relying party
|
||||
-- fetch hit the certificate_revocations table, built the entry list,
|
||||
-- signed the CRL, and discarded the result. For a busy CA with many
|
||||
-- relying parties this DOSes itself.
|
||||
--
|
||||
-- After this migration the scheduler's crlGenerationLoop pre-generates
|
||||
-- CRLs at a configurable interval (default 1h, env var
|
||||
-- CERTCTL_CRL_GENERATION_INTERVAL) and the HTTP handler reads from
|
||||
-- crl_cache. On cache miss / staleness the cache service triggers an
|
||||
-- immediate generation via singleflight (to coalesce concurrent miss
|
||||
-- requests for the same issuer into a single generation).
|
||||
--
|
||||
-- Idempotent: every CREATE uses IF NOT EXISTS so re-running the
|
||||
-- migration is safe (matches the project's migration convention).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS crl_cache (
|
||||
issuer_id TEXT PRIMARY KEY REFERENCES issuers(id) ON DELETE CASCADE,
|
||||
crl_der BYTEA NOT NULL,
|
||||
crl_number BIGINT NOT NULL, -- monotonic per RFC 5280 §5.2.3
|
||||
this_update TIMESTAMPTZ NOT NULL,
|
||||
next_update TIMESTAMPTZ NOT NULL,
|
||||
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
generation_duration_ms INTEGER NOT NULL,
|
||||
revoked_count INTEGER NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Lets the scheduler quickly find issuers whose cache is stale (next_update
|
||||
-- already in the past). The query "find issuers needing regeneration" runs
|
||||
-- at every tick of crlGenerationLoop.
|
||||
CREATE INDEX IF NOT EXISTS idx_crl_cache_next_update ON crl_cache(next_update);
|
||||
|
||||
-- Track every (re)generation event for ops visibility. Failed generations
|
||||
-- (succeeded=false) leave a breadcrumb operators can grep when
|
||||
-- troubleshooting "why isn't the CRL fresh." The id is bigserial so the
|
||||
-- table is naturally ordered by insertion; the (issuer_id, started_at)
|
||||
-- index serves the GUI's "recent generations for this issuer" query.
|
||||
CREATE TABLE IF NOT EXISTS crl_generation_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
issuer_id TEXT NOT NULL,
|
||||
crl_number BIGINT NOT NULL,
|
||||
duration_ms INTEGER NOT NULL,
|
||||
revoked_count INTEGER NOT NULL,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
succeeded BOOLEAN NOT NULL,
|
||||
error TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_crl_generation_events_issuer_started
|
||||
ON crl_generation_events(issuer_id, started_at DESC);
|
||||
Reference in New Issue
Block a user