mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 18:51:32 +00:00
9cce2ab043
Audit 2026-05-10 — close 8 LOWs + 2 Nits in-bundle. Remainder
(LOW-1/6/9/11/12, Nit-2/5) need GUI or DB-test runtime not present
in-session; tracked in the audit-doc batch table.
LOW-2: bootstrap.ValidateAndMint now emits 'bootstrap.consume_failed'
audit rows on persist-key + grant-role failure branches before
bubbling. Recovery requires DB seeding per the docstring; without this
row, later forensics can't tell 'bootstrap was used and failed' from
'never invoked.'
LOW-3: randomB64URLForHandler now uses crypto/rand (was time-nano-
shifted). Two providers/mappings created in the same nanosecond used
to collide; now they don't. Time-nano fallback retained for the
unlikely crypto/rand-broken path.
LOW-4: breakglass.verifyDummy uses s.readRand(salt) for the dummy
Argon2id verify. Wall-clock cost unchanged (Argon2id memory alloc
dominates), but cache/branch behavior now matches a real verify —
closes the subtle timing side channel.
LOW-5: clientIPFromRequest now only honors X-Forwarded-For when the
direct connection's RemoteAddr falls in the CERTCTL_TRUSTED_PROXIES
CIDR allowlist. Default-deny: empty list means XFF is ignored.
SetTrustedProxies wired in cmd/server/main.go from cfg.Auth.TrustedProxies.
LOW-7: internal/auth/protocol_endpoints.go::ProtocolEndpointPrefixes
now carries /scep-mtls + /.well-known/est-mtls (previously only in
router.AuthExemptDispatchPrefixes; the two lists had drifted). The
canonical-prefix coverage test in Phase 12 still pins the set.
LOW-8: docs/operator/rbac.md documents that r-mcp / r-cli / r-agent
are not actor-type-bound — role naming is a hint, not an enforcement.
Operators wanting hard binding must apply periodic audit queries.
Native binding is on the v2 roadmap.
LOW-10: Session.Validate now rejects a post-login row with empty
CSRFTokenHash (IsPreLogin=false branch). validSession test fixture
updated with a valid 64-hex CSRF hash.
Nit-1: production RevokeAllForActor call sites already use typed
constants (only test-file literals remain — acceptable).
Nit-3: peekIssuer docstring documents the unsigned-permissive-by-design
invariant + the post-verify re-check pin that the BCL handler enforces.
A future commit that uses peekIssuer output before verify will trip
the inline comment + the existing BCL test matrix.
Status table updated in cowork/auth-bundles-audit-2026-05-10.md:
8 LOWs + 2 Nits CLOSED; 5 LOWs + 2 Nits OPEN with explicit reason
(GUI work, repo refactor, Keycloak integration runtime, WONTFIX).
Refs: cowork/auth-bundles-audit-2026-05-10.md LOW-2/3/4/5/7/8/10
cowork/auth-bundles-audit-2026-05-10.md Nit-1/3
61 lines
2.6 KiB
Go
61 lines
2.6 KiB
Go
package auth
|
|
|
|
import "strings"
|
|
|
|
// ProtocolEndpointPrefixes lists the URL path prefixes that authenticate
|
|
// via the protocol itself rather than via certctl's Bearer / cookie
|
|
// stack. Bundle 1 Phase 3 uses this allowlist as the explicit "do NOT
|
|
// wrap with RequirePermission" set: the RBAC middleware applies only to
|
|
// admin handlers replacing legacy IsAdmin checks plus any new
|
|
// permission-gated routes; the endpoints below keep their existing
|
|
// protocol-level auth.
|
|
//
|
|
// Adding a new protocol endpoint that doesn't take a Bearer token MUST
|
|
// also add the prefix here and a parallel test in Phase 12 asserting
|
|
// the route is unwrapped.
|
|
//
|
|
// Per the Phase 3 audit:
|
|
//
|
|
// ACME server : /acme/profile/<id>/* + /acme/* (JWS-signed, RFC 8555).
|
|
// SCEP server : /scep (challenge password +
|
|
// signed CSR, RFC 8894).
|
|
// EST server : /.well-known/est/* (mTLS client cert,
|
|
// RFC 7030).
|
|
// OCSP responder : /.well-known/pki/ocsp (RFC 6960, public).
|
|
// CRL distrib. : /.well-known/pki/crl/* (RFC 5280, public).
|
|
//
|
|
// Plus the existing public-route bypass list at internal/api/router
|
|
// (router.go:69-72): /health, /ready, /api/v1/auth/info. Those bypass
|
|
// EVERY middleware stack, not just RBAC, so they're not in this
|
|
// allowlist; they're handled in router.go directly.
|
|
// Audit 2026-05-10 LOW-7 closure — this slice is the canonical
|
|
// source of truth for "do NOT gate via RBAC" surfaces. The router's
|
|
// AuthExemptDispatchPrefixes had drifted (carrying /scep-mtls and
|
|
// /.well-known/est-mtls that weren't in this list); both are now
|
|
// included so the two slices stay in lockstep. A CI guard
|
|
// (scripts/ci-guards/protocol-endpoint-prefix-sync.sh) is queued
|
|
// against the two slices for future drift detection — meanwhile the
|
|
// Phase 12 TestPhase12_IsProtocolEndpoint_CoversCanonicalPrefixes
|
|
// regression pins the canonical set against this var.
|
|
var ProtocolEndpointPrefixes = []string{
|
|
"/acme",
|
|
"/scep",
|
|
"/scep-mtls", // SCEP + mTLS sibling route (Phase 6.5)
|
|
"/.well-known/est",
|
|
"/.well-known/est-mtls", // EST + mTLS sibling route (EST hardening Phase 2)
|
|
"/.well-known/pki/ocsp",
|
|
"/.well-known/pki/crl",
|
|
}
|
|
|
|
// IsProtocolEndpoint reports whether the request path is in the
|
|
// "do not gate" allowlist. Phase 3 RequirePermission check bails out
|
|
// early for these paths so the protocol surface is preserved.
|
|
func IsProtocolEndpoint(path string) bool {
|
|
for _, p := range ProtocolEndpointPrefixes {
|
|
if path == p || strings.HasPrefix(path, p+"/") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|