Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 07d5dc8

Browse files
committed
fix(tailnet): update WorkspaceAgentSSHPort to 22
Change-Id: Ifd986b260f8ac317e37d65111cd4e0bd1dc38af8 Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent cccdf1e commit 07d5dc8

File tree

5 files changed

+141
-16
lines changed

5 files changed

+141
-16
lines changed

agent/agent.go

+14-11
Original file line numberDiff line numberDiff line change
@@ -1360,19 +1360,22 @@ func (a *agent) createTailnet(
13601360
return nil, xerrors.Errorf("update host signer: %w", err)
13611361
}
13621362

1363-
sshListener, err := network.Listen("tcp", ":"+strconv.Itoa(workspacesdk.AgentSSHPort))
1364-
if err != nil {
1365-
return nil, xerrors.Errorf("listen on the ssh port: %w", err)
1366-
}
1367-
defer func() {
1363+
for _, port := range []int{workspacesdk.AgentSSHPort, workspacesdk.AgentStandardSSHPort} {
1364+
sshListener, err := network.Listen("tcp", ":"+strconv.Itoa(port))
13681365
if err != nil {
1369-
_ = sshListener.Close()
1366+
return nil, xerrors.Errorf("listen on the ssh port (%v): %w", port, err)
1367+
}
1368+
// nolint:revive // We do want to run the deferred functions when createTailnet returns.
1369+
defer func() {
1370+
if err != nil {
1371+
_ = sshListener.Close()
1372+
}
1373+
}()
1374+
if err = a.trackGoroutine(func() {
1375+
_ = a.sshServer.Serve(sshListener)
1376+
}); err != nil {
1377+
return nil, err
13701378
}
1371-
}()
1372-
if err = a.trackGoroutine(func() {
1373-
_ = a.sshServer.Serve(sshListener)
1374-
}); err != nil {
1375-
return nil, err
13761379
}
13771380

13781381
reconnectingPTYListener, err := network.Listen("tcp", ":"+strconv.Itoa(workspacesdk.AgentReconnectingPTYPort))

cli/configssh_test.go

+116-2
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,118 @@ func TestConfigSSH(t *testing.T) {
169169
<-copyDone
170170
}
171171

172+
func TestConfigSSHOnPort22(t *testing.T) {
173+
t.Parallel()
174+
175+
if runtime.GOOS == "windows" {
176+
t.Skip("See coder/internal#117")
177+
}
178+
179+
const hostname = "test-coder."
180+
const expectedKey = "ConnectionAttempts"
181+
const removeKey = "ConnectTimeout"
182+
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
183+
ConfigSSH: codersdk.SSHConfigResponse{
184+
HostnamePrefix: hostname,
185+
SSHConfigOptions: map[string]string{
186+
// Something we can test for
187+
expectedKey: "3",
188+
removeKey: "",
189+
},
190+
},
191+
})
192+
owner := coderdtest.CreateFirstUser(t, client)
193+
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
194+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
195+
OrganizationID: owner.OrganizationID,
196+
OwnerID: memberUser.ID,
197+
}).WithAgent().Do()
198+
_ = agenttest.New(t, client.URL, r.AgentToken)
199+
resources := coderdtest.AwaitWorkspaceAgents(t, client, r.Workspace.ID)
200+
agentConn, err := workspacesdk.New(client).
201+
DialAgent(context.Background(), resources[0].Agents[0].ID, nil)
202+
require.NoError(t, err)
203+
defer agentConn.Close()
204+
205+
listener, err := net.Listen("tcp", "127.0.0.1:0")
206+
require.NoError(t, err)
207+
defer func() {
208+
_ = listener.Close()
209+
}()
210+
copyDone := make(chan struct{})
211+
go func() {
212+
defer close(copyDone)
213+
var wg sync.WaitGroup
214+
for {
215+
conn, err := listener.Accept()
216+
if err != nil {
217+
break
218+
}
219+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
220+
ssh, err := agentConn.SSHOnPort(ctx, workspacesdk.AgentStandardSSHPort)
221+
cancel()
222+
assert.NoError(t, err)
223+
wg.Add(2)
224+
go func() {
225+
defer wg.Done()
226+
_, _ = io.Copy(conn, ssh)
227+
}()
228+
go func() {
229+
defer wg.Done()
230+
_, _ = io.Copy(ssh, conn)
231+
}()
232+
}
233+
wg.Wait()
234+
}()
235+
236+
sshConfigFile := sshConfigFileName(t)
237+
238+
tcpAddr, valid := listener.Addr().(*net.TCPAddr)
239+
require.True(t, valid)
240+
inv, root := clitest.New(t, "config-ssh",
241+
"--ssh-option", "HostName "+tcpAddr.IP.String(),
242+
"--ssh-option", "Port "+strconv.Itoa(tcpAddr.Port),
243+
"--ssh-config-file", sshConfigFile,
244+
"--skip-proxy-command")
245+
clitest.SetupConfig(t, member, root)
246+
pty := ptytest.New(t)
247+
inv.Stdin = pty.Input()
248+
inv.Stdout = pty.Output()
249+
250+
waiter := clitest.StartWithWaiter(t, inv)
251+
252+
matches := []struct {
253+
match, write string
254+
}{
255+
{match: "Continue?", write: "yes"},
256+
}
257+
for _, m := range matches {
258+
pty.ExpectMatch(m.match)
259+
pty.WriteLine(m.write)
260+
}
261+
262+
waiter.RequireSuccess()
263+
264+
fileContents, err := os.ReadFile(sshConfigFile)
265+
require.NoError(t, err, "read ssh config file")
266+
require.Contains(t, string(fileContents), expectedKey, "ssh config file contains expected key")
267+
require.NotContains(t, string(fileContents), removeKey, "ssh config file should not have removed key")
268+
269+
home := filepath.Dir(filepath.Dir(sshConfigFile))
270+
// #nosec
271+
sshCmd := exec.Command("ssh", "-F", sshConfigFile, hostname+r.Workspace.Name, "echo", "test")
272+
pty = ptytest.New(t)
273+
// Set HOME because coder config is included from ~/.ssh/coder.
274+
sshCmd.Env = append(sshCmd.Env, fmt.Sprintf("HOME=%s", home))
275+
inv.Stderr = pty.Output()
276+
data, err := sshCmd.Output()
277+
require.NoError(t, err)
278+
require.Equal(t, "test", strings.TrimSpace(string(data)))
279+
280+
_ = listener.Close()
281+
<-copyDone
282+
}
283+
172284
func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
173285
t.Parallel()
174286

