mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:51:30 +00:00
F-001/F-002/F-003: CRL prefix-scan, digest error sanitization, ctx-aware sleeps
F-001 (P3): GenerateDERCRL scoped to issuer via composite index
- Add RevocationRepository.ListByIssuer leveraging migration 000012's
idx_certificate_revocations_issuer_serial composite index as a
prefix-scan target. Previously CAOperationsSvc.GenerateDERCRL called
ListAll() and filtered by IssuerID in Go — O(total revocations)
regardless of how many revocations belonged to the target issuer.
- Rewrite GenerateDERCRL to call ListByIssuer(ctx, issuerID) so PostgreSQL
drives a prefix scan of the composite index. Drops the in-memory filter.
- New regression test in ca_operations_test.go asserts the CRL hot path
invokes ListByIssuer exactly once and never ListAll, and that the
issuerID is threaded through correctly.
F-002 (P3): digest.go admin-auth endpoints no longer leak internal errors
- PreviewDigest (GET /api/v1/digest/preview) and SendDigest
(POST /api/v1/digest/send) previously wrote err.Error() into the HTTP
response body on 500s. Replace with slog.Error server-side logging plus
a generic "internal error" response body, matching the house pattern
in certificates.go and export.go.
F-003 (P4): three blocking time.Sleep sites now honor ctx cancellation
- internal/connector/issuer/acme/acme.go:672 (DNS-01 propagation wait)
now runs under a select{case <-ctx.Done(): CleanUp + return ctx.Err();
case <-time.After(d):} so graceful shutdown doesn't get stuck behind
the propagation delay.
- internal/connector/issuer/acme/acme.go:786 (dns-persist-01 propagation
wait) same pattern, returns ctx.Err() on cancel.
- cmd/agent/main.go:272 (polling backoff inside the heartbeat loop) now
wraps the sleep in select{case <-ctx.Done(): continue; case <-time.After(backoff):}
so the outer <-ctx.Done() case on the parent loop fires cleanly.
Verification: build, vet, and race-enabled short tests green across all
55+ packages. govulncheck reports zero vulnerabilities in the code path.
No migration needed — F-001 reuses the existing 000012 composite index.
No frontend changes.
This commit is contained in:
@@ -47,8 +47,15 @@ type RevocationRepository interface {
|
||||
// protocol endpoints carry it in the request path; RFC 5280 §5.2.3 guarantees
|
||||
// uniqueness only within a single issuer.
|
||||
GetByIssuerAndSerial(ctx context.Context, issuerID, serial string) (*domain.CertificateRevocation, error)
|
||||
// ListAll returns all revocations, ordered by revocation time (for CRL generation).
|
||||
// ListAll returns all revocations, ordered by revocation time (for global
|
||||
// revocation admin views). CRL generation should prefer ListByIssuer to
|
||||
// avoid loading and discarding rows that belong to other issuers.
|
||||
ListAll(ctx context.Context) ([]*domain.CertificateRevocation, error)
|
||||
// ListByIssuer returns revocations for a single issuer, ordered by revocation
|
||||
// time (for CRL generation). Pushing the issuer filter into the query lets
|
||||
// the migration 000012 composite index (issuer_id, serial_number) drive a
|
||||
// prefix scan instead of a full table read + in-memory filter.
|
||||
ListByIssuer(ctx context.Context, issuerID string) ([]*domain.CertificateRevocation, error)
|
||||
// ListByCertificate returns all revocations for a certificate.
|
||||
ListByCertificate(ctx context.Context, certID string) ([]*domain.CertificateRevocation, error)
|
||||
// MarkIssuerNotified updates the issuer_notified flag for a revocation.
|
||||
|
||||
@@ -81,6 +81,29 @@ func (r *RevocationRepository) ListAll(ctx context.Context) ([]*domain.Certifica
|
||||
return scanRevocations(rows)
|
||||
}
|
||||
|
||||
// ListByIssuer returns all revocations for a single issuer, ordered by revocation time.
|
||||
//
|
||||
// This is the hot path for CRL generation. Pushing the issuer filter into the
|
||||
// SQL query lets the composite index `idx_certificate_revocations_issuer_serial`
|
||||
// (migration 000012) drive a prefix scan on issuer_id rather than forcing
|
||||
// callers to load every row in the table and discard the ones belonging to
|
||||
// other issuers.
|
||||
func (r *RevocationRepository) ListByIssuer(ctx context.Context, issuerID string) ([]*domain.CertificateRevocation, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT id, certificate_id, serial_number, reason, revoked_by, revoked_at,
|
||||
issuer_id, issuer_notified, created_at
|
||||
FROM certificate_revocations
|
||||
WHERE issuer_id = $1
|
||||
ORDER BY revoked_at ASC
|
||||
`, issuerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list revocations by issuer: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanRevocations(rows)
|
||||
}
|
||||
|
||||
// ListByCertificate returns all revocations for a certificate.
|
||||
func (r *RevocationRepository) ListByCertificate(ctx context.Context, certID string) ([]*domain.CertificateRevocation, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
|
||||
Reference in New Issue
Block a user