proxy-pool/internal/controlplane/tlsreload/trust_test.go
2026-08-07 18:26:09 +08:00

121 lines
4.1 KiB
Go

package tlsreload
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"os"
"path/filepath"
"testing"
"time"
)
func TestTrustProviderReloadsRootsAndKeepsLastKnownGoodBundle(t *testing.T) {
directory := t.TempDir()
trustPath := filepath.Join(directory, "ca.crt")
firstAuthority := newTestAuthority(t, "first")
firstServer := firstAuthority.server(t, "controller")
writeTrustBundle(t, trustPath, firstAuthority.certificate)
provider, err := NewTrustProvider(trustPath)
if err != nil {
t.Fatalf("NewTrustProvider() error = %v", err)
}
verify := provider.VerifyServer("controller")
if err := verify(tls.ConnectionState{PeerCertificates: []*x509.Certificate{firstServer}}); err != nil {
t.Fatalf("verify first server with first root: %v", err)
}
secondAuthority := newTestAuthority(t, "second")
secondServer := secondAuthority.server(t, "controller")
writeTrustBundle(t, trustPath, secondAuthority.certificate)
if err := verify(tls.ConnectionState{PeerCertificates: []*x509.Certificate{firstServer}}); err == nil {
t.Fatal("verify old server after trust rotation error = nil")
}
if err := verify(tls.ConnectionState{PeerCertificates: []*x509.Certificate{secondServer}}); err != nil {
t.Fatalf("verify second server with second root: %v", err)
}
if err := os.WriteFile(trustPath, []byte("incomplete"), 0o600); err != nil {
t.Fatalf("break trust bundle: %v", err)
}
if err := verify(tls.ConnectionState{PeerCertificates: []*x509.Certificate{secondServer}}); err != nil {
t.Fatalf("verify with last known good trust bundle: %v", err)
}
}
func TestTrustProviderRejectsEmptyPath(t *testing.T) {
if _, err := NewTrustProvider(""); err != ErrInvalidTrustPath {
t.Fatalf("NewTrustProvider(empty path) error = %v, want %v", err, ErrInvalidTrustPath)
}
}
type testAuthority struct {
certificate *x509.Certificate
privateKey *ecdsa.PrivateKey
}
func newTestAuthority(t *testing.T, commonName string) testAuthority {
t.Helper()
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("generate authority key: %v", err)
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()), Subject: pkix.Name{CommonName: commonName},
NotBefore: now.Add(-time.Minute), NotAfter: now.Add(time.Hour),
IsCA: true, BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)
if err != nil {
t.Fatalf("create authority certificate: %v", err)
}
certificate, err := x509.ParseCertificate(der)
if err != nil {
t.Fatalf("parse authority certificate: %v", err)
}
return testAuthority{certificate: certificate, privateKey: privateKey}
}
func (authority testAuthority) server(t *testing.T, serverName string) *x509.Certificate {
t.Helper()
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("generate server key: %v", err)
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()), Subject: pkix.Name{CommonName: serverName},
DNSNames: []string{serverName}, NotBefore: now.Add(-time.Minute), NotAfter: now.Add(time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
der, err := x509.CreateCertificate(rand.Reader, template, authority.certificate, &privateKey.PublicKey, authority.privateKey)
if err != nil {
t.Fatalf("create server certificate: %v", err)
}
certificate, err := x509.ParseCertificate(der)
if err != nil {
t.Fatalf("parse server certificate: %v", err)
}
return certificate
}
func writeTrustBundle(t *testing.T, path string, certificate *x509.Certificate) {
t.Helper()
if certificate == nil {
t.Fatal("write trust bundle: certificate is nil")
}
payload := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw})
if err := os.WriteFile(path, payload, 0o600); err != nil {
t.Fatalf("write trust bundle: %v", err)
}
}