From 0188ef2e62e772be30ef01344e6103ecf7a57db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oleh=20Konko=20=7C=20trust=20infra=20security=20audit=20?= =?UTF-8?q?=26=20contribution=20=7C=20deterministic=20ai-augmented=20pipel?= =?UTF-8?q?ine=20=C2=B7=20human-verified?= Date: Wed, 11 Feb 2026 18:54:51 +0000 Subject: [PATCH] acmeserver: warn when policy rules unset (#7469) --- modules/caddypki/acmeserver/acmeserver.go | 17 ++++ .../caddypki/acmeserver/acmeserver_test.go | 94 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 modules/caddypki/acmeserver/acmeserver_test.go diff --git a/modules/caddypki/acmeserver/acmeserver.go b/modules/caddypki/acmeserver/acmeserver.go index 1a41671f2..4d158ed9f 100644 --- a/modules/caddypki/acmeserver/acmeserver.go +++ b/modules/caddypki/acmeserver/acmeserver.go @@ -140,6 +140,8 @@ func (ash *Handler) Provision(ctx caddy.Context) error { } } + ash.warnIfPolicyAllowsAll() + // get a reference to the configured CA appModule, err := ctx.App("pki") if err != nil { @@ -214,6 +216,21 @@ func (ash *Handler) Provision(ctx caddy.Context) error { return nil } +func (ash *Handler) warnIfPolicyAllowsAll() { + allow := ash.Policy.normalizeAllowRules() + deny := ash.Policy.normalizeDenyRules() + if allow != nil || deny != nil { + return + } + + allowWildcardNames := ash.Policy != nil && ash.Policy.AllowWildcardNames + ash.logger.Warn( + "acme_server policy has no allow/deny rules; order identifiers are unrestricted (allow-all)", + zap.String("ca", ash.CA), + zap.Bool("allow_wildcard_names", allowWildcardNames), + ) +} + func (ash Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { if strings.HasPrefix(r.URL.Path, ash.PathPrefix) { acmeCtx := acme.NewContext( diff --git a/modules/caddypki/acmeserver/acmeserver_test.go b/modules/caddypki/acmeserver/acmeserver_test.go new file mode 100644 index 000000000..ca54012eb --- /dev/null +++ b/modules/caddypki/acmeserver/acmeserver_test.go @@ -0,0 +1,94 @@ +package acmeserver + +import ( + "strings" + "testing" + + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +func TestHandler_warnIfPolicyAllowsAll(t *testing.T) { + tests := []struct { + name string + policy *Policy + wantWarns int + wantAllowWildcard bool + }{ + { + name: "warns when policy is nil", + policy: nil, + wantWarns: 1, + wantAllowWildcard: false, + }, + { + name: "warns when allow/deny rules are empty", + policy: &Policy{}, + wantWarns: 1, + wantAllowWildcard: false, + }, + { + name: "warns when only allow_wildcard_names is true", + policy: &Policy{ + AllowWildcardNames: true, + }, + wantWarns: 1, + wantAllowWildcard: true, + }, + { + name: "does not warn when allow rules are configured", + policy: &Policy{ + Allow: &RuleSet{ + Domains: []string{"example.com"}, + }, + }, + wantWarns: 0, + wantAllowWildcard: false, + }, + { + name: "does not warn when deny rules are configured", + policy: &Policy{ + Deny: &RuleSet{ + Domains: []string{"bad.example.com"}, + }, + }, + wantWarns: 0, + wantAllowWildcard: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + core, logs := observer.New(zap.WarnLevel) + ash := &Handler{ + CA: "local", + Policy: tt.policy, + logger: zap.New(core), + } + + ash.warnIfPolicyAllowsAll() + if logs.Len() != tt.wantWarns { + t.Fatalf("expected %d warning logs, got %d", tt.wantWarns, logs.Len()) + } + + if tt.wantWarns == 0 { + return + } + + entry := logs.All()[0] + if entry.Level != zap.WarnLevel { + t.Fatalf("expected warn level, got %v", entry.Level) + } + if !strings.Contains(entry.Message, "policy has no allow/deny rules") { + t.Fatalf("unexpected log message: %q", entry.Message) + } + ctx := entry.ContextMap() + if ctx["ca"] != "local" { + t.Fatalf("expected ca=local, got %v", ctx["ca"]) + } + if ctx["allow_wildcard_names"] != tt.wantAllowWildcard { + t.Fatalf("expected allow_wildcard_names=%v, got %v", tt.wantAllowWildcard, ctx["allow_wildcard_names"]) + } + }) + } +}