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
Showing only changes of commit 40c98cd267 - Show all commits
+43 -19
View File
@@ -10,25 +10,25 @@ import (
"testing"
"time"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/tstest/natlab/vmtest"
"tailscale.com/tstest/natlab/vnet"
)
// hardDualNoEndpoints is hard NAT with both an IPv4 LAN and an IPv6 prefix
// (so the DebugDERPRegion probe exercises both address families against the
// test DERP server) and TS_DEBUG_STRIP_ENDPOINTS=1 set on tailscaled so it
// doesn't announce any direct endpoints to peers. Combined on both nodes,
// that leaves DERP as the only available path for the tailnet ping. The
// home DERP itself is left alone so the sha256-raw verification path is
// still exercised.
func hardDualNoEndpoints(env *vmtest.Env) *vmtest.Node {
// easyAnd6NoEndpoints is easy NAT plus an IPv6 prefix and
// TS_DEBUG_STRIP_ENDPOINTS=1 on tailscaled so peer endpoints from control
// are dropped. That forces the initial peer-to-peer disco bootstrap to
// traverse DERP, since without endpoints from control the nodes have no
// other way to first learn about each other.
func easyAnd6NoEndpoints(env *vmtest.Env) *vmtest.Node {
n := env.NumNodes()
return env.AddNode(fmt.Sprintf("node-%d", n),
env.AddNetwork(
fmt.Sprintf("2.%d.%d.%d", n, n, n), // public IP
fmt.Sprintf("10.0.%d.1/24", n),
fmt.Sprintf("192.168.%d.1/24", n),
v6cidr(n),
vnet.HardNAT),
vnet.EasyNAT),
vnet.TailscaledEnv{Key: "TS_DEBUG_STRIP_ENDPOINTS", Value: "1"},
vmtest.OS(vmtest.Gokrazy))
}
@@ -39,12 +39,11 @@ func hardDualNoEndpoints(env *vmtest.Env) *vmtest.Node {
// fronting CertName), the two nodes communicate over the resulting tailnet,
// and `tailscale debug derp` against the same region succeeds.
//
// Both nodes sit behind hard NATs and additionally strip their direct
// endpoints (TS_DEBUG_STRIP_ENDPOINTS=1) so disco cannot find a direct path
// and the tailnet ping must traverse DERP, making the sha256-raw pinning of
// the tailscaled→DERP path part of the assertion. (Stripping endpoints — not
// just relying on hard NAT — is needed because the dual-stack LAN provides a
// non-NATted IPv6 path that the nodes would otherwise discover.)
// Nodes are dual-stack (v4 + v6) so the DebugDERPRegion probe exercises both
// address families against the test DERP server. They additionally strip
// peer endpoints from control so the initial peer-to-peer disco bootstrap
// must traverse DERP; the eventual data path may be direct or DERP, the
// test doesn't care, only that DERP worked end-to-end.
//
// The debug-derp half is the regression test for the bug fixed in PR #19965:
// before that change, [ipn/localapi.serveDebugDERPRegion] passed the raw
@@ -52,14 +51,39 @@ func hardDualNoEndpoints(env *vmtest.Env) *vmtest.Node {
// failed with a hostname mismatch.
func TestSelfSignedDERPHashPinning(t *testing.T) {
env := vmtest.New(t, vmtest.SelfSignedDERPCertPinning())
n1 := hardDualNoEndpoints(env)
n2 := hardDualNoEndpoints(env)
n1 := easyAnd6NoEndpoints(env)
n2 := easyAnd6NoEndpoints(env)
env.Start()
if err := env.PingExpect(n1, n2, vmtest.PingRouteDERP, 60*time.Second); err != nil {
// End-to-end ping over the WireGuard tunnel. With peer endpoints
// stripped from control, the only way for the peers to first reach each
// other is via DERP, so a successful tunnel ping proves the
// tailscaled→DERP TLS handshake (and thus sha256-raw cert pinning)
// worked on both ends.
if err := env.Ping(n1, n2, tailcfg.PingTSMP, 60*time.Second); err != nil {
t.Fatalf("ping node-0 -> node-1: %v", err)
}
// Also verify each node both sent and received data packets over DERP.
// With endpoints stripped from control, the TSMP ping above has no
// direct path available, so the WireGuard packets it generates must
// flow via DERP. These counters never decrease, so once they're
// non-zero we know DERP carried real frames in both directions.
for _, n := range []*vmtest.Node{n1, n2} {
if err := tstest.WaitFor(30*time.Second, func() error {
m := env.ClientMetrics(n)
sent := m["magicsock_send_data_derp"].Value
recv := m["magicsock_recv_data_derp"].Value
if sent == 0 || recv == 0 {
return fmt.Errorf("DERP data packets: sent=%d recv=%d; want both > 0", sent, recv)
}
t.Logf("[%s] DERP data packets: sent=%d recv=%d", n.Name(), sent, recv)
return nil
}); err != nil {
t.Errorf("[%s] %v", n.Name(), err)
}
}
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()