From 194d5b841266f9b7b8e61da758582925d289ec7e Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 25 Jun 2021 12:59:45 -0700 Subject: [PATCH] tstest/integration/vms: add in-process DERP server (#2108) Previously this test would reach out to the public DERP servers in order to help machines connect with eachother. This is not ideal given our plans to run these tests completely disconnected from the internet. This patch introduces an in-process DERP server running on its own randomly assigned HTTP port. Updates #1988 Signed-off-by: Christine Dodrill --- tstest/integration/integration.go | 62 ++++++++++++++++++++++++++ tstest/integration/integration_test.go | 59 +----------------------- tstest/integration/vms/vms_test.go | 6 ++- 3 files changed, 68 insertions(+), 59 deletions(-) diff --git a/tstest/integration/integration.go b/tstest/integration/integration.go index 120fbf578..025cb31e6 100644 --- a/tstest/integration/integration.go +++ b/tstest/integration/integration.go @@ -9,6 +9,11 @@ package integration import ( + "crypto/rand" + "crypto/tls" + "net" + "net/http" + "net/http/httptest" "os" "os/exec" "path" @@ -19,6 +24,13 @@ import ( "testing" "time" + "tailscale.com/derp" + "tailscale.com/derp/derphttp" + "tailscale.com/net/stun/stuntest" + "tailscale.com/tailcfg" + "tailscale.com/types/key" + "tailscale.com/types/logger" + "tailscale.com/types/nettype" "tailscale.com/version" ) @@ -98,3 +110,53 @@ func exe() string { } return "" } + +// RunDERPAndSTUN runs a local DERP and STUN server for tests, returning the derpMap +// that clients should use. This creates resources that must be cleaned up with the +// returned cleanup function. +func RunDERPAndSTUN(t testing.TB, logf logger.Logf, ipAddress string) (derpMap *tailcfg.DERPMap) { + t.Helper() + + var serverPrivateKey key.Private + if _, err := rand.Read(serverPrivateKey[:]); err != nil { + t.Fatal(err) + } + d := derp.NewServer(serverPrivateKey, logf) + + httpsrv := httptest.NewUnstartedServer(derphttp.Handler(d)) + httpsrv.Config.ErrorLog = logger.StdLogger(logf) + httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) + httpsrv.StartTLS() + + stunAddr, stunCleanup := stuntest.ServeWithPacketListener(t, nettype.Std{}) + + m := &tailcfg.DERPMap{ + Regions: map[int]*tailcfg.DERPRegion{ + 1: { + RegionID: 1, + RegionCode: "test", + Nodes: []*tailcfg.DERPNode{ + { + Name: "t1", + RegionID: 1, + HostName: ipAddress, + IPv4: ipAddress, + IPv6: "none", + STUNPort: stunAddr.Port, + DERPTestPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port, + STUNTestIP: stunAddr.IP.String(), + }, + }, + }, + }, + } + + t.Cleanup(func() { + httpsrv.CloseClientConnections() + httpsrv.Close() + d.Close() + stunCleanup() + }) + + return m +} diff --git a/tstest/integration/integration_test.go b/tstest/integration/integration_test.go index 271da1283..4c264dcff 100644 --- a/tstest/integration/integration_test.go +++ b/tstest/integration/integration_test.go @@ -7,8 +7,6 @@ package integration import ( "bytes" "context" - crand "crypto/rand" - "crypto/tls" "encoding/json" "errors" "flag" @@ -16,7 +14,6 @@ import ( "io" "io/ioutil" "log" - "net" "net/http" "net/http/httptest" "os" @@ -31,18 +28,13 @@ import ( "time" "go4.org/mem" - "tailscale.com/derp" - "tailscale.com/derp/derphttp" "tailscale.com/ipn/ipnstate" - "tailscale.com/net/stun/stuntest" "tailscale.com/safesocket" "tailscale.com/smallzstd" "tailscale.com/tailcfg" "tailscale.com/tstest" "tailscale.com/tstest/integration/testcontrol" - "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/nettype" ) var ( @@ -301,8 +293,6 @@ type testEnv struct { TrafficTrap *trafficTrap TrafficTrapServer *httptest.Server - - derpShutdown func() } type testEnvOpt interface { @@ -323,7 +313,7 @@ func newTestEnv(t testing.TB, bins *Binaries, opts ...testEnvOpt) *testEnv { if runtime.GOOS == "windows" { t.Skip("not tested/working on Windows yet") } - derpMap, derpShutdown := runDERPAndStun(t, logger.Discard) + derpMap := RunDERPAndSTUN(t, logger.Discard, "127.0.0.1") logc := new(logCatcher) control := &testcontrol.Server{ DERPMap: derpMap, @@ -339,7 +329,6 @@ func newTestEnv(t testing.TB, bins *Binaries, opts ...testEnvOpt) *testEnv { ControlServer: control.HTTPTestServer, TrafficTrap: trafficTrap, TrafficTrapServer: httptest.NewServer(trafficTrap), - derpShutdown: derpShutdown, } for _, o := range opts { o.modifyTestEnv(e) @@ -357,7 +346,6 @@ func (e *testEnv) Close() error { e.LogCatcherServer.Close() e.TrafficTrapServer.Close() e.ControlServer.Close() - e.derpShutdown() return nil } @@ -620,51 +608,6 @@ func (tt *trafficTrap) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(403) } -func runDERPAndStun(t testing.TB, logf logger.Logf) (derpMap *tailcfg.DERPMap, cleanup func()) { - var serverPrivateKey key.Private - if _, err := crand.Read(serverPrivateKey[:]); err != nil { - t.Fatal(err) - } - d := derp.NewServer(serverPrivateKey, logf) - - httpsrv := httptest.NewUnstartedServer(derphttp.Handler(d)) - httpsrv.Config.ErrorLog = logger.StdLogger(logf) - httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) - httpsrv.StartTLS() - - stunAddr, stunCleanup := stuntest.ServeWithPacketListener(t, nettype.Std{}) - - m := &tailcfg.DERPMap{ - Regions: map[int]*tailcfg.DERPRegion{ - 1: { - RegionID: 1, - RegionCode: "test", - Nodes: []*tailcfg.DERPNode{ - { - Name: "t1", - RegionID: 1, - HostName: "127.0.0.1", // to bypass HTTP proxy - IPv4: "127.0.0.1", - IPv6: "none", - STUNPort: stunAddr.Port, - DERPTestPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port, - STUNTestIP: stunAddr.IP.String(), - }, - }, - }, - }, - } - - cleanup = func() { - httpsrv.CloseClientConnections() - httpsrv.Close() - d.Close() - stunCleanup() - } - - return m, cleanup -} - type authURLParserWriter struct { buf bytes.Buffer fn func(urlStr string) error diff --git a/tstest/integration/vms/vms_test.go b/tstest/integration/vms/vms_test.go index 327301139..ce5d910c0 100644 --- a/tstest/integration/vms/vms_test.go +++ b/tstest/integration/vms/vms_test.go @@ -546,7 +546,8 @@ func TestVMIntegrationEndToEnd(t *testing.T) { rex := distroRex.Unwrap() - ln, err := net.Listen("tcp", deriveBindhost(t)+":0") + bindHost := deriveBindhost(t) + ln, err := net.Listen("tcp", net.JoinHostPort(bindHost, "0")) if err != nil { t.Fatalf("can't make TCP listener: %v", err) } @@ -555,6 +556,9 @@ func TestVMIntegrationEndToEnd(t *testing.T) { cs := &testcontrol.Server{} + derpMap := integration.RunDERPAndSTUN(t, t.Logf, bindHost) + cs.DERPMap = derpMap + var ( ipMu sync.Mutex ipMap = map[string]ipMapping{}