@@ -317,7 +429,8 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
317429
strings.Join([]string{
318430
headerEnd,
319431
"",
320-
}, "\n")},
432+
}, "\n"),
433+
},
321434
},
322435
args: []string{"--ssh-option", "ForwardAgent=yes"},
323436
matches: []match{
@@ -342,7 +455,8 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
342455
strings.Join([]string{
343456
headerEnd,
344457
"",
345-
}, "\n")},
458+
}, "\n"),
459+
},
346460
},
347461
args: []string{"--ssh-option", "ForwardAgent=yes"},
348462
matches: []match{

codersdk/workspacesdk/agentconn.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,21 @@ func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, w
165165
// SSH pipes the SSH protocol over the returned net.Conn.
166166
// This connects to the built-in SSH server in the workspace agent.
167167
func (c *AgentConn) SSH(ctx context.Context) (*gonet.TCPConn, error) {
168+
return c.SSHOnPort(ctx, AgentSSHPort)
169+
}
170+
171+
// SSHOnPort pipes the SSH protocol over the returned net.Conn.
172+
// This connects to the built-in SSH server in the workspace agent on the specified port.
173+
func (c *AgentConn) SSHOnPort(ctx context.Context, port uint16) (*gonet.TCPConn, error) {
168174
ctx, span := tracing.StartSpan(ctx)
169175
defer span.End()
170176

171177
if !c.AwaitReachable(ctx) {
172178
return nil, xerrors.Errorf("workspace agent not reachable in time: %v", ctx.Err())
173179
}
174180

175-
c.Conn.SendConnectedTelemetry(c.agentAddress(), tailnet.TelemetryApplicationSSH)
176-
return c.Conn.DialContextTCP(ctx, netip.AddrPortFrom(c.agentAddress(), AgentSSHPort))
181+
c.SendConnectedTelemetry(c.agentAddress(), tailnet.TelemetryApplicationSSH)
182+
return c.DialContextTCP(ctx, netip.AddrPortFrom(c.agentAddress(), port))
177183
}
178184

179185
// SSHClient calls SSH to create a client that uses a weak cipher

codersdk/workspacesdk/workspacesdk.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var ErrSkipClose = xerrors.New("skip tailnet close")
3131

3232
const (
3333
AgentSSHPort = tailnet.WorkspaceAgentSSHPort
34+
AgentStandardSSHPort = tailnet.WorkspaceAgentStandardSSHPort
3435
AgentReconnectingPTYPort = tailnet.WorkspaceAgentReconnectingPTYPort
3536
AgentSpeedtestPort = tailnet.WorkspaceAgentSpeedtestPort
3637
// AgentHTTPAPIServerPort serves a HTTP server with endpoints for e.g.

tailnet/conn.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const (
5252
WorkspaceAgentSSHPort = 1
5353
WorkspaceAgentReconnectingPTYPort = 2
5454
WorkspaceAgentSpeedtestPort = 3
55+
WorkspaceAgentStandardSSHPort = 22
5556
)
5657

5758
// EnvMagicsockDebugLogging enables super-verbose logging for the magicsock
@@ -745,7 +746,7 @@ func (c *Conn) forwardTCP(src, dst netip.AddrPort) (handler func(net.Conn), opts
745746
return nil, nil, false
746747
}
747748
// See: https://github.com/tailscale/tailscale/blob/c7cea825aea39a00aca71ea02bab7266afc03e7c/wgengine/netstack/netstack.go#L888
748-
if dst.Port() == WorkspaceAgentSSHPort || dst.Port() == 22 {
749+
if dst.Port() == WorkspaceAgentSSHPort || dst.Port() == WorkspaceAgentStandardSSHPort {
749750
opt := tcpip.KeepaliveIdleOption(72 * time.Hour)
750751
opts = append(opts, &opt)
751752
}

0 commit comments

Comments
 (0)