From bc18f6c1133f0ffb10a7016596dcad909d3a4d73 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 3 Apr 2023 11:20:19 -0500 Subject: [PATCH] fix: add `CODER_AGENT_TAILNET_LISTEN_PORT` for specifying a static tailnet port (#6980) Fixes #5175. --- agent/agent.go | 22 +++++++++++++--------- cli/agent.go | 25 +++++++++++++++++-------- cli/testdata/coder_agent_--help.golden | 3 +++ tailnet/conn.go | 2 ++ 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 1c4fa6d394..e22d5c3576 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -82,6 +82,7 @@ type Options struct { Logger slog.Logger AgentPorts map[int]string SSHMaxTimeout time.Duration + TailnetListenPort uint16 } type Client interface { @@ -118,6 +119,7 @@ func New(options Options) io.Closer { } ctx, cancelFunc := context.WithCancel(context.Background()) a := &agent{ + tailnetListenPort: options.TailnetListenPort, reconnectingPTYTimeout: options.ReconnectingPTYTimeout, logger: options.Logger, closeCancel: cancelFunc, @@ -139,12 +141,13 @@ func New(options Options) io.Closer { } type agent struct { - logger slog.Logger - client Client - exchangeToken func(ctx context.Context) (string, error) - filesystem afero.Fs - logDir string - tempDir string + logger slog.Logger + client Client + exchangeToken func(ctx context.Context) (string, error) + tailnetListenPort uint16 + filesystem afero.Fs + logDir string + tempDir string // ignorePorts tells the api handler which ports to ignore when // listing all listening ports. This is helpful to hide ports that // are used by the agent, that the user does not care about. @@ -606,9 +609,10 @@ func (a *agent) trackConnGoroutine(fn func()) error { func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (_ *tailnet.Conn, err error) { network, err := tailnet.NewConn(&tailnet.Options{ - Addresses: []netip.Prefix{netip.PrefixFrom(codersdk.WorkspaceAgentIP, 128)}, - DERPMap: derpMap, - Logger: a.logger.Named("tailnet"), + Addresses: []netip.Prefix{netip.PrefixFrom(codersdk.WorkspaceAgentIP, 128)}, + DERPMap: derpMap, + Logger: a.logger.Named("tailnet"), + ListenPort: a.tailnetListenPort, }) if err != nil { return nil, xerrors.Errorf("create tailnet: %w", err) diff --git a/cli/agent.go b/cli/agent.go index d9058912e8..706dc45d6c 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -30,11 +30,12 @@ import ( func (r *RootCmd) workspaceAgent() *clibase.Cmd { var ( - auth string - logDir string - pprofAddress string - noReap bool - sshMaxTimeout time.Duration + auth string + logDir string + pprofAddress string + noReap bool + sshMaxTimeout time.Duration + tailnetListenPort int64 ) cmd := &clibase.Cmd{ Use: "agent", @@ -187,9 +188,10 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { } closer := agent.New(agent.Options{ - Client: client, - Logger: logger, - LogDir: logDir, + Client: client, + Logger: logger, + LogDir: logDir, + TailnetListenPort: uint16(tailnetListenPort), ExchangeToken: func(ctx context.Context) (string, error) { if exchangeToken == nil { return client.SDK.SessionToken(), nil @@ -248,6 +250,13 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { Description: "Specify the max timeout for a SSH connection.", Value: clibase.DurationOf(&sshMaxTimeout), }, + { + Flag: "tailnet-listen-port", + Default: "0", + Env: "CODER_AGENT_TAILNET_LISTEN_PORT", + Description: "Specify a static port for Tailscale to use for listening.", + Value: clibase.Int64Of(&tailnetListenPort), + }, } return cmd diff --git a/cli/testdata/coder_agent_--help.golden b/cli/testdata/coder_agent_--help.golden index e1979ef23f..ae15c96c71 100644 --- a/cli/testdata/coder_agent_--help.golden +++ b/cli/testdata/coder_agent_--help.golden @@ -18,5 +18,8 @@ Starts the Coder workspace agent. --ssh-max-timeout duration, $CODER_AGENT_SSH_MAX_TIMEOUT (default: 0) Specify the max timeout for a SSH connection. + --tailnet-listen-port int, $CODER_AGENT_TAILNET_LISTEN_PORT (default: 0) + Specify a static port for Tailscale to use for listening. + --- Run `coder --help` for a list of global options. diff --git a/tailnet/conn.go b/tailnet/conn.go index db259573e0..2c99a83e86 100644 --- a/tailnet/conn.go +++ b/tailnet/conn.go @@ -59,6 +59,7 @@ type Options struct { // If so, only DERPs can establish connections. BlockEndpoints bool Logger slog.Logger + ListenPort uint16 } // NewConn constructs a new Wireguard server that will accept connections from the addresses provided. @@ -137,6 +138,7 @@ func NewConn(options *Options) (conn *Conn, err error) { wireguardEngine, err := wgengine.NewUserspaceEngine(Logger(options.Logger.Named("wgengine")), wgengine.Config{ LinkMonitor: wireguardMonitor, Dialer: dialer, + ListenPort: options.ListenPort, }) if err != nil { return nil, xerrors.Errorf("create wgengine: %w", err)