mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-09 14:09:01 +00:00
feat(ratelimit): per-endpoint rate limit on OCSP + cert-export (Phase 3)
Production hardening II Phase 3 — wire the existing
internal/ratelimit/SlidingWindowLimiter into the OCSP and cert-export
handlers. Removes the DoS vector where an unauthenticated relying
party (or compromised admin token) can hammer the responder /
key-export endpoint at unbounded rates.
OCSP: per-source-IP cap. Default 1000 req/min/IP, 50k tracked IPs
(matches the SCEP/Intune replay cache cap). Configurable via
CERTCTL_OCSP_RATE_LIMIT_PER_IP_MIN; zero disables. Source IP comes
from net.SplitHostPort(r.RemoteAddr) — we deliberately do NOT honor
X-Forwarded-For because OCSP is publicly reachable and untrusted
intermediaries could spoof the header to bypass the limit.
On rate-limit trip: respond with the canonical
ocsp.UnauthorizedErrorResponse pre-built blob from x/crypto/ocsp
(status 6 per RFC 6960 §2.3) plus Retry-After: 60. Using the
unauthorized status (instead of TryLater) avoids hand-rolling DER
for a single rejection path; relying parties retry on any non-good
status anyway.
Cert-export: per-actor cap. Default 50 exports/hr/operator.
Configurable via CERTCTL_CERT_EXPORT_RATE_LIMIT_PER_ACTOR_HR; zero
disables. Actor extracted from the X-Actor request header (set by
the auth middleware); falls back to RemoteAddr if empty (defensive).
On rate-limit trip: HTTP 429 + JSON body
{"error":"rate_limit_exceeded","retry_after_seconds":3600} +
Retry-After: 3600.
NEW config fields in internal/config/config.go::SchedulerConfig:
OCSPRateLimitPerIPMin (default 1000)
CertExportRateLimitPerActorHr (default 50)
WIRED in cmd/server/main.go: ocspLimiter constructed with the
configured cap, 1m window, 50k map cap; exportLimiter same shape with
1h window. Both wired via SetOCSPRateLimiter / SetExportRateLimiter
on their respective handlers. Existing deploys see no behavior
change unless the env vars are set to non-default values + traffic
exceeds the cap.
Pre-commit verification: go build ./... clean; go test -short
-count=1 green for handler + service + config.
This commit is contained in:
@@ -502,6 +502,11 @@ func main() {
|
||||
|
||||
// Initialize API handlers
|
||||
certificateHandler := handler.NewCertificateHandler(certificateService)
|
||||
// Production hardening II Phase 3: per-source-IP OCSP rate limit.
|
||||
// Window 1m so the cap counts requests per minute. Map cap 50k
|
||||
// matches the SCEP/Intune replay cache cap. Zero disables.
|
||||
ocspLimiter := ratelimit.NewSlidingWindowLimiter(cfg.Scheduler.OCSPRateLimitPerIPMin, time.Minute, 50_000)
|
||||
certificateHandler.SetOCSPRateLimiter(ocspLimiter)
|
||||
issuerHandler := handler.NewIssuerHandler(issuerService)
|
||||
targetHandler := handler.NewTargetHandler(targetService)
|
||||
agentHandler := handler.NewAgentHandler(agentService, cfg.Auth.AgentBootstrapToken)
|
||||
@@ -535,6 +540,10 @@ func main() {
|
||||
verificationHandler := handler.NewVerificationHandler(verificationService)
|
||||
exportService := service.NewExportService(certificateRepo, auditService)
|
||||
exportHandler := handler.NewExportHandler(exportService)
|
||||
// Production hardening II Phase 3: per-actor cert-export rate limit.
|
||||
// Window 1h so the cap counts exports per hour. Zero disables.
|
||||
exportLimiter := ratelimit.NewSlidingWindowLimiter(cfg.Scheduler.CertExportRateLimitPerActorHr, time.Hour, 50_000)
|
||||
exportHandler.SetExportRateLimiter(exportLimiter)
|
||||
|
||||
bulkRevocationHandler := handler.NewBulkRevocationHandler(bulkRevocationService)
|
||||
// L-1 master closure: handlers for the new bulk-renew + bulk-reassign
|
||||
|
||||
Reference in New Issue
Block a user