【问题标题】:Net::SSH sudo command hangs after entering password输入密码后 Net::SSH sudo 命令挂起
【发布时间】:2011-03-22 09:01:26
【问题描述】:

我一直在尝试使用 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:

但是在输入之后整个事情就挂了。

有谁知道它为什么会完全挂起,以及我可以做些什么来在 Net::SSH(或其他替代方法)下的远程服务器上运行 sudo 命令?

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

【问题讨论】:

  • 你能输入密码吗? ssh("sudo cp #{from} #{to} < "mysupass")
  • 我当然忘记转义上一条评论中的双引号了。
  • 试过这个,但它不起作用。它带有 "bash: mysupass: No such file or directory" 。这是尝试将密码传递到提示符吗?如果是这样,这个想法可能会奏效,但语法可能刚刚结束。我会看看我能找到什么。

标签: ruby sudo net-ssh sudoers thor


【解决方案1】:

我希望这对搜索的人有所帮助。我还需要在部署期间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

【讨论】:

    【解决方案2】:

    net/ssh设置是可以的,但是实在是太费劲了,像这样执行ssh命令:

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

    这个-t 表示分配一个tty。没有它,它仍然可以工作,但您的密码会清晰可见。

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

    【讨论】:

      【解决方案3】:

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

      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
      

      但是使用此解决方案,您可以将根密码清楚地写入脚本文件中的某处...

      【讨论】:

      • @japancheese 您发布的最后一个代码不处理 sudo 提示密码。这与您的问题中给出的代码完全相同。
      【解决方案4】:

      我通过添加这个作弊:

      sudouser  ALL=(ALL:ALL) NOPASSWD: ALL
      

      /etc/sudoers

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

      【讨论】:

        【解决方案5】:

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

        例如:

        (这部分真的取决于你拥有的服务器/客户端软件)

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

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

        【讨论】:

        • 其实我用的是公钥。它提示的密码不是 ssh 连接的密码,而是 sudo 运行命令的密码。所以我至少知道它可以连接,但是在我填写 sudo 密码后它会挂起。
        • 您可以尝试设置 sudo 不为特定用户询问密码。
        • 我已经在远程服务器上的 sudoers 文件中尝试了这个,方法是在“user ALL=(ALL) ALL”末尾添加跟随行,但我仍然收到提示。我做错了吗?
        • 来自手册:“默认情况下,sudo 要求用户在运行命令之前对其进行身份验证。此行为可以通过 NOPASSWD 标签进行修改。”
        • 让它与 NOPASSWD 标签一起使用。对于查找信息的任何人,语法都是“user ALL=NOPASSWD: ALL”。 @sukru,假设我使用密钥从我的个人计算机进行访问,这种方法是否存在我应该关注的固有安全风险?
        【解决方案6】:

        实际上最后,我做了与 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
        

        【讨论】:

          猜你喜欢
          • 2023-03-25
          • 1970-01-01
          • 1970-01-01
          • 2015-04-20
          • 1970-01-01
          • 1970-01-01
          • 2010-09-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多