Enter SSH Sudo Password on Prompt (or exit) in Go

xuanbao · · 29 次点击    
<p>I&#39;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&#39;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&#39;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&#39;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&#39;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&#39;ing into the root account anyway.</p></pre>Acidic92: <pre><p>I read your comment quite a few times but I still don&#39;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&#39;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&#39;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&#39;t want to use root (why not? If it&#39;s an administrative task, that&#39;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&#39;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&#39;t end with a newline, so it&#39;s still in the buffer. You&#39;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(&#39;\n&#39;) { line = &#34;&#34; continue } line += string(b) if line == &#34;[sudo] password for &#34; { _, err = in.Write([]byte(conn.password + &#34;\n&#34;)) if err != nil { log.Fatal(err) } } } }(in, out) output, err := session.Output(&#34;sudo echo hi&#34;) </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&#39;s my final code I&#39;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 &#34;fabric&#34; 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>
29 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet