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", "[email protected]")
    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.

6
  • 2
    You cannot do this very easily with ssh, because it will refuse to read the password from stdin. See stackoverflow.com/q/1340366. Your best option seems to be generating a key pair and authenticating using that. Commented Apr 11, 2014 at 18:44
  • 2
    I have tried to do this before in Python. I was only able to get it to work using Paramiko. You should try with go.crypto/ssh. Commented Apr 11, 2014 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. Commented Apr 12, 2014 at 3:15
  • 1
    does cmd.Env = append(cmd.Env, 'TERM=xterm') help? Commented Apr 13, 2014 at 21:11
  • 1
    @OneOfOne thanks for input really that was the what I have searching for. Thanks to GOD. Commented Jun 8, 2016 at 20:33

2 Answers 2

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())
}
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.