From 5d189aff40dcfd6e8245310eb696e0de3775a690 Mon Sep 17 00:00:00 2001 From: Tao Date: Sat, 21 Mar 2026 01:36:03 +1000 Subject: [PATCH] caddytls: Avoid default issuers for implicit tailscale policies (#7577) --- modules/caddytls/automation.go | 25 ++++++++++++++++++- modules/caddytls/automation_test.go | 37 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 modules/caddytls/automation_test.go diff --git a/modules/caddytls/automation.go b/modules/caddytls/automation.go index 74125a222..5b7a4ed5d 100644 --- a/modules/caddytls/automation.go +++ b/modules/caddytls/automation.go @@ -235,7 +235,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error { } issuers := ap.Issuers - if len(issuers) == 0 { + if len(issuers) == 0 && !ap.implicitTailscaleManagersOnly() { var err error issuers, err = DefaultIssuersProvisioned(tlsApp.ctx) if err != nil { @@ -429,6 +429,29 @@ func (ap *AutomationPolicy) AllInternalSubjects() bool { }) } +// implicitTailscaleManagersOnly returns true if this policy is configured to +// serve only Tailscale names from the Tailscale manager at handshake-time. +func (ap *AutomationPolicy) implicitTailscaleManagersOnly() bool { + if len(ap.subjects) == 0 { + return false + } + + for _, subject := range ap.subjects { + if !strings.HasSuffix(strings.ToLower(subject), tailscaleDomainAliasEnding) { + return false + } + } + + for _, manager := range ap.Managers { + switch manager.(type) { + case Tailscale, *Tailscale: + return true + } + } + + return false +} + func (ap *AutomationPolicy) onlyInternalIssuer() bool { if len(ap.Issuers) != 1 { return false diff --git a/modules/caddytls/automation_test.go b/modules/caddytls/automation_test.go new file mode 100644 index 000000000..d991b9cf6 --- /dev/null +++ b/modules/caddytls/automation_test.go @@ -0,0 +1,37 @@ +package caddytls + +import ( + "testing" + + "github.com/caddyserver/certmagic" + "go.uber.org/zap" +) + +func TestAutomationPolicyMakeCertMagicConfigImplicitTailscaleManagersOnly(t *testing.T) { + ap := AutomationPolicy{ + Managers: []certmagic.Manager{Tailscale{}}, + subjects: []string{"test-node.example.ts.net"}, + } + + cfg, err := ap.makeCertMagicConfig(&TLS{ + logger: zap.NewNop(), + }, nil, &certmagic.FileStorage{Path: t.TempDir()}) + if err != nil { + t.Fatalf("making certmagic config: %v", err) + } + if cfg.OnDemand == nil { + t.Fatal("expected on-demand config to be set") + } + if len(cfg.Issuers) != 0 { + t.Fatalf("expected no issuers for tailscale-managed ts.net policy, got %d", len(cfg.Issuers)) + } +} + +func TestAutomationPolicyImplicitTailscaleManagersOnlyCatchAll(t *testing.T) { + ap := AutomationPolicy{ + Managers: []certmagic.Manager{Tailscale{}}, + } + if ap.implicitTailscaleManagersOnly() { + t.Fatal("expected catch-all manager policy to remain outside tailscale-only special case") + } +}