Команда Net::SSH sudo зависает после ввода пароля
Я пытался написать небольшую библиотеку, используя 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, пока не наткнулся на Тора и подумал, что это будет хороший шанс опробовать его. Я не против того, чтобы переключать все это обратно на Capistrano, если это необходимо, но я был бы очень удивлен, если бы не было простого способа запуска команд sudo на удаленном сервере.
6 ответов
Первое, что вы можете попробовать, это использовать открытые ключи вместо паролей для входа в систему. Затем также попробуйте запустить команду из интерактивной оболочки.
Например:
(Эта часть действительно зависит от программного обеспечения сервера / клиента)
$ ssh-keygen
$ scp .ssh/id-dsa.pub server:
$ ssh server
server$ cat id-dsa.pub >> .ssh/authorizedkeys
$ scp -c "ls"
это должно работать без каких-либо запросов, если обмен ключами прошел успешно.
Надеюсь, это поможет кому-то искать. Мне также нужно было 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
Вот возможное решение:
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
Но с этим решением ваш корневой пароль записан в открытом виде в файле сценария...
Можно настроить с помощью net/ssh
, но это действительно слишком много усилий, просто выполнить ssh
команда как это:
system("ssh", "-t", "#{user}@#{host}", "sudo cp #{file_from_path} #{file_to_path}")
это -t
значит выделить tty. Без него все равно будет работать, но ваш пароль будет виден в открытом виде.
(Я предполагаю, что вы намерены ввести пароль sudo вручную. Если нет, перейдите по пути авторизованных ключей).
Я обманул, добавив это:
sudouser ALL=(ALL:ALL) NOPASSWD: ALL
в /etc/sudoers
Также обязательно используйте visudo
при редактировании!!!
На самом деле, в конце концов, я сделал что-то очень похожее на предложение Рибы (хотя я думаю, что его код гораздо лучше и умнее)
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