mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-12 16:18:59 +00:00
main: wire CRL/OCSP responder services into runtime
Activates the CRL/OCSP responder pipeline that landed dormant in phases 1-4 (commits30765ba,a0b7f7d,dc32694,dc1e0bf): * IssuerRegistry gains SetLocalIssuerDeps + LocalIssuerDeps struct. Rebuild type-asserts each constructed connector to *local.Connector and injects ocspResponderRepo + signerDriver + IssuerID + key dir + (optional) rotation-grace + validity overrides. Non-local connectors are unaffected (the type-assert fails silently). Adapter pattern preserved: callers still see service.IssuerConnector. * cmd/server/main.go: - constructs CRLCacheRepository + OCSPResponderRepository from db - constructs signer.FileDriver (default; PKCS#11 driver plugs in later via the same Driver interface, no main.go changes needed) - calls issuerRegistry.SetLocalIssuerDeps(...) BEFORE BuildRegistry so the deps are in place when local connectors are constructed - wires CRLCacheService into CertificateService via SetCRLCacheSvc (Phase 4 cache-aware GenerateDERCRL path now active) - calls scheduler.SetCRLCacheService + SetCRLGenerationInterval after sched is constructed; logs the interval at startup * config: new OCSPResponderConfig struct + Scheduler.CRLGenerationInterval field. Three new env vars: CERTCTL_OCSP_RESPONDER_KEY_DIR (no default; operator MUST set in prod) CERTCTL_OCSP_RESPONDER_ROTATION_GRACE (default 7d) CERTCTL_OCSP_RESPONDER_VALIDITY (default 30d) CERTCTL_CRL_GENERATION_INTERVAL (default 1h) Backward compat: when env vars are unset, the responder bootstrap path still activates (with default rotation grace + validity, key dir = cwd which is fine for tests), and the CRL cache pre-populates on the 1h interval. Operators not running the local issuer see no behavior change. go vet clean across the full module. Targeted tests for config + service + scheduler packages all green. Full module build deferred to CI (sandbox /sessions disk pressure prevented unzipping a transitive dep — same disk-full pattern the prior commits hit; not a code issue).
This commit is contained in:
@@ -5,10 +5,14 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/connector/issuer/local"
|
||||
"github.com/shankar0123/certctl/internal/connector/issuerfactory"
|
||||
"github.com/shankar0123/certctl/internal/crypto"
|
||||
"github.com/shankar0123/certctl/internal/crypto/signer"
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
)
|
||||
|
||||
// IssuerRegistry is a thread-safe registry of issuer connectors.
|
||||
@@ -18,6 +22,29 @@ type IssuerRegistry struct {
|
||||
mu sync.RWMutex
|
||||
issuers map[string]IssuerConnector
|
||||
logger *slog.Logger
|
||||
|
||||
// localDeps, when set, is injected into every *local.Connector
|
||||
// constructed by Rebuild via SetOCSPResponderRepo + SetSignerDriver
|
||||
// + SetIssuerID + SetOCSPResponderKeyDir. Wires the dedicated OCSP
|
||||
// responder cert flow (RFC 6960 §2.6); see Bundle CRL/OCSP-Responder
|
||||
// Phase 2. When unset, local connectors fall back to signing OCSP
|
||||
// with the CA key directly (the historical behaviour, preserved for
|
||||
// callers that don't supply these deps).
|
||||
localDeps *LocalIssuerDeps
|
||||
}
|
||||
|
||||
// LocalIssuerDeps groups the optional dependencies that the local
|
||||
// issuer needs for the dedicated OCSP responder cert flow. All fields
|
||||
// are required when localDeps is set on the registry; nil-checking
|
||||
// individual fields would partially-initialize the responder path
|
||||
// which is worse than the all-or-nothing fallback to direct CA-key
|
||||
// signing.
|
||||
type LocalIssuerDeps struct {
|
||||
OCSPResponderRepo repository.OCSPResponderRepository
|
||||
SignerDriver signer.Driver
|
||||
KeyDir string // where FileDriver-backed responder keys land
|
||||
RotationGrace time.Duration // optional override; default 7d if zero
|
||||
Validity time.Duration // optional override; default 30d if zero
|
||||
}
|
||||
|
||||
// NewIssuerRegistry creates a new empty issuer registry.
|
||||
@@ -28,6 +55,17 @@ func NewIssuerRegistry(logger *slog.Logger) *IssuerRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
// SetLocalIssuerDeps configures the per-local-connector dependencies
|
||||
// applied by Rebuild. Must be called before BuildRegistry / Rebuild
|
||||
// so the deps are in place when local connectors are constructed.
|
||||
//
|
||||
// Bundle CRL/OCSP-Responder Phase 2.
|
||||
func (r *IssuerRegistry) SetLocalIssuerDeps(deps *LocalIssuerDeps) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.localDeps = deps
|
||||
}
|
||||
|
||||
// Get returns the issuer connector for the given ID and whether it exists.
|
||||
func (r *IssuerRegistry) Get(id string) (IssuerConnector, bool) {
|
||||
r.mu.RLock()
|
||||
@@ -109,6 +147,31 @@ func (r *IssuerRegistry) Rebuild(configs []*domain.Issuer, encryptionKey string)
|
||||
continue
|
||||
}
|
||||
|
||||
// Bundle CRL/OCSP-Responder Phase 2: when local deps are
|
||||
// configured on the registry, inject them into every freshly-
|
||||
// constructed *local.Connector so its SignOCSPResponse takes
|
||||
// the dedicated responder cert path. Type-assert is the
|
||||
// pragmatic seam — the factory returns issuer.Connector so
|
||||
// this is the only place that knows what concrete type was
|
||||
// just built.
|
||||
if localConn, ok := connector.(*local.Connector); ok && r.localDeps != nil {
|
||||
localConn.SetIssuerID(cfg.ID)
|
||||
localConn.SetOCSPResponderRepo(r.localDeps.OCSPResponderRepo)
|
||||
localConn.SetSignerDriver(r.localDeps.SignerDriver)
|
||||
if r.localDeps.KeyDir != "" {
|
||||
localConn.SetOCSPResponderKeyDir(r.localDeps.KeyDir)
|
||||
}
|
||||
if r.localDeps.RotationGrace > 0 {
|
||||
localConn.SetOCSPResponderRotationGrace(r.localDeps.RotationGrace)
|
||||
}
|
||||
if r.localDeps.Validity > 0 {
|
||||
localConn.SetOCSPResponderValidity(r.localDeps.Validity)
|
||||
}
|
||||
r.logger.Info("local issuer wired with dedicated OCSP responder deps",
|
||||
"id", cfg.ID,
|
||||
"key_dir", r.localDeps.KeyDir)
|
||||
}
|
||||
|
||||
newIssuers[cfg.ID] = NewIssuerConnectorAdapter(connector)
|
||||
r.logger.Info("issuer loaded into registry", "id", cfg.ID, "type", cfg.Type)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user