@@ -169,6 +169,118 @@ func TestConfigSSH(t *testing.T) {
169
169
<- copyDone
170
170
}
171
171
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
+
172
284
func TestConfigSSH_FileWriteAndOptionsFlow (t * testing.T ) {
173
285
t .Parallel ()
174
286
@@ -317,7 +429,8 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
317
429
strings .Join ([]string {
318
430
headerEnd ,
319
431
"" ,
320
- }, "\n " )},
432
+ }, "\n " ),
433
+ },
321
434
},
322
435
args : []string {"--ssh-option" , "ForwardAgent=yes" },
323
436
matches : []match {
@@ -342,7 +455,8 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
342
455
strings .Join ([]string {
343
456
headerEnd ,
344
457
"" ,
345
- }, "\n " )},
458
+ }, "\n " ),
459
+ },
346
460
},
347
461
args : []string {"--ssh-option" , "ForwardAgent=yes" },
348
462
matches : []match {
0 commit comments