0

I'm trying to run scp as command via golang's exec.cmd because the non-official implementations available for scp transfer did not work for me. Scp executes until it asks for the password and then I'm unable to write data to the stdin. Could it be possible that the stdin is redirected somewhere else?

var cmd *exec.Cmd
if m.IsDir() {
    cmd = exec.Command("scp", "-r", src, user+"@"+client.Host+":"+dest)
} else {
    cmd = exec.Command("scp", src, user+"@"+client.Host+":"+dest)
}

cmdWriter, err := cmd.StdinPipe()
if err != nil {
    return err
}

err = cmd.Start()

if cred.Typ() == "PW" {
    cmdWriter.Write( []byte(cred.Data()+"\n") )
} else {
    log.Println( "credential typ not supported" )
}

err = cmd.Wait()
log.Println( "done" )
return err

Setting cmd.Stdin to os.Stdin works but of course I have to manually enter the password which is not what I want. Or would you overall not recommend this way to run scp? I'm able to run simple SSH commands on the server (with "golang.org/x/crypto/ssh"). Is there maybe a way to transfer binaries just via a SSH session? Thanks in advance.

gbatt
  • 53
  • 6
  • 1
    See how you could implement scp: https://github.com/viant/toolbox/blob/master/ssh – Adrian Jun 30 '18 at 10:51
  • Thanks but I was really looking for a simple solution. I already tried a few scp implementations. – gbatt Jun 30 '18 at 11:29
  • Have you looked if `cmd` has some output? It does not necessarily return, as it may say wrong password or other things. – leaf bebop Jun 30 '18 at 11:48
  • No writing to stdIn just has no effect. I even tried setting the input to os.stdin and then writing with os.stdin.write but it does not work. It only works if I enter it manually. I'm quite confident now that scp creates a subprocess that no longer uses the old pipe once it writes user@10.1.1.2' password to the output. – gbatt Jun 30 '18 at 11:52

2 Answers2

2

I will assume you are on Linux. If you want to pass a password to scp from the command line, the best way is with sshpass. It should be available for install from the default package repositories on major Linux distros, just install it with apt or yum. The syntax is sshpass -p [password] scp ...

Here I spawn a bash session and pipe commands to it from the StdinPipe:

func main() { 
    cmd := exec.Command("bash")
    cmdWriter, _ := cmd.StdinPipe()
    cmd.Start()

    cmdString := fmt.Sprintf("sshpass -p %s scp -r %s %s@%s:%s", password, src, user, client, dest)

    cmdWriter.Write([]byte(cmdString + "\n"))
    cmdWriter.Write([]byte("exit"    + "\n"))

    cmd.Wait()
}

I have ignored errors and simplified your code a little for brevity.


However, the "github.com/pkg/sftp" package is much easier to use for file transfers like this. You can find a simple example of how to use it here. Note that using nil for HostKeyCallback in the client config is deprecated - use HostKeyCallback: ssh.InsecureIgnoreHostKey() if you absolutely need to accept all host keys.

Arthur Ruckman
  • 101
  • 2
  • 4
  • The github package looks like the best way to go. My software has to run with as few as dependencies possible so sshpass is not really an option. Thank you. – gbatt Jun 30 '18 at 21:05
0

scp and ssh insist on reading the password from a tty (or psuedo tty)

stdin from a remote session is not this so it will not work

The standard go golang.org/x/crypto/ssh package does support sending passwords so presumably has a work around. scp is implemented as a server that is invoked by ssh so probably extending the existing go ssh support would not be difficult

However, there is always an external program based workaround! As discussed here, this involves using the sshpass program

Vorsprung
  • 32,923
  • 5
  • 39
  • 63