diff --git a/caddyconfig/httpcaddyfile/pkiapp.go b/caddyconfig/httpcaddyfile/pkiapp.go index 3f856ff3640..14b86b8722b 100644 --- a/caddyconfig/httpcaddyfile/pkiapp.go +++ b/caddyconfig/httpcaddyfile/pkiapp.go @@ -22,6 +22,7 @@ import ( "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddypki" + "github.com/caddyserver/caddy/v2/modules/caddytls" ) func init() { @@ -260,5 +261,33 @@ func (st ServerType) buildPKIApp( pkiApp.CAs[ca.ID] = ca } + // an explicit site-level internal issuer creates its own CA + // regardless of `auto_https`, so make sure `skip_install_trust` + // is honored for it too; otherwise the CA is created later at + // runtime with trust installation left on + if skipInstallTrust { + for _, p := range pairings { + for _, sblock := range p.serverBlocks { + for _, issuerCfgValue := range sblock.pile["tls.cert_issuer"] { + internalIssuer, ok := issuerCfgValue.Value.(*caddytls.InternalIssuer) + if !ok { + continue + } + caID := internalIssuer.CA + if caID == "" { + caID = caddypki.DefaultCAID + } + if _, ok := pkiApp.CAs[caID]; ok { + continue + } + ca := new(caddypki.CA) + ca.ID = caID + ca.InstallTrust = &falseBool + pkiApp.CAs[ca.ID] = ca + } + } + } + } + return pkiApp, warnings, nil } diff --git a/caddyconfig/httpcaddyfile/pkiapp_test.go b/caddyconfig/httpcaddyfile/pkiapp_test.go index 57662f71e5f..365546a92d0 100644 --- a/caddyconfig/httpcaddyfile/pkiapp_test.go +++ b/caddyconfig/httpcaddyfile/pkiapp_test.go @@ -67,6 +67,96 @@ func TestParsePKIApp_maintenanceIntervalAndRenewalWindowRatio(t *testing.T) { } } +// adaptedPKIConfig runs the given Caddyfile through the adapter and +// returns just the parts of the resulting JSON this test cares about: +// which CAs got a certificate_authorities entry, and whether install_trust +// was set to false for each. +func adaptedPKIConfig(t *testing.T, input string) map[string]struct { + InstallTrust *bool `json:"install_trust,omitempty"` +} { + t.Helper() + + adapter := caddyfile.Adapter{ServerType: ServerType{}} + out, _, err := adapter.Adapt([]byte(input), nil) + if err != nil { + t.Fatalf("Adapt failed: %v", err) + } + + var cfg struct { + Apps struct { + PKI struct { + CertificateAuthorities map[string]struct { + InstallTrust *bool `json:"install_trust,omitempty"` + } `json:"certificate_authorities,omitempty"` + } `json:"pki,omitempty"` + } `json:"apps"` + } + if err := json.Unmarshal(out, &cfg); err != nil { + t.Fatalf("unmarshal config: %v", err) + } + return cfg.Apps.PKI.CertificateAuthorities +} + +func TestParsePKIApp_skipInstallTrustHonoredForInternalTLSDefaultCA(t *testing.T) { + input := `{ + auto_https off + skip_install_trust + } + internal.example.com { + tls internal + } + ` + cas := adaptedPKIConfig(t, input) + + ca, ok := cas["local"] + if !ok { + t.Fatal("expected certificate_authorities.local to exist") + } + if ca.InstallTrust == nil || *ca.InstallTrust != false { + t.Errorf("install_trust = %v, want false", ca.InstallTrust) + } +} + +func TestParsePKIApp_skipInstallTrustHonoredForInternalTLSCustomCA(t *testing.T) { + input := `{ + auto_https off + skip_install_trust + } + internal.example.com { + tls { + issuer internal { + ca mycustomca + } + } + } + ` + cas := adaptedPKIConfig(t, input) + + ca, ok := cas["mycustomca"] + if !ok { + t.Fatal("expected certificate_authorities.mycustomca to exist") + } + if ca.InstallTrust == nil || *ca.InstallTrust != false { + t.Errorf("install_trust = %v, want false", ca.InstallTrust) + } +} + +func TestParsePKIApp_noPKIAppWhenNoInternalIssuerConfigured(t *testing.T) { + input := `{ + auto_https off + skip_install_trust + } + localhost:8080 { + respond "no tls internal used anywhere" + } + ` + cas := adaptedPKIConfig(t, input) + + if len(cas) != 0 { + t.Errorf("expected no certificate_authorities, got %v", cas) + } +} + func TestParsePKIApp_renewalWindowRatioInvalid(t *testing.T) { input := `{ pki {