WIP: rebase fork onto upstream/main (v1.103.0) #15

Closed
codinget wants to merge 670 commits from webnet into save/webnet-2026-07-29
2 changed files with 82 additions and 0 deletions
Showing only changes of commit 1a14668a31 - Show all commits
+7
View File
@@ -88,6 +88,13 @@ func (e *extension) getCertPEMWithValidity(ctx context.Context, b *ipnlocal.Loca
return getCertForTest(domain)
}
// Trim a trailing dot from the domain (e.g. from an SNI ServerName of
// "host.ts.net.") before lookup. Per RFC 6066 §3 the SNI HostName has
// no trailing dot, but some clients send a fully-qualified name with
// one, and cert store names have no trailing dot. See
// https://github.com/tailscale/tailscale/issues/10233.
domain = strings.TrimSuffix(domain, ".")
if !validLookingCertDomain(domain) {
return nil, errors.New("invalid domain")
}
+75
View File
@@ -806,6 +806,81 @@ func TestGetCertPEMWithValidity(t *testing.T) {
}
}
// TestGetCertPEMWithValidityTrimsTrailingDot verifies that an SNI
// ServerName with a trailing dot (e.g. "example.com.") resolves to the
// same cached certificate as the dotless form. Per RFC 6066 §3 the SNI
// HostName carries no trailing dot, but some clients send an FQDN with
// one; the cert store keys certs by the dotless domain, so the trailing
// dot must be trimmed before lookup. See tailscale/tailscale#10233.
func TestGetCertPEMWithValidityTrimsTrailingDot(t *testing.T) {
tstest.AssertNotParallel(t)
const testDomain = "example.com"
b := ipnlocaltest.NewBackend(t)
b.SetVarRoot(t.TempDir())
e := extOf(t, b)
// Set up netmap with CertDomains so resolveCertDomain works. Note
// CertDomains holds the dotless domain, matching what control sends.
b.ForTest().SetNetMap(&netmap.NetworkMap{
SelfNode: (&tailcfg.Node{}).View(),
DNS: tailcfg.DNSConfig{
CertDomains: []string{testDomain},
},
})
certDirPath, err := certDir(b)
if err != nil {
t.Fatalf("certDir error: %v", err)
}
if _, err := e.getCertStore(b); err != nil {
t.Fatalf("getCertStore error: %v", err)
}
testRoot, err := certTestFS.ReadFile("testdata/rootCA.pem")
if err != nil {
t.Fatal(err)
}
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(testRoot) {
t.Fatal("Unable to add test CA to the cert pool")
}
testX509Roots = roots
defer func() { testX509Roots = nil }()
// Store a valid, unexpired cached cert under the dotless domain.
os.MkdirAll(certDirPath, 0755)
if err := os.WriteFile(filepath.Join(certDirPath, "example.com.crt"),
must.Get(certTestFS.ReadFile("testdata/example.com.pem")), 0644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(certDirPath, "example.com.key"),
must.Get(certTestFS.ReadFile("testdata/example.com-key.pem")), 0644); err != nil {
t.Fatal(err)
}
// A time at which the stored cert is valid and does not need renewal.
b.ForTest().SetClock(tstest.NewClock(tstest.ClockOpts{
Start: time.Date(2023, time.February, 20, 0, 0, 0, 0, time.UTC),
}))
// Fail loudly if issuance/renewal is attempted: a trailing-dot lookup
// must hit the cached cert, not trigger a fresh ACME order.
orig := getCertPEM
getCertPEM = func(ctx context.Context, e *extension, b *ipnlocal.LocalBackend, cs certStore, logf logger.Logf, traceACME func(any), domain string, now time.Time, minValidity time.Duration) (*ipnlocal.TLSCertKeyPair, error) {
t.Errorf("unexpected getCertPEM call for domain %q; trailing-dot lookup should have hit the cache", domain)
return nil, nil
}
t.Cleanup(func() { getCertPEM = orig })
pair, err := b.GetCertPEMWithValidity(context.Background(), testDomain+".", 0)
if err != nil {
t.Fatalf("GetCertPEMWithValidity(%q) error = %v, want cached cert", testDomain+".", err)
}
if pair == nil || !pair.Cached {
t.Fatalf("GetCertPEMWithValidity(%q) = %+v, want cached cert", testDomain+".", pair)
}
}
func TestCertPendingWarnable(t *testing.T) {
b := ipnlocaltest.NewBackend(t)
e := extOf(t, b)