diff --git a/coderd/coderd.go b/coderd/coderd.go index 52bdc54208d11..1f9971bf014da 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -165,7 +165,13 @@ func New(options *Options) *API { // other applications might not as well. r.Route("/%40{user}/{workspace_and_agent}/apps/{workspaceapp}", apps) r.Route("/@{user}/{workspace_and_agent}/apps/{workspaceapp}", apps) - r.Get("/derp", derphttp.Handler(api.derpServer).ServeHTTP) + r.Route("/derp", func(r chi.Router) { + r.Get("/", derphttp.Handler(api.derpServer).ServeHTTP) + // This is used when UDP is blocked, and latency must be checked via HTTP(s). + r.Get("/latency-check", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + }) r.Route("/api/v2", func(r chi.Router) { r.NotFound(func(rw http.ResponseWriter, r *http.Request) { diff --git a/coderd/coderd_test.go b/coderd/coderd_test.go index 7e9175e73392b..87206873b1073 100644 --- a/coderd/coderd_test.go +++ b/coderd/coderd_test.go @@ -2,6 +2,7 @@ package coderd_test import ( "context" + "net/http" "net/netip" "strconv" "testing" @@ -114,3 +115,12 @@ func TestDERP(t *testing.T) { w1.Close() w2.Close() } + +func TestDERPLatencyCheck(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + res, err := client.Request(context.Background(), http.MethodGet, "/derp/latency-check", nil) + require.NoError(t, err) + defer res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode) +} diff --git a/coderd/coderdtest/authtest.go b/coderd/coderdtest/authtest.go index 7bfa855da8f7a..66a7007eeb4ce 100644 --- a/coderd/coderdtest/authtest.go +++ b/coderd/coderdtest/authtest.go @@ -168,6 +168,7 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) { skipRoutes := map[string]string{ "POST:/api/v2/users/logout": "Logging out deletes the API Key for other routes", "GET:/derp": "This requires a WebSocket upgrade!", + "GET:/derp/latency-check": "This always returns a 200!", } assertRoute := map[string]RouteCheck{ diff --git a/go.mod b/go.mod index 2b8480f592d66..d6e0cc3528888 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ replace github.com/tcnksm/go-httpstat => github.com/kylecarbs/go-httpstat v0.0.0 // There are a few minor changes we make to Tailscale that we're slowly upstreaming. Compare here: // https://github.com/tailscale/tailscale/compare/main...coder:tailscale:main -replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20220831012541-a77bda274fd6 +replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20220901032724-2ed2977662a4 require ( cdr.dev/slog v1.4.2-0.20220525200111-18dce5c2cd5f diff --git a/go.sum b/go.sum index f479632bf4fb2..7e82e1685fbb0 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,8 @@ github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1 h1:UqBrPWSYvRI2s5RtOu github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/coder/retry v1.3.0 h1:5lAAwt/2Cm6lVmnfBY7sOMXcBOwcwJhmV5QGSELIVWY= github.com/coder/retry v1.3.0/go.mod h1:tXuRgZgWjUnU5LZPT4lJh4ew2elUhexhlnXzrJWdyFY= -github.com/coder/tailscale v1.1.1-0.20220831012541-a77bda274fd6 h1://ApBDDh58hFwMe0AzlgqJrGhzu6Rjk8fQXrR+mbhYE= -github.com/coder/tailscale v1.1.1-0.20220831012541-a77bda274fd6/go.mod h1:MO+tWkQp2YIF3KBnnej/mQvgYccRS5Xk/IrEpZ4Z3BU= +github.com/coder/tailscale v1.1.1-0.20220901032724-2ed2977662a4 h1:TXtMXPt4ds1pM9QrLsfiDEJv7KE8q0yRQGCkSepeKZ8= +github.com/coder/tailscale v1.1.1-0.20220901032724-2ed2977662a4/go.mod h1:MO+tWkQp2YIF3KBnnej/mQvgYccRS5Xk/IrEpZ4Z3BU= github.com/coder/wireguard-go/tun/netstack v0.0.0-20220823170024-a78136eb0cab h1:9yEvRWXXfyKzXu8AqywCi+tFZAoqCy4wVcsXwuvZNMc= github.com/coder/wireguard-go/tun/netstack v0.0.0-20220823170024-a78136eb0cab/go.mod h1:TCJ66NtXh3urJotTdoYQOHHkyE899vOQl5TuF+WLSes= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= diff --git a/tailnet/conn.go b/tailnet/conn.go index 84aed3fc1ae81..7f73d66c9f849 100644 --- a/tailnet/conn.go +++ b/tailnet/conn.go @@ -161,21 +161,6 @@ func NewConn(options *Options) (*Conn, error) { return nil, xerrors.Errorf("start netstack: %w", err) } wireguardEngine = wgengine.NewWatchdog(wireguardEngine) - - // Update the wireguard configuration to allow traffic to flow. - wireguardConfig, err := nmcfg.WGCfg(netMap, Logger(options.Logger.Named("wgconfig")), netmap.AllowSingleHosts, "") - if err != nil { - return nil, xerrors.Errorf("create wgcfg: %w", err) - } - - wireguardRouter := &router.Config{ - LocalAddrs: wireguardConfig.Addresses, - } - err = wireguardEngine.Reconfig(wireguardConfig, wireguardRouter, &dns.Config{}, &tailcfg.Debug{}) - if err != nil { - return nil, xerrors.Errorf("reconfig: %w", err) - } - wireguardEngine.SetDERPMap(options.DERPMap) netMapCopy := *netMap wireguardEngine.SetNetworkMap(&netMapCopy) @@ -198,8 +183,10 @@ func NewConn(options *Options) (*Conn, error) { netMap: netMap, netStack: netStack, wireguardMonitor: wireguardMonitor, - wireguardRouter: wireguardRouter, - wireguardEngine: wireguardEngine, + wireguardRouter: &router.Config{ + LocalAddrs: netMap.Addresses, + }, + wireguardEngine: wireguardEngine, } netStack.ForwardTCPIn = server.forwardTCP return server, nil @@ -261,7 +248,7 @@ func (c *Conn) SetNodeCallback(callback func(node *Node)) { DERPLatency: c.lastDERPLatency, } } - c.magicConn.SetNetInfoCallback(func(ni *tailcfg.NetInfo) { + c.wireguardEngine.SetNetInfoCallback(func(ni *tailcfg.NetInfo) { c.lastMutex.Lock() c.lastPreferredDERP = ni.PreferredDERP c.lastDERPLatency = ni.DERPLatency @@ -309,6 +296,7 @@ func (c *Conn) UpdateNodes(nodes []*Node) error { peerMap[peer.ID] = peer } for _, node := range nodes { + peerStatus, ok := status.Peer[node.Key] peerMap[node.ID] = &tailcfg.Node{ ID: node.ID, Key: node.Key, @@ -318,12 +306,18 @@ func (c *Conn) UpdateNodes(nodes []*Node) error { Endpoints: node.Endpoints, DERP: fmt.Sprintf("%s:%d", tailcfg.DerpMagicIP, node.PreferredDERP), Hostinfo: hostinfo.New().View(), + // Starting KeepAlive messages at the initialization + // of a connection cause it to hang for an unknown + // reason. TODO: @kylecarbs debug this! + KeepAlive: ok && peerStatus.Active, } } c.netMap.Peers = make([]*tailcfg.Node, 0, len(peerMap)) for _, peer := range peerMap { c.netMap.Peers = append(c.netMap.Peers, peer) } + netMapCopy := *c.netMap + c.wireguardEngine.SetNetworkMap(&netMapCopy) cfg, err := nmcfg.WGCfg(c.netMap, Logger(c.logger.Named("wgconfig")), netmap.AllowSingleHosts, "") if err != nil { return xerrors.Errorf("update wireguard config: %w", err) @@ -332,15 +326,13 @@ func (c *Conn) UpdateNodes(nodes []*Node) error { if err != nil { return xerrors.Errorf("reconfig: %w", err) } - netMapCopy := *c.netMap - c.wireguardEngine.SetNetworkMap(&netMapCopy) return nil } // Status returns the current ipnstate of a connection. func (c *Conn) Status() *ipnstate.Status { sb := &ipnstate.StatusBuilder{} - c.magicConn.UpdateStatus(sb) + c.wireguardEngine.UpdateStatus(sb) return sb.Status() } diff --git a/tailnet/conn_test.go b/tailnet/conn_test.go index 32c3083604ae5..bc3d5aec284af 100644 --- a/tailnet/conn_test.go +++ b/tailnet/conn_test.go @@ -55,10 +55,12 @@ func TestTailnet(t *testing.T) { _ = w2.Close() }) w1.SetNodeCallback(func(node *tailnet.Node) { - w2.UpdateNodes([]*tailnet.Node{node}) + err := w2.UpdateNodes([]*tailnet.Node{node}) + require.NoError(t, err) }) w2.SetNodeCallback(func(node *tailnet.Node) { - w1.UpdateNodes([]*tailnet.Node{node}) + err := w1.UpdateNodes([]*tailnet.Node{node}) + require.NoError(t, err) }) conn := make(chan struct{}) diff --git a/tailnet/coordinator.go b/tailnet/coordinator.go index db8dffa0ebaef..95209d56559ff 100644 --- a/tailnet/coordinator.go +++ b/tailnet/coordinator.go @@ -28,19 +28,25 @@ type Node struct { // ServeCoordinator matches the RW structure of a coordinator to exchange node messages. func ServeCoordinator(conn net.Conn, updateNodes func(node []*Node) error) (func(node *Node), <-chan error) { - errChan := make(chan error, 3) + errChan := make(chan error, 1) + sendErr := func(err error) { + select { + case errChan <- err: + default: + } + } go func() { decoder := json.NewDecoder(conn) for { var nodes []*Node err := decoder.Decode(&nodes) if err != nil { - errChan <- xerrors.Errorf("read: %w", err) + sendErr(xerrors.Errorf("read: %w", err)) return } err = updateNodes(nodes) if err != nil { - errChan <- xerrors.Errorf("update nodes: %w", err) + sendErr(xerrors.Errorf("update nodes: %w", err)) } } }() @@ -48,12 +54,12 @@ func ServeCoordinator(conn net.Conn, updateNodes func(node []*Node) error) (func return func(node *Node) { data, err := json.Marshal(node) if err != nil { - errChan <- xerrors.Errorf("marshal node: %w", err) + sendErr(xerrors.Errorf("marshal node: %w", err)) return } _, err = conn.Write(data) if err != nil { - errChan <- xerrors.Errorf("write: %w", err) + sendErr(xerrors.Errorf("write: %w", err)) } }, errChan }