2

I have a question regarding how to send input and receive output from a terminal subprocess such as ssh. An example in python would be something like this:

how to give subprocess a password and get stdout at the same time

I cannot find a simple example in Golang that is similar how the above work.

In Golang I would want to do something like this but it does not seem to work:

    cmd := exec.Command("ssh", "user@x.x.x.x")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    stdin, _ := cmd.StdinPipe()
    stdin.Write([]byte("password\n"))
    cmd.Run()

However; I'm not sure how to do this in go because every time i exec this ssh command I am only able to get the output. I am unable to input my password automatically from code. Does anyone have examples of writing to terminal processes such as ssh? If so, please share.

Community
  • 1
  • 1
efel
  • 1,054
  • 3
  • 14
  • 29
  • 2
    You cannot do this very easily with `ssh`, because it will refuse to read the password from `stdin`. See http://stackoverflow.com/q/1340366. Your best option seems to be generating a key pair and authenticating using that. – Attila O. Apr 11 '14 at 18:44
  • 2
    I have tried to do this before in Python. I was only able to get it to work using [Paramiko](http://www.lag.net/paramiko/). You should try with [go.crypto/ssh](http://godoc.org/code.google.com/p/go.crypto/ssh). – deft_code Apr 11 '14 at 20:12
  • you seem to know how to handle stdin and stdout for a subprocess just fine. the problem is not in your code but due to ssh having diffferent behavior when it detects it's not running in a shell or tty. you'll have to use cert authentication instead of a password. – Jeremy Wall Apr 12 '14 at 03:15
  • 1
    does `cmd.Env = append(cmd.Env, 'TERM=xterm')` help? – OneOfOne Apr 13 '14 at 21:11
  • when I add the cmd.Env line it gives me issues with resolving the hostname – efel Apr 14 '14 at 19:43
  • 1
    @OneOfOne thanks for input really that was the what I have searching for. Thanks to GOD. – EGurelli Jun 08 '16 at 20:33

2 Answers2

4

Thanks to the comments above, I was able to get ssh access working with a password. I used golang's ssh api library. It was fairly simple as I followed the examples from:

https://code.google.com/p/go/source/browse/ssh/example_test.go?repo=crypto

Specifically:

func ExampleDial() {
    // An SSH client is represented with a ClientConn. Currently only
    // the "password" authentication method is supported.
    //
    // To authenticate with the remote server you must pass at least one
    // implementation of AuthMethod via the Auth field in ClientConfig.
    config := &ClientConfig{
            User: "username",
            Auth: []AuthMethod{
                    Password("yourpassword"),
            },
    }
    client, err := Dial("tcp", "yourserver.com:22", config)
    if err != nil {
            panic("Failed to dial: " + err.Error())
    }

    // Each ClientConn can support multiple interactive sessions,
    // represented by a Session.
    session, err := client.NewSession()
    if err != nil {
            panic("Failed to create session: " + err.Error())
    }
    defer session.Close()

    // Once a Session is created, you can execute a single command on
    // the remote side using the Run method.
    var b bytes.Buffer
    session.Stdout = &b
    if err := session.Run("/usr/bin/whoami"); err != nil {
            panic("Failed to run: " + err.Error())
    }
    fmt.Println(b.String())
}
efel
  • 1,054
  • 3
  • 14
  • 29
2

This is a modified/complete version of above example https://godoc.org/golang.org/x/crypto/ssh#example-Dial

First get terminal package by go get golang.org/x/crypto/ssh

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "os"
    "strings"

    "golang.org/x/crypto/ssh"
    "golang.org/x/crypto/ssh/terminal"
)

func main() {
    if len(os.Args) < 3 {
        usage := "\n./remote-ssh {host} {port}"
        fmt.Println(usage)
    } else {
        host := os.Args[1]
        port := os.Args[2]

        username, password := credentials()
        config := &ssh.ClientConfig{
            User: username,
            Auth: []ssh.AuthMethod{
                ssh.Password(password),
            },
        }
        connectingMsg := fmt.Sprintf("\nConnecting to %s:%v remote server...", host, port)
        fmt.Println(connectingMsg)

        hostAddress := strings.Join([]string{host, port}, ":")
        // fmt.Println("Host add %s ", hostAddress)
        client, err := ssh.Dial("tcp", hostAddress, config)
        if err != nil {
            panic("Failed to dial: " + err.Error())
        }

        for {
            session, err := client.NewSession()
            if err != nil {
                panic("Failed to create session: " + err.Error())
            }
            defer session.Close()

            // Once a Session is created, can execute a single command on remote side
            var cmd string
            str := "\nEnter command (e.g. /usr/bin/whoami OR enter 'exit' to return) : "
            fmt.Print(str)
            fmt.Scanf("%s", &cmd)
            if cmd == "exit" || cmd == "EXIT" {
                break
            }
            s := fmt.Sprintf("Wait for command '%s' run and response...", cmd)
            fmt.Println(s)

            var b bytes.Buffer
            session.Stdout = &b
            if err := session.Run(cmd); err != nil {
                panic("Failed to run: " + err.Error())
            }
            fmt.Println(b.String())
        }
    }
}

func credentials() (string, string) {
    reader := bufio.NewReader(os.Stdin)

    fmt.Print("Enter Username: ")
    username, _ := reader.ReadString('\n')

    fmt.Print("Enter Password: ")
    bytePassword, err := terminal.ReadPassword(0)
    if err != nil {
        panic(err)
    }
    password := string(bytePassword)

    return strings.TrimSpace(username), strings.TrimSpace(password)
}

https://play.golang.org/p/4Ad1vKNXmI

gihanchanuka
  • 4,783
  • 2
  • 32
  • 32