@@ -106,7 +106,8 @@ func NewServer(ctx context.Context, logger slog.Logger, fs afero.Fs, maxTimeout
106
106
"session" : ssh .DefaultSessionHandler ,
107
107
},
108
108
ConnectionFailedCallback : func (_ net.Conn , err error ) {
109
- s .logger .Info (ctx , "ssh connection ended" , slog .Error (err ))
109
+ s .logger .Warn (ctx , "ssh connection failed" , slog .Error (err ))
110
+ metricConnectionFailedCallback .Add (1 )
110
111
},
111
112
Handler : s .sessionHandler ,
112
113
HostSigners : []ssh.Signer {randomSigner },
@@ -115,16 +116,19 @@ func NewServer(ctx context.Context, logger slog.Logger, fs afero.Fs, maxTimeout
115
116
s .logger .Debug (ctx , "local port forward" ,
116
117
slog .F ("destination-host" , destinationHost ),
117
118
slog .F ("destination-port" , destinationPort ))
119
+ metricLocalPortForwardingCallback .Add (1 )
118
120
return true
119
121
},
120
122
PtyCallback : func (ctx ssh.Context , pty ssh.Pty ) bool {
123
+ metricPtyCallback .Add (1 )
121
124
return true
122
125
},
123
126
ReversePortForwardingCallback : func (ctx ssh.Context , bindHost string , bindPort uint32 ) bool {
124
127
// Allow reverse port forwarding all!
125
128
s .logger .Debug (ctx , "local port forward" ,
126
129
slog .F ("bind-host" , bindHost ),
127
130
slog .F ("bind-port" , bindPort ))
131
+ metricReversePortForwardingCallback .Add (1 )
128
132
return true
129
133
},
130
134
RequestHandlers : map [string ]ssh.RequestHandler {
@@ -205,6 +209,7 @@ func (s *Server) sessionHandler(session ssh.Session) {
205
209
s .logger .Warn (ctx , "ssh session failed" , slog .Error (err ))
206
210
// This exit code is designed to be unlikely to be confused for a legit exit code
207
211
// from the process.
212
+ metricsSessionError .Add (1 )
208
213
_ = session .Exit (MagicSessionErrorCode )
209
214
return
210
215
}
@@ -238,12 +243,14 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er
238
243
239
244
cmd , err := s .CreateCommand (ctx , session .RawCommand (), env )
240
245
if err != nil {
246
+ metricsAgentCreateCommandError .Add (1 )
241
247
return err
242
248
}
243
249
244
250
if ssh .AgentRequested (session ) {
245
251
l , err := ssh .NewAgentListener ()
246
252
if err != nil {
253
+ metricsAgentListenerError .Add (1 )
247
254
return xerrors .Errorf ("new agent listener: %w" , err )
248
255
}
249
256
defer l .Close ()
@@ -259,20 +266,27 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er
259
266
}
260
267
261
268
func startNonPTYSession (session ssh.Session , cmd * exec.Cmd ) error {
269
+ metricsStartNonPTYSession .Add (1 )
270
+
262
271
cmd .Stdout = session
263
272
cmd .Stderr = session .Stderr ()
264
273
// This blocks forever until stdin is received if we don't
265
274
// use StdinPipe. It's unknown what causes this.
266
275
stdinPipe , err := cmd .StdinPipe ()
267
276
if err != nil {
277
+ metricsNonPTYStdinPipeError .Add (1 )
268
278
return xerrors .Errorf ("create stdin pipe: %w" , err )
269
279
}
270
280
go func () {
271
- _ , _ = io .Copy (stdinPipe , session )
281
+ _ , err := io .Copy (stdinPipe , session )
282
+ if err != nil {
283
+ metricsNonPTYStdinIoCopyError .Add (1 )
284
+ }
272
285
_ = stdinPipe .Close ()
273
286
}()
274
287
err = cmd .Start ()
275
288
if err != nil {
289
+ metricsNonPTYCmdStartError .Add (1 )
276
290
return xerrors .Errorf ("start: %w" , err )
277
291
}
278
292
return cmd .Wait ()
@@ -288,6 +302,8 @@ type ptySession interface {
288
302
}
289
303
290
304
func (s * Server ) startPTYSession (session ptySession , cmd * pty.Cmd , sshPty ssh.Pty , windowSize <- chan ssh.Window ) (retErr error ) {
305
+ metricsStartPTYSession .Add (1 )
306
+
291
307
ctx := session .Context ()
292
308
// Disable minimal PTY emulation set by gliderlabs/ssh (NL-to-CRNL).
293
309
// See https://github.com/coder/coder/issues/3371.
@@ -299,6 +315,7 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
299
315
err := showMOTD (session , manifest .MOTDFile )
300
316
if err != nil {
301
317
s .logger .Error (ctx , "show MOTD" , slog .Error (err ))
318
+ metricsPTYMotdError .Add (1 )
302
319
}
303
320
} else {
304
321
s .logger .Warn (ctx , "metadata lookup failed, unable to show MOTD" )
@@ -313,12 +330,14 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
313
330
pty .WithLogger (slog .Stdlib (ctx , s .logger , slog .LevelInfo )),
314
331
))
315
332
if err != nil {
333
+ metricsPTYCmdStartError .Add (1 )
316
334
return xerrors .Errorf ("start command: %w" , err )
317
335
}
318
336
defer func () {
319
337
closeErr := ptty .Close ()
320
338
if closeErr != nil {
321
339
s .logger .Warn (ctx , "failed to close tty" , slog .Error (closeErr ))
340
+ metricsPTYCloseError .Add (1 )
322
341
if retErr == nil {
323
342
retErr = closeErr
324
343
}
@@ -330,12 +349,16 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
330
349
// If the pty is closed, then command has exited, no need to log.
331
350
if resizeErr != nil && ! errors .Is (resizeErr , pty .ErrClosed ) {
332
351
s .logger .Warn (ctx , "failed to resize tty" , slog .Error (resizeErr ))
352
+ metricsPTYResizeError .Add (1 )
333
353
}
334
354
}
335
355
}()
336
356
337
357
go func () {
338
- _ , _ = io .Copy (ptty .InputWriter (), session )
358
+ _ , err := io .Copy (ptty .InputWriter (), session )
359
+ if err != nil {
360
+ metricsPTYInputIoCopyError .Add (1 )
361
+ }
339
362
}()
340
363
341
364
// We need to wait for the command output to finish copying. It's safe to
@@ -349,6 +372,7 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
349
372
n , err := io .Copy (session , ptty .OutputReader ())
350
373
s .logger .Debug (ctx , "copy output done" , slog .F ("bytes" , n ), slog .Error (err ))
351
374
if err != nil {
375
+ metricsPTYOutputIoCopyError .Add (1 )
352
376
return xerrors .Errorf ("copy error: %w" , err )
353
377
}
354
378
// We've gotten all the output, but we need to wait for the process to
@@ -360,6 +384,7 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
360
384
// and not something to be concerned about. But, if it's something else, we should log it.
361
385
if err != nil && ! xerrors .As (err , & exitErr ) {
362
386
s .logger .Warn (ctx , "wait error" , slog .Error (err ))
387
+ metricsPTYWaitError .Add (1 )
363
388
}
364
389
if err != nil {
365
390
return xerrors .Errorf ("process wait: %w" , err )
@@ -368,6 +393,8 @@ func (s *Server) startPTYSession(session ptySession, cmd *pty.Cmd, sshPty ssh.Pt
368
393
}
369
394
370
395
func (s * Server ) sftpHandler (session ssh.Session ) {
396
+ metricSftpHandler .Add (1 )
397
+
371
398
ctx := session .Context ()
372
399
373
400
// Typically sftp sessions don't request a TTY, but if they do,
@@ -407,6 +434,7 @@ func (s *Server) sftpHandler(session ssh.Session) {
407
434
return
408
435
}
409
436
s .logger .Warn (ctx , "sftp server closed with error" , slog .Error (err ))
437
+ metricSftpServerError .Add (1 )
410
438
_ = session .Exit (1 )
411
439
}
412
440
0 commit comments