Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit15a0b1f

Browse files
authored
feat(agent): add second SSH listener on port 22 (#16627)
Fixes:coder/internal#377Added an additional SSH listener on port 22, so the agent now listens on both, port one and port 22.---Change-Id: Ifd986b260f8ac317e37d65111cd4e0bd1dc38af8Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent114cf57 commit15a0b1f

File tree

6 files changed

+153
-95
lines changed

6 files changed

+153
-95
lines changed

‎agent/agent.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,19 +1193,22 @@ func (a *agent) createTailnet(
11931193
returnnil,xerrors.Errorf("update host signer: %w",err)
11941194
}
11951195

1196-
sshListener,err:=network.Listen("tcp",":"+strconv.Itoa(workspacesdk.AgentSSHPort))
1197-
iferr!=nil {
1198-
returnnil,xerrors.Errorf("listen on the ssh port: %w",err)
1199-
}
1200-
deferfunc() {
1196+
for_,port:=range []int{workspacesdk.AgentSSHPort,workspacesdk.AgentStandardSSHPort} {
1197+
sshListener,err:=network.Listen("tcp",":"+strconv.Itoa(port))
12011198
iferr!=nil {
1202-
_=sshListener.Close()
1199+
returnnil,xerrors.Errorf("listen on the ssh port (%v): %w",port,err)
1200+
}
1201+
// nolint:revive // We do want to run the deferred functions when createTailnet returns.
1202+
deferfunc() {
1203+
iferr!=nil {
1204+
_=sshListener.Close()
1205+
}
1206+
}()
1207+
iferr=a.trackGoroutine(func() {
1208+
_=a.sshServer.Serve(sshListener)
1209+
});err!=nil {
1210+
returnnil,err
12031211
}
1204-
}()
1205-
iferr=a.trackGoroutine(func() {
1206-
_=a.sshServer.Serve(sshListener)
1207-
});err!=nil {
1208-
returnnil,err
12091212
}
12101213

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

‎agent/agent_test.go

Lines changed: 120 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -61,38 +61,48 @@ func TestMain(m *testing.M) {
6161
goleak.VerifyTestMain(m,testutil.GoleakOptions...)
6262
}
6363

64+
varsshPorts= []uint16{workspacesdk.AgentSSHPort,workspacesdk.AgentStandardSSHPort}
65+
6466
// NOTE: These tests only work when your default shell is bash for some reason.
6567

6668
funcTestAgent_Stats_SSH(t*testing.T) {
6769
t.Parallel()
68-
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
69-
defercancel()
7070

71-
//nolint:dogsled
72-
conn,_,stats,_,_:=setupAgent(t, agentsdk.Manifest{},0)
71+
for_,port:=rangesshPorts {
72+
port:=port
73+
t.Run(fmt.Sprintf("(:%d)",port),func(t*testing.T) {
74+
t.Parallel()
7375

74-
sshClient,err:=conn.SSHClient(ctx)
75-
require.NoError(t,err)
76-
defersshClient.Close()
77-
session,err:=sshClient.NewSession()
78-
require.NoError(t,err)
79-
defersession.Close()
80-
stdin,err:=session.StdinPipe()
81-
require.NoError(t,err)
82-
err=session.Shell()
83-
require.NoError(t,err)
76+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
77+
defercancel()
8478

85-
vars*proto.Stats
86-
require.Eventuallyf(t,func()bool {
87-
varokbool
88-
s,ok=<-stats
89-
returnok&&s.ConnectionCount>0&&s.RxBytes>0&&s.TxBytes>0&&s.SessionCountSsh==1
90-
},testutil.WaitLong,testutil.IntervalFast,
91-
"never saw stats: %+v",s,
92-
)
93-
_=stdin.Close()
94-
err=session.Wait()
95-
require.NoError(t,err)
79+
//nolint:dogsled
80+
conn,_,stats,_,_:=setupAgent(t, agentsdk.Manifest{},0)
81+
82+
sshClient,err:=conn.SSHClientOnPort(ctx,port)
83+
require.NoError(t,err)
84+
defersshClient.Close()
85+
session,err:=sshClient.NewSession()
86+
require.NoError(t,err)
87+
defersession.Close()
88+
stdin,err:=session.StdinPipe()
89+
require.NoError(t,err)
90+
err=session.Shell()
91+
require.NoError(t,err)
92+
93+
vars*proto.Stats
94+
require.Eventuallyf(t,func()bool {
95+
varokbool
96+
s,ok=<-stats
97+
returnok&&s.ConnectionCount>0&&s.RxBytes>0&&s.TxBytes>0&&s.SessionCountSsh==1
98+
},testutil.WaitLong,testutil.IntervalFast,
99+
"never saw stats: %+v",s,
100+
)
101+
_=stdin.Close()
102+
err=session.Wait()
103+
require.NoError(t,err)
104+
})
105+
}
96106
}
97107

98108
funcTestAgent_Stats_ReconnectingPTY(t*testing.T) {
@@ -266,15 +276,23 @@ func TestAgent_Stats_Magic(t *testing.T) {
266276

267277
funcTestAgent_SessionExec(t*testing.T) {
268278
t.Parallel()
269-
session:=setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{},nil)
270279

271-
command:="echo test"
272-
ifruntime.GOOS=="windows" {
273-
command="cmd.exe /c echo test"
280+
for_,port:=rangesshPorts {
281+
port:=port
282+
t.Run(fmt.Sprintf("(:%d)",port),func(t*testing.T) {
283+
t.Parallel()
284+
285+
session:=setupSSHSessionOnPort(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{},nil,port)
286+
287+
command:="echo test"
288+
ifruntime.GOOS=="windows" {
289+
command="cmd.exe /c echo test"
290+
}
291+
output,err:=session.Output(command)
292+
require.NoError(t,err)
293+
require.Equal(t,"test",strings.TrimSpace(string(output)))
294+
})
274295
}
275-
output,err:=session.Output(command)
276-
require.NoError(t,err)
277-
require.Equal(t,"test",strings.TrimSpace(string(output)))
278296
}
279297

280298
//nolint:tparallel // Sub tests need to run sequentially.
@@ -384,25 +402,33 @@ func TestAgent_SessionTTYShell(t *testing.T) {
384402
// it seems like it could be either.
385403
t.Skip("ConPTY appears to be inconsistent on Windows.")
386404
}
387-
session:=setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{},nil)
388-
command:="sh"
389-
ifruntime.GOOS=="windows" {
390-
command="cmd.exe"
405+
406+
for_,port:=rangesshPorts {
407+
port:=port
408+
t.Run(fmt.Sprintf("(%d)",port),func(t*testing.T) {
409+
t.Parallel()
410+
411+
session:=setupSSHSessionOnPort(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{},nil,port)
412+
command:="sh"
413+
ifruntime.GOOS=="windows" {
414+
command="cmd.exe"
415+
}
416+
err:=session.RequestPty("xterm",128,128, ssh.TerminalModes{})
417+
require.NoError(t,err)
418+
ptty:=ptytest.New(t)
419+
session.Stdout=ptty.Output()
420+
session.Stderr=ptty.Output()
421+
session.Stdin=ptty.Input()
422+
err=session.Start(command)
423+
require.NoError(t,err)
424+
_=ptty.Peek(ctx,1)// wait for the prompt
425+
ptty.WriteLine("echo test")
426+
ptty.ExpectMatch("test")
427+
ptty.WriteLine("exit")
428+
err=session.Wait()
429+
require.NoError(t,err)
430+
})
391431
}
392-
err:=session.RequestPty("xterm",128,128, ssh.TerminalModes{})
393-
require.NoError(t,err)
394-
ptty:=ptytest.New(t)
395-
session.Stdout=ptty.Output()
396-
session.Stderr=ptty.Output()
397-
session.Stdin=ptty.Input()
398-
err=session.Start(command)
399-
require.NoError(t,err)
400-
_=ptty.Peek(ctx,1)// wait for the prompt
401-
ptty.WriteLine("echo test")
402-
ptty.ExpectMatch("test")
403-
ptty.WriteLine("exit")
404-
err=session.Wait()
405-
require.NoError(t,err)
406432
}
407433

408434
funcTestAgent_SessionTTYExitCode(t*testing.T) {
@@ -596,37 +622,41 @@ func TestAgent_Session_TTY_MOTD_Update(t *testing.T) {
596622
//nolint:dogsled // Allow the blank identifiers.
597623
conn,client,_,_,_:=setupAgent(t, agentsdk.Manifest{},0,setSBInterval)
598624

599-
sshClient,err:=conn.SSHClient(ctx)
600-
require.NoError(t,err)
601-
t.Cleanup(func() {
602-
_=sshClient.Close()
603-
})
604-
605625
//nolint:paralleltest // These tests need to swap the banner func.
606-
fori,test:=rangetests {
607-
test:=test
608-
t.Run(fmt.Sprintf("%d",i),func(t*testing.T) {
609-
// Set new banner func and wait for the agent to call it to update the
610-
// banner.
611-
ready:=make(chanstruct{},2)
612-
client.SetAnnouncementBannersFunc(func() ([]codersdk.BannerConfig,error) {
613-
select {
614-
caseready<-struct{}{}:
615-
default:
616-
}
617-
return []codersdk.BannerConfig{test.banner},nil
618-
})
619-
<-ready
620-
<-ready// Wait for two updates to ensure the value has propagated.
621-
622-
session,err:=sshClient.NewSession()
623-
require.NoError(t,err)
624-
t.Cleanup(func() {
625-
_=session.Close()
626-
})
626+
for_,port:=rangesshPorts {
627+
port:=port
627628

628-
testSessionOutput(t,session,test.expected,test.unexpected,nil)
629+
sshClient,err:=conn.SSHClientOnPort(ctx,port)
630+
require.NoError(t,err)
631+
t.Cleanup(func() {
632+
_=sshClient.Close()
629633
})
634+
635+
fori,test:=rangetests {
636+
test:=test
637+
t.Run(fmt.Sprintf("(:%d)/%d",port,i),func(t*testing.T) {
638+
// Set new banner func and wait for the agent to call it to update the
639+
// banner.
640+
ready:=make(chanstruct{},2)
641+
client.SetAnnouncementBannersFunc(func() ([]codersdk.BannerConfig,error) {
642+
select {
643+
caseready<-struct{}{}:
644+
default:
645+
}
646+
return []codersdk.BannerConfig{test.banner},nil
647+
})
648+
<-ready
649+
<-ready// Wait for two updates to ensure the value has propagated.
650+
651+
session,err:=sshClient.NewSession()
652+
require.NoError(t,err)
653+
t.Cleanup(func() {
654+
_=session.Close()
655+
})
656+
657+
testSessionOutput(t,session,test.expected,test.unexpected,nil)
658+
})
659+
}
630660
}
631661
}
632662

@@ -2313,6 +2343,17 @@ func setupSSHSession(
23132343
banner codersdk.BannerConfig,
23142344
prepareFSfunc(fs afero.Fs),
23152345
opts...func(*agenttest.Client,*agent.Options),
2346+
)*ssh.Session {
2347+
returnsetupSSHSessionOnPort(t,manifest,banner,prepareFS,workspacesdk.AgentSSHPort,opts...)
2348+
}
2349+
2350+
funcsetupSSHSessionOnPort(
2351+
t*testing.T,
2352+
manifest agentsdk.Manifest,
2353+
banner codersdk.BannerConfig,
2354+
prepareFSfunc(fs afero.Fs),
2355+
portuint16,
2356+
opts...func(*agenttest.Client,*agent.Options),
23162357
)*ssh.Session {
23172358
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
23182359
defercancel()
@@ -2326,7 +2367,7 @@ func setupSSHSession(
23262367
ifprepareFS!=nil {
23272368
prepareFS(fs)
23282369
}
2329-
sshClient,err:=conn.SSHClient(ctx)
2370+
sshClient,err:=conn.SSHClientOnPort(ctx,port)
23302371
require.NoError(t,err)
23312372
t.Cleanup(func() {
23322373
_=sshClient.Close()

‎agent/usershell/usershell_darwin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func Get(username string) (string, error) {
1717
return"",xerrors.Errorf("username is nonlocal path: %s",username)
1818
}
1919
//nolint: gosec // input checked above
20-
out,_:=exec.Command("dscl",".","-read",filepath.Join("/Users",username),"UserShell").Output()
20+
out,_:=exec.Command("dscl",".","-read",filepath.Join("/Users",username),"UserShell").Output()//nolint:gocritic
2121
s,ok:=strings.CutPrefix(string(out),"UserShell: ")
2222
ifok {
2323
returnstrings.TrimSpace(s),nil

‎codersdk/workspacesdk/agentconn.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,24 +143,36 @@ func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, w
143143
// SSH pipes the SSH protocol over the returned net.Conn.
144144
// This connects to the built-in SSH server in the workspace agent.
145145
func (c*AgentConn)SSH(ctx context.Context) (*gonet.TCPConn,error) {
146+
returnc.SSHOnPort(ctx,AgentSSHPort)
147+
}
148+
149+
// SSHOnPort pipes the SSH protocol over the returned net.Conn.
150+
// This connects to the built-in SSH server in the workspace agent on the specified port.
151+
func (c*AgentConn)SSHOnPort(ctx context.Context,portuint16) (*gonet.TCPConn,error) {
146152
ctx,span:=tracing.StartSpan(ctx)
147153
deferspan.End()
148154

149155
if!c.AwaitReachable(ctx) {
150156
returnnil,xerrors.Errorf("workspace agent not reachable in time: %v",ctx.Err())
151157
}
152158

153-
c.Conn.SendConnectedTelemetry(c.agentAddress(),tailnet.TelemetryApplicationSSH)
154-
returnc.Conn.DialContextTCP(ctx,netip.AddrPortFrom(c.agentAddress(),AgentSSHPort))
159+
c.SendConnectedTelemetry(c.agentAddress(),tailnet.TelemetryApplicationSSH)
160+
returnc.DialContextTCP(ctx,netip.AddrPortFrom(c.agentAddress(),port))
155161
}
156162

157163
// SSHClient calls SSH to create a client that uses a weak cipher
158164
// to improve throughput.
159165
func (c*AgentConn)SSHClient(ctx context.Context) (*ssh.Client,error) {
166+
returnc.SSHClientOnPort(ctx,AgentSSHPort)
167+
}
168+
169+
// SSHClientOnPort calls SSH to create a client on a specific port
170+
// that uses a weak cipher to improve throughput.
171+
func (c*AgentConn)SSHClientOnPort(ctx context.Context,portuint16) (*ssh.Client,error) {
160172
ctx,span:=tracing.StartSpan(ctx)
161173
deferspan.End()
162174

163-
netConn,err:=c.SSH(ctx)
175+
netConn,err:=c.SSHOnPort(ctx,port)
164176
iferr!=nil {
165177
returnnil,xerrors.Errorf("ssh: %w",err)
166178
}

‎codersdk/workspacesdk/workspacesdk.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var ErrSkipClose = xerrors.New("skip tailnet close")
2929

3030
const (
3131
AgentSSHPort=tailnet.WorkspaceAgentSSHPort
32+
AgentStandardSSHPort=tailnet.WorkspaceAgentStandardSSHPort
3233
AgentReconnectingPTYPort=tailnet.WorkspaceAgentReconnectingPTYPort
3334
AgentSpeedtestPort=tailnet.WorkspaceAgentSpeedtestPort
3435
// AgentHTTPAPIServerPort serves a HTTP server with endpoints for e.g.

‎tailnet/conn.go

Lines changed: 2 additions & 1 deletion
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
returnnil,nil,false
746747
}
747748
// See: https://github.com/tailscale/tailscale/blob/c7cea825aea39a00aca71ea02bab7266afc03e7c/wgengine/netstack/netstack.go#L888
748-
ifdst.Port()==WorkspaceAgentSSHPort||dst.Port()==22 {
749+
ifdst.Port()==WorkspaceAgentSSHPort||dst.Port()==WorkspaceAgentStandardSSHPort {
749750
opt:=tcpip.KeepaliveIdleOption(72*time.Hour)
750751
opts=append(opts,&opt)
751752
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp