Question

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.

Was it helpful?

Solution

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())
}

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top