<p>I'm trying to run a script via the <a href="https://godoc.org/golang.org/x/crypto/ssh" rel="nofollow">SSH package</a> in my Go program (so far I've had success).</p>
<p>My issue is, the script attempts to run a command with sudo if the user has sudo privileges, and this causes the bash script to pause until a password is entered by the user.</p>
<p>For example:</p>
<pre><code>[ERROR ] Install cs-server: Checking dependencies: missing: lib32gcc1
# It attempts to install the missing dependencies with sudo but pauses here
[sudo] password for guest:
</code></pre>
<p>In my Go program, I have written something that looks similar to this:</p>
<pre><code>// Connect to SSH and retreive session...
out, err := session.StdoutPipe()
if err != nil {
log.Fatal(err)
}
go func(out io.Reader) {
r := bufio.NewScanner(out)
for r.Scan() {
fmt.Println(r.Text())
}
}(out)
// Execute ssh command...
</code></pre>
<p>And I receive the exact same output as the example above, only in this case, I don't even see the line <code>[sudo] password for guest:</code>... it only prints up to <code>[ERROR ] Install cs-server: Checking dependencies: missing: lib32gcc1</code> and pauses forever.</p>
<p>How can I bypass this pause? My options are to either enter the password from my Go program automatically, or end the ssh execution and just receive the output.</p>
<p><strong>Update --- Working Example Here:</strong></p>
<p><a href="https://pastebin.com/1uVcusXM" rel="nofollow">https://pastebin.com/1uVcusXM</a></p>
<hr/>**评论:**<br/><br/>TheMerovius: <pre><p>sudo checks whether stdin is a terminal and only allows you to enter a password if that's the case (so that it can disable echo and the like). So, what you need to do is making sure, that the sshd on the remote side allocates a terminal (what <code>ssh -t</code> does).</p>
<p>But, more likely, you just don't want to use sudo. The best option, would be to ssh directly into the root account and to use public key authentication for that. Using password-less sudo (as <a href="/u/singron" rel="nofollow">/u/singron</a> suggested) would be an alternative, but depending on <em>what</em> commands you want allow, end up being equivalent to ssh'ing into the root account anyway.</p></pre>Acidic92: <pre><p>I read your comment quite a few times but I still don't understand what exactly I should do. I have <code>ssh.TerminalModes{ssh.ECHO: 0}</code> and it still pauses.</p>
<p>I am making a control panel web app and my preference is that the end user is only required to enter the username and password for the user on their machine (without root).</p>
<p>Here is the code I'm playing around with: <a href="https://play.golang.org/p/_AfeTlQqOp" rel="nofollow">https://play.golang.org/p/_AfeTlQqOp</a></p></pre>TheMerovius: <pre><p>It seems you already request a PTY. So, my theory of why it doesn't work appears to be wrong :)</p>
<p>The rest of my comment still holds. That is a bad preference. Use public/private key auth. If you don't want to use root (why not? If it's an administrative task, that's perfectly fine), but need more privileges than a regular user, create a dedicated service account and give them only the rights they need. If you need sudo to achieve that granularity, give the service account the right to use it without a password.</p>
<p>Storing a username/password and passing that around and circumventing sudo's sanity checks is a bad idea.</p></pre>JohnStow: <pre><p>The default bufio.Scanner reads lines of text, ending with a newline. The password prompt doesn't end with a newline, so it's still in the buffer. You'll probably need to use a plain io.Read on the connection, and handle newlines etc. yourself.</p></pre>Acidic92: <pre><blockquote>
<p>out, err := session.StdoutPipe()
if err != nil {
log.Fatal(err)
}</p>
<p>go func(out io.Reader) {
r := bufio.NewScanner(out)
for r.Scan() {
fmt.Println(r.Text())
}
}(out)</p>
</blockquote>
<pre><code>io.ReadFull(r io.Reader, buf []byte) (n int, err error)
</code></pre>
<p>Do you mean io.ReadFull?</p></pre>JohnStow: <pre><p>Could do, or just do out.Read(buf) directly.</p></pre>Acidic92: <pre><p>Actually, using your suggestion I did manage to receive the message <code>[sudo] password for gues:</code></p></pre>lexpi: <pre><p>How will write the password? Just print to stout? </p></pre>Acidic92: <pre><p>I managed to get it to work by doing something that looks like this:</p>
<pre><code>in, err := session.StdinPipe()
if err != nil {
log.Fatal(err)
}
out, err := session.StdoutPipe()
if err != nil {
log.Fatal(err)
}
go func(in io.WriteCloser, out io.Reader) {
var (
line string
r = bufio.NewReader(out)
)
for {
b, err := r.ReadByte()
if err != nil {
break
}
fmt.Print(string(b))
if b == byte('\n') {
line = ""
continue
}
line += string(b)
if line == "[sudo] password for " {
_, err = in.Write([]byte(conn.password + "\n"))
if err != nil {
log.Fatal(err)
}
}
}
}(in, out)
output, err := session.Output("sudo echo hi")
</code></pre></pre>shadyabhi: <pre><p>Can you share a fully working example that I can run? </p></pre>Acidic92: <pre><p><a href="https://pastebin.com/1uVcusXM" rel="nofollow">https://pastebin.com/1uVcusXM</a> - That's my final code I'm using.</p>
<p>Are you asking for one of your own projects? or just curious?</p></pre>shadyabhi: <pre><p>I was planning to write a "fabric" alternative in Go (just the functionality to execute arbitrary commands in parallel) and I thought that your code will give me a good start. Thanks! </p></pre>singron: <pre><p>Alternatively you could add NOPASSWD to /etc/sudoers or just ssh as root.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传