Ctrl K
ComunidadGuías…

internal/server/fleet_sync_wildcard_push_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: Apache-2.0

package server

import (
	"crypto/x509"
	"encoding/pem"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/X4Applegate/caddyui/internal/models"
)

// leafFromPEM parses the certificate a push left on the target.
func leafFromPEM(t *testing.T, certPEM string) *x509.Certificate {
	t.Helper()
	block, _ := pem.Decode([]byte(certPEM))
	if block == nil {
		t.Fatalf("target certificate is not PEM: %q", certPEM)
	}
	leaf, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		t.Fatal(err)
	}
	return leaf
}

// Issue #153: a Managed ACME certificate listing both the wildcard and the
// apex is stored by Caddy as two separate certificates. Source Push used to
// send whichever expired latest — normally the apex, because it was added
// later — so the target stopped serving every subdomain.
func TestSourcePushPrefersWildcardOverApexWhenBothAreStored(t *testing.T) {
	s, sourceServerID, targetServerID := newFleetSyncTestServer(t)
	dataDir := t.TempDir()
	root := filepath.Join(dataDir, "caddy", "certificates")
	issuer := "acme-v02.api.letsencrypt.org-directory"

	// The apex was obtained later, so it expires later.
	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))

	srv, _ := models.GetCaddyServer(s.DB, sourceServerID)
	srv.DataDir = dataDir
	if err := models.UpdateCaddyServer(s.DB, srv); err != nil {
		t.Fatal(err)
	}

	cert := newSourcePushCertificate(t, s, sourceServerID, "*.example.com,example.com", targetServerID)
	s.crossDeployCertificate("admin@example.com", sourceServerID, cert, cert.FleetPushTargetIDs())

	targets, err := models.ListCertificates(s.DB, targetServerID)
	if err != nil {
		t.Fatal(err)
	}
	if len(targets) != 1 {
		t.Fatalf("target certificates = %d, want exactly one pushed copy", len(targets))
	}
	leaf := leafFromPEM(t, targets[0].CertPEM)
	if !certificateCoversName(leaf, "app.example.com") {
		t.Fatalf("pushed certificate presents %v — it does not cover a subdomain, so HTTPS breaks on the target", leaf.DNSNames)
	}

	// The operator must be told that the apex is not in this push, rather than
	// discovering it when the apex stops resolving.
	activity, err := models.ListActivity(s.DB, sourceServerID, 50)
	if err != nil {
		t.Fatal(err)
	}
	var warned bool
	for _, entry := range activity {
		if entry.Action == "cert_source_push" && strings.Contains(entry.Detail, "example.com") {
			warned = true
		}
	}
	if !warned {
		t.Fatal("no activity entry warned that the push does not cover every name")
	}
}

// A wildcard-only definition still pushes cleanly and warns about nothing.
func TestSourcePushWildcardOnlyDefinitionWarnsAboutNothing(t *testing.T) {
	s, sourceServerID, targetServerID := newFleetSyncTestServer(t)
	dataDir := t.TempDir()
	root := filepath.Join(dataDir, "caddy", "certificates")
	writeStorageCertificate(t, root, "acme-v02.api.letsencrypt.org-directory", "*.example.com", time.Now().Add(60*24*time.Hour))

	srv, _ := models.GetCaddyServer(s.DB, sourceServerID)
	srv.DataDir = dataDir
	if err := models.UpdateCaddyServer(s.DB, srv); err != nil {
		t.Fatal(err)
	}

	cert := newSourcePushCertificate(t, s, sourceServerID, "*.example.com", targetServerID)
	s.crossDeployCertificate("admin@example.com", sourceServerID, cert, cert.FleetPushTargetIDs())

	activity, err := models.ListActivity(s.DB, sourceServerID, 50)
	if err != nil {
		t.Fatal(err)
	}
	for _, entry := range activity {
		if entry.Action == "cert_source_push" {
			t.Fatalf("unexpected coverage warning for a fully covered push: %s", entry.Detail)
		}
	}
}