Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Answer to password shell prompt in golang

Tags:

git

linux

go

I am searching for a way to answer to a shell password prompt in golang.

like :

bussiere@kus:~/Workspace/rteest$ ./passwordtest.sh
Password : 

I would like to enter the password automatically with my token in golang after launching a shell command / script ...

I've made some script that get a one time token with mfa if everything is ok (in golang). So i need to enter the tempory token to a linux password prompt.

I know that there is the expect command but i would like to compile my program to embed it and have minimal depency.

Thanks and regards

edit thks to @nevermore i've tried this (but it doesn't work) : https://play.golang.org/p/Ffm3q5h636

package main

import (
    "os/exec"
    "fmt"
    "log"
    "io"
)

func main() {
    cmdb := "git"
    args := "clone https://[email protected]/bb/fzgs.git"
cmd := exec.Command(cmdb, args)
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}

go func() {
defer stdin.Close()
io.WriteString(stdin, "QSRDFGHJfZERTYU")
}()

out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s\n", out)

}

it gives me this :

2017/05/12 20:42:36 exit status 1
exit status 1

edit bis :

    cmdb := "git"
    args := "https://[email protected]/bb/fzgs.git"
cmd := exec.Command(cmdb, "clone", args)

it asks for the password :

Password for 'https://[email protected]':

like image 995
Bussiere Avatar asked May 12 '17 17:05

Bussiere


2 Answers

The problem is that you are trying to send the password before Git asks you to do so.

And the way Git asks for password depends on whether credential helper is set. If the credential helper is not set, or doesn't return a password, the user is asked for input.

Solution #1: Include Password into URI

In most cases (at least with Github and Bitbucket) the Git server accepts credentials passed via HTTPS URI, e.g. https://user:[email protected]/repo.git.

cmd := exec.Command("git", "clone", "https://bb:[email protected]/bb/fzgs.git")

env := os.Environ()
env = append(env, "GIT_ASKPASS=")
cmd.Env = env

var out bytes.Buffer
cmd.Stdout = &out

err := cmd.Run()

Solution #2: Custom Credential Helper

Another way is to make a credential helper script, e.g.:

#!/bin/bash -
printf '%s\n' 'YOUR_PASSWORD'

and pass it via GIT_ASKPASS:

cmd := exec.Command("git", "clone", "https://[email protected]/bb/fzgs.git")

env := os.Environ()
env = append(env, "GIT_ASKPASS=/path/to/fzgs-askpass.sh")
cmd.Env = env

var out bytes.Buffer
cmd.Stdout = &out

err := cmd.Run()
like image 160
Ruslan Osmanov Avatar answered Nov 07 '22 02:11

Ruslan Osmanov


If a user is asked for a password in their shell, you can use golang to write to the io.Writer that is os.Stdin

Properly passing data on stdin to a command and receiving data from stdout of that command in golang

os.Stdin is a os.File created by

NewFile(uintptr(syscall.Stdin), "/dev/stdin")

And the password prompt will be reading from this file

You can execute ./passwordtest.sh as in your question, and then write the password to os.Stdin, and the bash script should accept the bytes written by golang as the password.


Alternatively is actually a method for this in the exec package for the Cmd type.

  1. Use cmd to execute your shell script
  2. Input the password using stdin, or cmd.StdinPip()
  3. Read the shells output using cmd.CombinedOutput()

Cmd represents an external command being prepared or run.

https://golang.org/pkg/os/exec/#Cmd.StdinPipe

cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
    log.Fatal(err)
}

go func() {
    defer stdin.Close()
    io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
}()

out, err := cmd.CombinedOutput()
if err != nil {
    log.Fatal(err)
}

fmt.Printf("%s\n", out)

StdinPipe returns a pipe that will be connected to the command's standard input when the command starts. The pipe will be closed automatically after Wait sees the command exit. A caller need only call Close to force the pipe to close sooner. For example, if the command being run will not exit until standard input is closed, the caller must close the pipe.

Also, your cmd arguments shouldn't combine clone and the url, try instead

cmd := exec.Command(cmdb, "clone", url)
like image 40
Nevermore Avatar answered Nov 07 '22 02:11

Nevermore