diff --git a/lib/client/session.go b/lib/client/session.go index facdf02a6b2e3..ffde9db8a0fa4 100644 --- a/lib/client/session.go +++ b/lib/client/session.go @@ -383,7 +383,7 @@ func (ns *NodeSession) allocateTerminal(ctx context.Context, termType string, s go ns.updateTerminalSize(ctx, s) } go func() { - if _, err := io.Copy(os.Stderr, stderr); err != nil { + if _, err := io.Copy(ns.nodeClient.TC.Stderr, stderr); err != nil { log.Debugf("Error reading remote STDERR: %v", err) } }() @@ -529,7 +529,7 @@ func (ns *NodeSession) runCommand(ctx context.Context, mode types.SessionPartici // fallback to non-interactive mode if interactive && !ns.terminal.IsAttached() { interactive = false - fmt.Fprintf(os.Stderr, "TTY will not be allocated on the server because stdin is not a terminal\n") + fmt.Fprintf(ns.nodeClient.TC.Stderr, "TTY will not be allocated on the server because stdin is not a terminal\n") } // Start a interactive session ("exec" request with a TTY). diff --git a/lib/web/terminal.go b/lib/web/terminal.go index 7cbe3e7db5b4a..d01bcbd748e16 100644 --- a/lib/web/terminal.go +++ b/lib/web/terminal.go @@ -404,6 +404,17 @@ func (t *TerminalHandler) handler(ws *websocket.Conn, r *http.Request) { t.log.Debug("Closing websocket stream") } +type stderrWriter struct { + stream *TerminalStream +} + +func (s stderrWriter) Write(b []byte) (int, error) { + if err := s.stream.writeError(string(b)); err != nil { + return 0, trace.Wrap(err) + } + return len(b), nil +} + // makeClient builds a *client.TeleportClient for the connection. func (t *TerminalHandler) makeClient(ctx context.Context, ws *websocket.Conn) (*client.TeleportClient, error) { ctx, span := tracing.DefaultProvider().Tracer("terminal").Start(ctx, "terminal/makeClient") @@ -418,7 +429,7 @@ func (t *TerminalHandler) makeClient(ctx context.Context, ws *websocket.Conn) (* clientConfig.ForwardAgent = client.ForwardAgentLocal clientConfig.Namespace = apidefaults.Namespace clientConfig.Stdout = t.stream - clientConfig.Stderr = t.stream + clientConfig.Stderr = stderrWriter{stream: t.stream} clientConfig.Stdin = t.stream clientConfig.SiteName = t.sessionData.ClusterName if err := clientConfig.ParseProxyHost(t.proxyHostPort); err != nil { @@ -887,7 +898,7 @@ func (t *TerminalHandler) windowChange(ctx context.Context, params *session.Term // writeError displays an error in the terminal window. func (t *TerminalHandler) writeError(err error) { - if writeErr := t.stream.writeError(err); writeErr != nil { + if writeErr := t.stream.writeError(err.Error()); writeErr != nil { t.log.WithError(writeErr).Warnf("Unable to send error to terminal: %v", err) } } @@ -1019,8 +1030,8 @@ type TerminalStream struct { var replacer = strings.NewReplacer("\r\n", "\r\n", "\n", "\r\n") // writeError displays an error in the terminal window. -func (t *WSStream) writeError(err error) error { - _, writeErr := replacer.WriteString(t, err.Error()) +func (t *WSStream) writeError(err string) error { + _, writeErr := replacer.WriteString(t, err) return trace.Wrap(writeErr) }