WIP: rebase fork onto upstream/main (v1.103.0) #15
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user