diff --git a/codersdk/workspaceagentconn.go b/codersdk/workspaceagentconn.go index 7fb86c1d3a098..0095ac0e13426 100644 --- a/codersdk/workspaceagentconn.go +++ b/codersdk/workspaceagentconn.go @@ -30,9 +30,9 @@ import ( var WorkspaceAgentIP = netip.MustParseAddr("fd7a:115c:a1e0:49d6:b259:b7ac:b1b2:48f4") const ( - WorkspaceAgentSSHPort = 1 - WorkspaceAgentReconnectingPTYPort = 2 - WorkspaceAgentSpeedtestPort = 3 + WorkspaceAgentSSHPort = tailnet.WorkspaceAgentSSHPort + WorkspaceAgentReconnectingPTYPort = tailnet.WorkspaceAgentReconnectingPTYPort + WorkspaceAgentSpeedtestPort = tailnet.WorkspaceAgentSpeedtestPort // WorkspaceAgentHTTPAPIServerPort serves a HTTP server with endpoints for e.g. // gathering agent statistics. WorkspaceAgentHTTPAPIServerPort = 4 diff --git a/go.mod b/go.mod index 5c1ef53e23a26..e63edefb10da4 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ replace github.com/dlclark/regexp2 => github.com/dlclark/regexp2 v1.7.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.20230411160749-27a458a0ac0a +replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20230418202606-ed9307cf1b22 // Switch to our fork that imports fixes from http://github.com/tailscale/ssh. // See: https://github.com/coder/coder/issues/3371 diff --git a/go.sum b/go.sum index 5ddac2eeccd28..c3ead3f4c48fe 100644 --- a/go.sum +++ b/go.sum @@ -380,8 +380,8 @@ github.com/coder/retry v1.3.1-0.20230210155434-e90a2e1e091d h1:09JG37IgTB6n3ouX9 github.com/coder/retry v1.3.1-0.20230210155434-e90a2e1e091d/go.mod h1:r+1J5i/989wt6CUeNSuvFKKA9hHuKKPMxdzDbTuvwwk= github.com/coder/ssh v0.0.0-20220811105153-fcea99919338 h1:tN5GKFT68YLVzJoA8AHuiMNJ0qlhoD3pGN3JY9gxSko= github.com/coder/ssh v0.0.0-20220811105153-fcea99919338/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914= -github.com/coder/tailscale v1.1.1-0.20230411160749-27a458a0ac0a h1:kgfkNHT0yiDAfs5AKwxICqsFWeiHD/pR+bd0w20LXYI= -github.com/coder/tailscale v1.1.1-0.20230411160749-27a458a0ac0a/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA= +github.com/coder/tailscale v1.1.1-0.20230418202606-ed9307cf1b22 h1:bvGOqnI0ITbwOZFQ0SZ4MBw/8LLUEjxmNu57XEujrfQ= +github.com/coder/tailscale v1.1.1-0.20230418202606-ed9307cf1b22/go.mod h1:jpg+77g19FpXL43U1VoIqoSg1K/Vh5CVxycGldQ8KhA= github.com/coder/terraform-provider-coder v0.6.23 h1:O2Rcj0umez4DfVdGnKZi63z1Xzxd0IQOn9VQDB8YU8g= github.com/coder/terraform-provider-coder v0.6.23/go.mod h1:UIfU3bYNeSzJJvHyJ30tEKjD6Z9utloI+HUM/7n94CY= github.com/coder/wgtunnel v0.1.5 h1:WP3sCj/3iJ34eKvpMQEp1oJHvm24RYh0NHbj1kfUKfs= diff --git a/tailnet/conn.go b/tailnet/conn.go index 2c99a83e86331..e5f422cb973c8 100644 --- a/tailnet/conn.go +++ b/tailnet/conn.go @@ -17,6 +17,7 @@ import ( "github.com/google/uuid" "go4.org/netipx" "golang.org/x/xerrors" + "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "tailscale.com/hostinfo" "tailscale.com/ipn/ipnstate" @@ -44,6 +45,12 @@ import ( "github.com/coder/coder/cryptorand" ) +const ( + WorkspaceAgentSSHPort = 1 + WorkspaceAgentReconnectingPTYPort = 2 + WorkspaceAgentSpeedtestPort = 3 +) + func init() { // Globally disable network namespacing. All networking happens in // userspace. @@ -267,6 +274,7 @@ func NewConn(options *Options) (conn *Conn, err error) { server.sendNode() }) netStack.ForwardTCPIn = server.forwardTCP + netStack.ForwardTCPSockOpts = server.forwardTCPSockOpts err = netStack.Start(nil) if err != nil { @@ -301,17 +309,16 @@ type Conn struct { logger slog.Logger blockEndpoints bool - dialer *tsdial.Dialer - tunDevice *tstun.Wrapper - peerMap map[tailcfg.NodeID]*tailcfg.Node - netMap *netmap.NetworkMap - netStack *netstack.Impl - magicConn *magicsock.Conn - wireguardMonitor *monitor.Mon - wireguardRouter *router.Config - wireguardEngine wgengine.Engine - listeners map[listenKey]*listener - forwardTCPCallback func(conn net.Conn, listenerExists bool) net.Conn + dialer *tsdial.Dialer + tunDevice *tstun.Wrapper + peerMap map[tailcfg.NodeID]*tailcfg.Node + netMap *netmap.NetworkMap + netStack *netstack.Impl + magicConn *magicsock.Conn + wireguardMonitor *monitor.Mon + wireguardRouter *router.Config + wireguardEngine wgengine.Engine + listeners map[listenKey]*listener lastMutex sync.Mutex nodeSending bool @@ -327,17 +334,6 @@ type Conn struct { trafficStats *connstats.Statistics } -// SetForwardTCPCallback is called every time a TCP connection is initiated inbound. -// listenerExists is true if a listener is registered for the target port. If there -// isn't one, traffic is forwarded to the local listening port. -// -// This allows wrapping a Conn to track reads and writes. -func (c *Conn) SetForwardTCPCallback(callback func(conn net.Conn, listenerExists bool) net.Conn) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.forwardTCPCallback = callback -} - func (c *Conn) SetNodeCallback(callback func(node *Node)) { c.lastMutex.Lock() c.nodeCallback = callback @@ -699,12 +695,11 @@ func (c *Conn) selfNode() *Node { // This and below is taken _mostly_ verbatim from Tailscale: // https://github.com/tailscale/tailscale/blob/c88bd53b1b7b2fcf7ba302f2e53dd1ce8c32dad4/tsnet/tsnet.go#L459-L494 -// Listen announces only on the Tailscale network. -// It will start the server if it has not been started yet. +// Listen listens for connections only on the Tailscale network. func (c *Conn) Listen(network, addr string) (net.Listener, error) { host, port, err := net.SplitHostPort(addr) if err != nil { - return nil, xerrors.Errorf("wgnet: %w", err) + return nil, xerrors.Errorf("tailnet: split host port for listen: %w", err) } lk := listenKey{network, host, port} ln := &listener{ @@ -725,7 +720,7 @@ func (c *Conn) Listen(network, addr string) (net.Listener, error) { } if _, ok := c.listeners[lk]; ok { c.mutex.Unlock() - return nil, xerrors.Errorf("wgnet: listener already open for %s, %s", network, addr) + return nil, xerrors.Errorf("tailnet: listener already open for %s, %s", network, addr) } c.listeners[lk] = ln c.mutex.Unlock() @@ -743,14 +738,12 @@ func (c *Conn) DialContextUDP(ctx context.Context, ipp netip.AddrPort) (*gonet.U func (c *Conn) forwardTCP(conn net.Conn, port uint16) { c.mutex.Lock() ln, ok := c.listeners[listenKey{"tcp", "", fmt.Sprint(port)}] - if c.forwardTCPCallback != nil { - conn = c.forwardTCPCallback(conn, ok) - } c.mutex.Unlock() if !ok { c.forwardTCPToLocal(conn, port) return } + t := time.NewTimer(time.Second) defer t.Stop() select { @@ -763,6 +756,18 @@ func (c *Conn) forwardTCP(conn net.Conn, port uint16) { _ = conn.Close() } +func (*Conn) forwardTCPSockOpts(port uint16) []tcpip.SettableSocketOption { + opts := []tcpip.SettableSocketOption{} + + // See: https://github.com/tailscale/tailscale/blob/c7cea825aea39a00aca71ea02bab7266afc03e7c/wgengine/netstack/netstack.go#L888 + if port == WorkspaceAgentSSHPort || port == 22 { + opt := tcpip.KeepaliveIdleOption(72 * time.Hour) + opts = append(opts, &opt) + } + + return opts +} + func (c *Conn) forwardTCPToLocal(conn net.Conn, port uint16) { defer conn.Close() dialAddrStr := net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port))) @@ -842,7 +847,7 @@ func (ln *listener) Accept() (net.Conn, error) { select { case c = <-ln.conn: case <-ln.closed: - return nil, xerrors.Errorf("wgnet: %w", net.ErrClosed) + return nil, xerrors.Errorf("tailnet: %w", net.ErrClosed) } return c, nil }