sniproxy_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "context"
  6. "encoding/json"
  7. "flag"
  8. "fmt"
  9. "log"
  10. "net"
  11. "net/http/httptest"
  12. "net/netip"
  13. "os"
  14. "path/filepath"
  15. "strings"
  16. "testing"
  17. "time"
  18. "github.com/google/go-cmp/cmp"
  19. "tailscale.com/ipn/store/mem"
  20. "tailscale.com/net/netns"
  21. "tailscale.com/tailcfg"
  22. "tailscale.com/tsnet"
  23. "tailscale.com/tstest/integration"
  24. "tailscale.com/tstest/integration/testcontrol"
  25. "tailscale.com/tstest/nettest"
  26. "tailscale.com/types/appctype"
  27. "tailscale.com/types/ipproto"
  28. "tailscale.com/types/key"
  29. "tailscale.com/types/logger"
  30. )
  31. func TestPortForwardingArguments(t *testing.T) {
  32. tests := []struct {
  33. in string
  34. wanterr string
  35. want *portForward
  36. }{
  37. {"", "", nil},
  38. {"bad port specifier", "cannot parse", nil},
  39. {"tcp/xyz/example.com", "bad forwarding port", nil},
  40. {"tcp//example.com", "bad forwarding port", nil},
  41. {"tcp/2112/", "bad destination", nil},
  42. {"udp/53/example.com", "unsupported forwarding protocol", nil},
  43. {"tcp/22/github.com", "", &portForward{Proto: "tcp", Port: 22, Destination: "github.com"}},
  44. }
  45. for _, tt := range tests {
  46. got, goterr := parseForward(tt.in)
  47. if tt.wanterr != "" {
  48. if !strings.Contains(goterr.Error(), tt.wanterr) {
  49. t.Errorf("f(%q).err = %v; want %v", tt.in, goterr, tt.wanterr)
  50. }
  51. } else if diff := cmp.Diff(got, tt.want); diff != "" {
  52. t.Errorf("Parsed forward (-got, +want):\n%s", diff)
  53. }
  54. }
  55. }
  56. var verboseDERP = flag.Bool("verbose-derp", false, "if set, print DERP and STUN logs")
  57. var verboseNodes = flag.Bool("verbose-nodes", false, "if set, print tsnet.Server logs")
  58. func startControl(t *testing.T) (control *testcontrol.Server, controlURL string) {
  59. // Corp#4520: don't use netns for tests.
  60. netns.SetEnabled(false)
  61. t.Cleanup(func() {
  62. netns.SetEnabled(true)
  63. })
  64. derpLogf := logger.Discard
  65. if *verboseDERP {
  66. derpLogf = t.Logf
  67. }
  68. derpMap := integration.RunDERPAndSTUN(t, derpLogf, "127.0.0.1")
  69. control = &testcontrol.Server{
  70. DERPMap: derpMap,
  71. DNSConfig: &tailcfg.DNSConfig{
  72. Proxied: true,
  73. },
  74. MagicDNSDomain: "tail-scale.ts.net",
  75. }
  76. control.HTTPTestServer = httptest.NewUnstartedServer(control)
  77. control.HTTPTestServer.Start()
  78. t.Cleanup(control.HTTPTestServer.Close)
  79. controlURL = control.HTTPTestServer.URL
  80. t.Logf("testcontrol listening on %s", controlURL)
  81. return control, controlURL
  82. }
  83. func startNode(t *testing.T, ctx context.Context, controlURL, hostname string) (*tsnet.Server, key.NodePublic, netip.Addr) {
  84. t.Helper()
  85. tmp := filepath.Join(t.TempDir(), hostname)
  86. os.MkdirAll(tmp, 0755)
  87. s := &tsnet.Server{
  88. Dir: tmp,
  89. ControlURL: controlURL,
  90. Hostname: hostname,
  91. Store: new(mem.Store),
  92. Ephemeral: true,
  93. }
  94. if *verboseNodes {
  95. s.Logf = log.Printf
  96. }
  97. t.Cleanup(func() { s.Close() })
  98. status, err := s.Up(ctx)
  99. if err != nil {
  100. t.Fatal(err)
  101. }
  102. return s, status.Self.PublicKey, status.TailscaleIPs[0]
  103. }
  104. func TestSNIProxyWithNetmapConfig(t *testing.T) {
  105. nettest.SkipIfNoNetwork(t)
  106. c, controlURL := startControl(t)
  107. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  108. defer cancel()
  109. // Create a listener to proxy connections to.
  110. ln, err := net.Listen("tcp", "127.0.0.1:0")
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. defer ln.Close()
  115. // Start sniproxy
  116. sni, nodeKey, ip := startNode(t, ctx, controlURL, "snitest")
  117. go run(ctx, sni, 0, sni.Hostname, false, 0, "", "")
  118. // Configure the mock coordination server to send down app connector config.
  119. config := &appctype.AppConnectorConfig{
  120. DNAT: map[appctype.ConfigID]appctype.DNATConfig{
  121. "nic_test": {
  122. Addrs: []netip.Addr{ip},
  123. To: []string{"127.0.0.1"},
  124. IP: []tailcfg.ProtoPortRange{
  125. {
  126. Proto: int(ipproto.TCP),
  127. Ports: tailcfg.PortRange{First: uint16(ln.Addr().(*net.TCPAddr).Port), Last: uint16(ln.Addr().(*net.TCPAddr).Port)},
  128. },
  129. },
  130. },
  131. },
  132. }
  133. b, err := json.Marshal(config)
  134. if err != nil {
  135. t.Fatal(err)
  136. }
  137. c.SetNodeCapMap(nodeKey, tailcfg.NodeCapMap{
  138. configCapKey: []tailcfg.RawMessage{tailcfg.RawMessage(b)},
  139. })
  140. // Lets spin up a second node (to represent the client).
  141. client, _, _ := startNode(t, ctx, controlURL, "client")
  142. // Make sure that the sni node has received its config.
  143. l, err := sni.LocalClient()
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. gotConfigured := false
  148. for range 100 {
  149. s, err := l.StatusWithoutPeers(ctx)
  150. if err != nil {
  151. t.Fatal(err)
  152. }
  153. if len(s.Self.CapMap) > 0 {
  154. gotConfigured = true
  155. break // we got it
  156. }
  157. time.Sleep(10 * time.Millisecond)
  158. }
  159. if !gotConfigured {
  160. t.Error("sni node never received its configuration from the coordination server!")
  161. }
  162. // Lets make the client open a connection to the sniproxy node, and
  163. // make sure it results in a connection to our test listener.
  164. w, err := client.Dial(ctx, "tcp", fmt.Sprintf("%s:%d", ip, ln.Addr().(*net.TCPAddr).Port))
  165. if err != nil {
  166. t.Fatal(err)
  167. }
  168. defer w.Close()
  169. r, err := ln.Accept()
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. r.Close()
  174. }
  175. func TestSNIProxyWithFlagConfig(t *testing.T) {
  176. nettest.SkipIfNoNetwork(t)
  177. _, controlURL := startControl(t)
  178. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  179. defer cancel()
  180. // Create a listener to proxy connections to.
  181. ln, err := net.Listen("tcp", "127.0.0.1:0")
  182. if err != nil {
  183. t.Fatal(err)
  184. }
  185. defer ln.Close()
  186. // Start sniproxy
  187. sni, _, ip := startNode(t, ctx, controlURL, "snitest")
  188. go run(ctx, sni, 0, sni.Hostname, false, 0, "", fmt.Sprintf("tcp/%d/localhost", ln.Addr().(*net.TCPAddr).Port))
  189. // Lets spin up a second node (to represent the client).
  190. client, _, _ := startNode(t, ctx, controlURL, "client")
  191. // Lets make the client open a connection to the sniproxy node, and
  192. // make sure it results in a connection to our test listener.
  193. w, err := client.Dial(ctx, "tcp", fmt.Sprintf("%s:%d", ip, ln.Addr().(*net.TCPAddr).Port))
  194. if err != nil {
  195. t.Fatal(err)
  196. }
  197. defer w.Close()
  198. r, err := ln.Accept()
  199. if err != nil {
  200. t.Fatal(err)
  201. }
  202. r.Close()
  203. }