internal/server/certificate_storage_coverage_test.go
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: Apache-2.0
package server
import (
"path/filepath"
"strings"
"testing"
"time"
)
// Issue #153: when a Managed ACME definition lists both a wildcard and the
// apex, Caddy stores TWO separate certificates. findStorageCertificate used to
// return whichever merely expired latest, so the apex certificate — normally
// the newer one, because it was added later — was chosen and pushed to fleet
// targets. Every subdomain then failed TLS on the target.
//
// Selection must follow domain coverage, not expiry.
func TestFindStorageCertificatePrefersBroadestCoverage(t *testing.T) {
dataDir := t.TempDir()
root := filepath.Join(dataDir, "caddy", "certificates")
roots := []string{dataDir}
issuer := "acme-v02.api.letsencrypt.org-directory"
domains := []string{"*.example.com", "example.com"}
// The apex is obtained later, so it expires later. That must not win.
writeStorageCertificate(t, root, issuer, "*.example.com", time.Now().Add(40*24*time.Hour))
writeStorageCertificate(t, root, issuer, "example.com", time.Now().Add(80*24*time.Hour))
found, err := findStorageCertificate(dataDir, domains, roots)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(found.CertPath, "wildcard_.example.com") {
t.Fatalf("selected %s, want the wildcard certificate", found.CertPath)
}
// Order in the definition must not change the answer.
found, err = findStorageCertificate(dataDir, []string{"example.com", "*.example.com"}, roots)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(found.CertPath, "wildcard_.example.com") {
t.Fatalf("reversed domain order selected %s, want the wildcard", found.CertPath)
}
}
// With equal coverage the newest certificate still wins, which is what the
// v2.42.0 export feature and renewal pickup rely on.
func TestFindStorageCertificateBreaksCoverageTiesByExpiry(t *testing.T) {
dataDir := t.TempDir()
root := filepath.Join(dataDir, "caddy", "certificates")
roots := []string{dataDir}
writeStorageCertificate(t, root, "acme.zerossl.com-v2-DV90", "*.example.com", time.Now().Add(20*24*time.Hour))
writeStorageCertificate(t, root, "acme-v02.api.letsencrypt.org-directory", "*.example.com", time.Now().Add(80*24*time.Hour))
found, err := findStorageCertificate(dataDir, []string{"*.example.com"}, roots)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(found.CertPath, "acme-v02.api.letsencrypt.org-directory") {
t.Fatalf("selected %s, want the later-expiring issuer", found.CertPath)
}
}
// A single stored certificate that covers every name is reported as complete,
// so callers can tell a full push from a partial one.
func TestFindStorageCertificateReportsFullCoverage(t *testing.T) {
dataDir := t.TempDir()
root := filepath.Join(dataDir, "caddy", "certificates")
roots := []string{dataDir}
writeStorageCertificate(t, root, "acme-v02.api.letsencrypt.org-directory", "*.example.com", time.Now().Add(60*24*time.Hour))
found, err := findStorageCertificate(dataDir, []string{"*.example.com", "a.example.com"}, roots)
if err != nil {
t.Fatal(err)
}
// One wildcard certificate covers both the wildcard name and a subdomain.
if len(found.Uncovered) != 0 {
t.Fatalf("uncovered = %v, want none: a wildcard covers its subdomains", found.Uncovered)
}
}
// When no single stored certificate covers every name, the gap is reported so
// the operator is told instead of being silently handed a partial certificate.
func TestFindStorageCertificateReportsUncoveredNames(t *testing.T) {
dataDir := t.TempDir()
root := filepath.Join(dataDir, "caddy", "certificates")
roots := []string{dataDir}
issuer := "acme-v02.api.letsencrypt.org-directory"
writeStorageCertificate(t, root, issuer, "*.example.com", time.Now().Add(40*24*time.Hour))
writeStorageCertificate(t, root, issuer, "example.com", time.Now().Add(80*24*time.Hour))
found, err := findStorageCertificate(dataDir, []string{"*.example.com", "example.com"}, roots)
if err != nil {
t.Fatal(err)
}
// The wildcard wins on coverage, but it does not cover the apex: *.example.com
// matches one label, never the bare domain.
if len(found.Uncovered) != 1 || found.Uncovered[0] != "example.com" {
t.Fatalf("uncovered = %v, want [example.com]", found.Uncovered)
}
}