Net::SSH sudo command hangs after entering password
Asked Answered
G

6

6

I've been trying to write a small library using Thor to help assist me in quick creating new projects and sites. I wrote this small method:

def ssh(cmd)
  Net::SSH.start( server_ip, user, :port => port) do |session|
    session.exec cmd
  end
end

to just assist me in running quick commands on remote servers when needed.

The problem is when I need to run a command under sudo on the remote end, the script just seems to hang on me. For example when executing this...

ssh("sudo cp #{file_from_path} #{file_to_path}" )

The script will prompt me for a password

[sudo] password for user:

But then the whole thing hhangs after typing it in.

Would anyone happen to know why it hangs exactly, and what I can do to run sudo command on a remote server under Net::SSH (or some alternative)?

*note: Before suggested, I had originally started writing this library as a recipe under Capistrano, until I came upon Thor, and thought it would be a good chance to try it out. I'm not against having to switch the whole thing back to Capistrano if needed, but I'd just be really surprised if there isn't an easy way to run sudo commands on a remote server.

Giguere answered 28/7, 2010 at 19:52 Comment(3)
are you able to pipe the password in? ssh("sudo cp #{from} #{to} < "mysupass")Silvie
of course i forgot to escape the double quotes in the previous comment.Silvie
Tried this, but it doesn't work. It comes back with "bash: mysupass: No such file or directory" . Is this an attempt to pipe the password through to the prompt? If so, the idea might work, but the syntax is probably just off. I'll see what I can find about that.Giguere
B
1

The first thing you might want to try is using public keys instead of passwords to login. Then also try running the command from the interactive shell.

For example:

(This part really depends on the server/client software you have)

$ ssh-keygen
$ scp .ssh/id-dsa.pub server:
$ ssh server
server$ cat id-dsa.pub >> .ssh/authorizedkeys

$ scp -c "ls"

this should work without any prompts, if the key sharing was successful.

Bergamo answered 28/7, 2010 at 19:56 Comment(5)
Actually I am using public keys. The password it's prompting for isn't the password for the ssh connection, but the password for sudo to run the command. So I at least know it connects, but it hangs after I fill in the sudo password.Giguere
You might try setting sudo not to ask for a password for the specific user.Bergamo
I had tried this in the sudoers file on the remote server by adding the follower lines at the end "user ALL=(ALL) ALL", but I'm still getting prompted. Am I doing this incorrectly?Giguere
From the manual: "By default, sudo requires that a user authenticate him or herself before running a command. This behavior can be modified via the NOPASSWD tag."Bergamo
Got it working with the NOPASSWD tag. The syntax is "user ALL=NOPASSWD: ALL" for anyone looking for the info. @sukru, assuming I am using keys to access from my personal computer though, are there any inherent security risks with this method that I should be concerned with?Giguere
W
14

I hope this will help someone searching. I also needed to sudo during deployment (restarting thin instances)

# deploy.rake
require 'net/ssh'

# INITIALIZE CONSTANTS HERE
HOST = 'yourwebsite.com'
USER = 'admin'
PASSWORD = 'your server password' # or use ENV variables?
# etc.

namespace :deploy do 
  namespace :staging do  
    task :restart do
      commands = [
        "cd #{PATH_TO_STAGING_APP} && git checkout master",
        "git reset --hard HEAD",
        "git pull origin master",
        "bundle install --without test development",
        "sudo thin restart -C /etc/thin/#{STAGING_APP}.yml"
      ]

      Net::SSH.start(HOST, USER, :password => PASSWORD) do |ssh|
        ssh.open_channel do |channel|
          channel.request_pty do |ch, success|
            if success
              puts "Successfully obtained pty"
            else
              puts "Could not obtain pty"
            end
          end

          channel.exec(commands.join(';')) do |ch, success|
            abort "Could not execute commands!" unless success

            channel.on_data do |ch, data|
              puts "#{data}"
              channel.send_data "#{PASSWORD}\n" if data =~ /password/
            end

            channel.on_extended_data do |ch, type, data|
              puts "stderr: #{data}"
            end

            channel.on_close do |ch|
              puts "Channel is closing!"
            end
          end
        end
        ssh.loop
      end
    end
  end
end

Note that one channel can only execute one command. Hence I chained the commands together with commands.join(';')

Reference: Net::SSH::Connection::Channel

Widdershins answered 30/11, 2012 at 4:22 Comment(0)
S
5

It's possible to set it up with net/ssh, but it's really too much effort, just execute ssh command like this:

system("ssh", "-t", "#{user}@#{host}", "sudo cp #{file_from_path} #{file_to_path}")

This -t means to allocate a tty. Without it it will still work, but your password will be seen in clear.

(I assume you intend to type sudo password manually. If not, go the authorized keys way).

Sartorius answered 28/7, 2010 at 23:28 Comment(0)
B
5

Here is a possible solution :

def ssh(cmd)
  Net::SSH.start( server_ip, user, :port => port) do |session|
    result = nil
    session.exec!(cmd) do |channel, stream, data|
      if data =~ /^\[sudo\] password for user:/
        channel.send_data 'your_sudo_password'
      else
        result << data
      end
    end
    result # content of 'cmd' result
  end
end

But with this solution, you have your root password writen in clear in a script file somewhere ...

Biodynamics answered 20/11, 2010 at 22:59 Comment(1)
@japancheese The last code you have posted does not handle the sudo prompt password. It's exactly the same code given in your question.Biodynamics
G
2

I cheated by doing adding this:

sudouser  ALL=(ALL:ALL) NOPASSWD: ALL

to /etc/sudoers

Also, be sure to use visudo when editing!!!

Gardant answered 5/12, 2012 at 21:13 Comment(0)
B
1

The first thing you might want to try is using public keys instead of passwords to login. Then also try running the command from the interactive shell.

For example:

(This part really depends on the server/client software you have)

$ ssh-keygen
$ scp .ssh/id-dsa.pub server:
$ ssh server
server$ cat id-dsa.pub >> .ssh/authorizedkeys

$ scp -c "ls"

this should work without any prompts, if the key sharing was successful.

Bergamo answered 28/7, 2010 at 19:56 Comment(5)
Actually I am using public keys. The password it's prompting for isn't the password for the ssh connection, but the password for sudo to run the command. So I at least know it connects, but it hangs after I fill in the sudo password.Giguere
You might try setting sudo not to ask for a password for the specific user.Bergamo
I had tried this in the sudoers file on the remote server by adding the follower lines at the end "user ALL=(ALL) ALL", but I'm still getting prompted. Am I doing this incorrectly?Giguere
From the manual: "By default, sudo requires that a user authenticate him or herself before running a command. This behavior can be modified via the NOPASSWD tag."Bergamo
Got it working with the NOPASSWD tag. The syntax is "user ALL=NOPASSWD: ALL" for anyone looking for the info. @sukru, assuming I am using keys to access from my personal computer though, are there any inherent security risks with this method that I should be concerned with?Giguere
G
-1

Actually in the end, I did something very similar to Riba's suggestion (although his/her code is much better and smarter, I think)

def ssh(env, cmd, opts={})
    Net::SSH.start( config[env]["ip"], config[env]["user"], :port => config[env]["port"]) do |session|
        cmd = "sudo #{cmd}" if opts[:sudo]
        print cmd
        session.exec cmd
    end
end
Giguere answered 21/11, 2010 at 6:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.