mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:11:29 +00:00
Merge fix/ci-bundle-B-tail: G-3 env-var docs + M-028 closure
This commit is contained in:
+8
-12
@@ -44,9 +44,8 @@ func TestMain_HealthEndpointBypassesAuth(t *testing.T) {
|
||||
})
|
||||
|
||||
// Build the handler chain the same way main.go does
|
||||
authMiddleware := middleware.NewAuth(middleware.AuthConfig{
|
||||
Type: "api-key",
|
||||
Secret: "test-secret-key",
|
||||
authMiddleware := middleware.NewAuthWithNamedKeys([]middleware.NamedAPIKey{
|
||||
{Name: "test", Key: "test-secret-key"},
|
||||
})
|
||||
|
||||
// API handler with auth
|
||||
@@ -160,9 +159,8 @@ func TestMain_AuthMiddlewareRejectsUnauthorized(t *testing.T) {
|
||||
})
|
||||
|
||||
// Wrap with auth middleware
|
||||
authMiddleware := middleware.NewAuth(middleware.AuthConfig{
|
||||
Type: "api-key",
|
||||
Secret: "test-secret-key",
|
||||
authMiddleware := middleware.NewAuthWithNamedKeys([]middleware.NamedAPIKey{
|
||||
{Name: "test", Key: "test-secret-key"},
|
||||
})
|
||||
|
||||
chainedHandler := middleware.Chain(protectedHandler, authMiddleware)
|
||||
@@ -189,9 +187,8 @@ func TestMain_AuthMiddlewareAllowsWithValidKey(t *testing.T) {
|
||||
})
|
||||
|
||||
// Wrap with auth middleware
|
||||
authMiddleware := middleware.NewAuth(middleware.AuthConfig{
|
||||
Type: "api-key",
|
||||
Secret: testKey,
|
||||
authMiddleware := middleware.NewAuthWithNamedKeys([]middleware.NamedAPIKey{
|
||||
{Name: "test", Key: testKey},
|
||||
})
|
||||
|
||||
chainedHandler := middleware.Chain(protectedHandler, authMiddleware)
|
||||
@@ -462,9 +459,8 @@ func TestMain_AuthNoneMode(t *testing.T) {
|
||||
})
|
||||
|
||||
// Wrap with auth middleware in "none" mode
|
||||
authMiddleware := middleware.NewAuth(middleware.AuthConfig{
|
||||
Type: "none",
|
||||
})
|
||||
// auth=none equivalent: empty named-keys list is a no-op pass-through.
|
||||
authMiddleware := middleware.NewAuthWithNamedKeys(nil)
|
||||
|
||||
chainedHandler := middleware.Chain(protectedHandler, authMiddleware)
|
||||
|
||||
|
||||
+11
-2
@@ -60,11 +60,20 @@ Two endpoints are served without auth so the GUI can detect auth mode before log
|
||||
|
||||
Token bucket algorithm protecting the control plane from misbehaving clients.
|
||||
|
||||
Bundle B (Audit M-025 / OWASP ASVS L2 §11.2.1): per-key keying. Each
|
||||
authenticated caller gets a bucket keyed on their API-key name; each
|
||||
unauthenticated source IP gets its own bucket. Bucket creation is
|
||||
on-demand under a `sync.RWMutex`; no eviction (the leak is bounded by
|
||||
realistic operator IP fan-out — appropriate for the OWASP ASVS L2 threat
|
||||
model of abuse-by-known-clients, not infinite-cardinality scanners).
|
||||
|
||||
| Env Var | Default | Description |
|
||||
|---|---|---|
|
||||
| `CERTCTL_RATE_LIMIT_ENABLED` | `true` | Enable/disable |
|
||||
| `CERTCTL_RATE_LIMIT_RPS` | `50` | Requests per second |
|
||||
| `CERTCTL_RATE_LIMIT_BURST` | `100` | Burst capacity |
|
||||
| `CERTCTL_RATE_LIMIT_RPS` | `50` | Per-key requests per second (default applies to IP-keyed buckets; user-keyed buckets fall back to this when `PER_USER_RPS` is unset) |
|
||||
| `CERTCTL_RATE_LIMIT_BURST` | `100` | Per-key burst capacity (default applies to IP-keyed buckets; user-keyed buckets fall back to this when `PER_USER_BURST` is unset) |
|
||||
| `CERTCTL_RATE_LIMIT_PER_USER_RPS` | `0` | Override RPS for authenticated callers. `0` means "use `RATE_LIMIT_RPS`". Set higher than `RATE_LIMIT_RPS` to grant authenticated clients a more generous budget than anonymous probes. |
|
||||
| `CERTCTL_RATE_LIMIT_PER_USER_BURST` | `0` | Override burst for authenticated callers. `0` means "use `RATE_LIMIT_BURST`". |
|
||||
|
||||
Exceeded requests receive `429 Too Many Requests` with a `Retry-After` header.
|
||||
|
||||
|
||||
@@ -263,6 +263,18 @@ func extractCSRFields(csrDER []byte) ([]byte, string, string, error) {
|
||||
// Attributes is []pkix.AttributeTypeAndValueSET where each has Type (OID)
|
||||
// and Value ([][]pkix.AttributeTypeAndValue). The challenge password value
|
||||
// is stored as a string in the inner AttributeTypeAndValue.Value field.
|
||||
//
|
||||
// Audit M-028 carve-out: Go's stdlib deprecates `csr.Attributes` for the
|
||||
// specific use case of parsing the "requestedExtensions" CSR attribute
|
||||
// (OID 1.2.840.113549.1.9.14), pointing callers at `csr.Extensions` /
|
||||
// `csr.ExtraExtensions`. challengePassword (OID 1.2.840.113549.1.9.7)
|
||||
// per RFC 2985 §5.4.1 is a SEPARATE CSR attribute that cannot be
|
||||
// retrieved via Extensions. There is no non-deprecated stdlib API for
|
||||
// it; callers either accept the deprecation warning or parse the raw
|
||||
// `csr.RawAttributes` ASN.1 themselves. We accept the warning; the
|
||||
// staticcheck.conf and golangci-lint rules suppress SA1019 for this
|
||||
// specific line per the audit closure note.
|
||||
//lint:ignore SA1019 RFC 2985 challengePassword has no non-deprecated stdlib API; see comment above.
|
||||
for _, attr := range csr.Attributes {
|
||||
if attr.Type.Equal(oidChallengePassword) {
|
||||
if len(attr.Value) > 0 && len(attr.Value[0]) > 0 {
|
||||
|
||||
@@ -334,11 +334,12 @@ func TestHashPublicKey_ECDSA_RoundTripPin(t *testing.T) {
|
||||
t.Fatalf("ecdsaToECDH: %v", err)
|
||||
}
|
||||
ecdhBytes := ecdhPub.Bytes()
|
||||
//nolint:staticcheck // SA1019: pin assertion — we DELIBERATELY use
|
||||
// the deprecated API here as a regression oracle to prove the
|
||||
// new crypto/ecdh path produces byte-identical output. If
|
||||
// elliptic.Marshal is removed in a future Go release this test
|
||||
// must be deleted (and the migration is then irreversibly proven).
|
||||
// Pin assertion — we DELIBERATELY use the deprecated API here
|
||||
// as a regression oracle to prove the new crypto/ecdh path
|
||||
// produces byte-identical output. If elliptic.Marshal is
|
||||
// removed in a future Go release this test must be deleted
|
||||
// (and the migration is then irreversibly proven).
|
||||
//lint:ignore SA1019 deliberate regression oracle for M-028 round-trip pin
|
||||
legacy := elliptic.Marshal(k.Curve, k.X, k.Y)
|
||||
if !bytes.Equal(ecdhBytes, legacy) {
|
||||
t.Fatalf("ECDH .Bytes() != legacy elliptic.Marshal output\n new: %x\n old: %x", ecdhBytes, legacy)
|
||||
|
||||
Reference in New Issue
Block a user