xrfang
xrfang

Reputation: 2322

How to implement ssh session logging in Go?

I am writing a simple bastion service which will log input/output of a ssh session. The ssh connection part looks like:

func connect(user, host string, logger sessionLogger) error {
    cmd := exec.Command("ssh", user+"@"+host)
    t, err := pty.Start(cmd)
    if err != nil {
        return err
    }
    defer t.Close()
    go func() {
        buf := make([]byte, 1024)
        for {
            n, err := t.Read(buf)
            if err != nil {
                break
            }
            os.Stdout.Write(buf[:n])
            logger(buf[:n])
        }
    }()
    go func() { io.Copy(t, os.Stdin) }()
    return cmd.Wait()
}

The problem is all commands entered by user is echoed to the PTY twice, like this:

enter image description here

I have tried to play with the stdin/stdout of a cmd, including not using pty at all, but having various strange problems, e.g. no output of command prompt etc. most of them related to the nature of stdout of a Command (it must be a pty).

This is the best result I have now. But I do need to eliminate the excessive echo of command.

Upvotes: 1

Views: 58

Answers (1)

Philippe
Philippe

Reputation: 26727

You need to use raw mode to prevent duplicate input

    // import "golang.org/x/term"
    ...
    defer t.Close()

    oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
    if err != nil {
        return err
    }
    defer term.Restore(int(os.Stdin.Fd()), oldState)
    ...

Upvotes: 1

Related Questions