Net::SSH sudo 命令在输入密码后挂起

发布于 2024-09-12 05:23:50 字数 661 浏览 11 评论 0原文

我一直在尝试使用 Thor 编写一个小型库来帮助我快速创建新项目和网站。我写了这个小方法:

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

只是在需要时帮助我在远程服务器上运行快速命令。

问题是当我需要在远程端的 sudo 下运行命令时,脚本似乎挂在我身上。例如,执行此操作时...

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

脚本将提示我输入密码,

[sudo] password for user:

但是输入密码后整个事情都会挂起。

是否有人知道它为什么挂起,以及我可以做什么来在远程服务器上运行 sudo 命令在 Net::SSH (或其他替代方案)下?

*注:在建议之前,我最初开始在 Capistrano 下编写这个库作为食谱,直到我遇到 Thor,并认为这将是一个尝试的好机会。我并不反对在需要时将整个系统切换回 Capistrano,但如果没有一种简单的方法在远程服务器上运行 sudo 命令,我会感到非常惊讶。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

£噩梦荏苒 2024-09-19 05:23:50

我希望这能帮助正在搜索的人。我还需要在部署期间使用 sudo(重新启动瘦实例)

# 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

请注意,一个通道只能执行一个命令。因此,我将命令与 commands.join(';') 链接在一起

参考:Net::SSH::Connection::Channel

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

孤君无依 2024-09-19 05:23:50

net/ssh来设置也是可以的,但是确实太费力了,执行ssh命令就可以了:

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

这个-t的意思是分配一个tty。如果没有它,它仍然可以工作,但是您的密码将被清晰地看到。

(我假设您打算手动输入 sudo 密码。如果没有,请采用授权密钥方式)。

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).

折戟 2024-09-19 05:23:50

这是一个可能的解决方案:

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

但是通过这个解决方案,您可以将 root 密码以清晰的形式写在某个脚本文件中......

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 ...

小糖芽 2024-09-19 05:23:50

我通过添加以下内容来作弊:

sudouser  ALL=(ALL:ALL) NOPASSWD: ALL

/etc/sudoers

另外,编辑时一定要使用 visudo

I cheated by doing adding this:

sudouser  ALL=(ALL:ALL) NOPASSWD: ALL

to /etc/sudoers

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

天荒地未老 2024-09-19 05:23:50

您可能想要尝试的第一件事是使用公钥而不是密码登录。然后还尝试从交互式 shell 运行该命令。

例如:(

这部分实际上取决于您拥有的服务器/客户端软件)

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

$ scp -c "ls"

如果密钥共享成功,这应该可以在没有任何提示的情况下工作。

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.

巨坚强 2024-09-19 05:23:50

实际上最后,我做了一些与Riba的建议非常相似的事情(尽管我认为他/她的代码更好更聪明)

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文