Ruby: Net::SSH::Multi using keys - ruby

I'm having problems getting Net::SSH::Multi library to work, it should connect to each box and run that command, I'm trying to get the output.
Here's my code:
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
require 'net/ssh/multi'
#ssh_responses = Array.new
puts "Starting vhost grab..."
# SSH into each instance, and download Vhost Dump
Net::SSH::Multi.start(:on_error => :ignore) do |ssh|
ssh.use 'ec2-1-1-1-1.compute-1.amazonaws.com', :user => 'portal_user', :keys => ['~/.ssh/portal_user.pem']
ssh.use 'ec2-1-1-1-2.compute-1.amazonaws.com', :user => 'portal_user', :keys => ['~/.ssh/portal_user.pem']
puts "Added servers..."
stdout = ""
# run this on all boxes
ssh.exec 'pwd' do |channel, stream, data|
puts "Trying execution..."
channel.request_pty do |c, success|
puts "Trying to request tty..."
if success
puts "Successfully requested tty"
command = "sudo ls /root"
c.exec(command) do |c, success, data|
puts "Executing commands..."
stdout << data if stream == :stdout
end
#ssh_responses << stdout
puts "Storing responses..."
end
end
end
ssh.loop
end
puts #ssh_responses
puts "Script executed."
And the response I get from running is:
$ ruby serverAction.rb
Starting vhost grab...
Added servers...
Trying execution...
Script executed.

Related

SSH Connections one after another, second one fails

I have code that requires me to connect to one server, rsync to a different server, then connect to the second server and run a bunch of commands on it. But without fail, the second SSH connection throws a 'do_open_failed': open failed (1) (Net::SSH::ChannelOpenFailed) error. Am I doing something wrong here, is there a way to close the first connection properly that makes the second one connect?
Net::SSH.start(self.from_creds['host'], self.from_creds['user'], :password => self.from_creds['password']) do |ssh|
channel = ssh.open_channel do |ch|
ch.exec "/usr/bin/rsync -e ssh -varuzP --exclude=sys-export --delete #{self.from_creds['filepath']}/#{self.client_id}/ #{self.scp_to}/#{new_client_id}" do |ch, success|
raise "could not execute command" unless success
# "on_data" is called when the process writes something to stdout
ch.on_data do |c, data|
$stdout.print data
end
# "on_extended_data" is called when the process writes something to stderr
ch.on_extended_data do |c, type, data|
$stderr.print data
end
ch.on_close { puts "done!" }
end
end
channel.wait
end
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec "mkdir -p #{tmp_path}"
ssh1.exec "cd #{self.to_creds['filepath']}/#{new_client_id}"
end
According to the documentation, exec doesn't block. Trying using exec! instead.
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec! "mkdir -p #{tmp_path}"
ssh1.exec! "cd #{self.to_creds['filepath']}/#{new_client_id}"
end
Alternatively,
Net::SSH.start(self.to_creds['host'], self.to_creds['user'], :password => self.to_creds['password']) do |ssh1|
# Do some other stuff here
tmp_path = "#{self.to_creds['filepath']}/tmp/#{Time.now.to_i}"
ssh1.exec "mkdir -p #{tmp_path}"
ssh1.exec "cd #{self.to_creds['filepath']}/#{new_client_id}"
ssh1.loop
end

Avoid shell hang when use ruby NET:SSH

For a specific bash command when I execute it locally after completion I got the shell free but when I execute remotely the shell hangs like that:
[user#host ~]$ ruby bin/remote_control.rb server start_server1
Running /home/server_manager.sh start_server ... wait
[]
When I use ruby and NET::SSH to call remotely this command and it's necessary to press ctrl+C to to get the shell prompt available again and press enter doesn't work.
Again the remote script/command /home/server_manager.sh doesn't has this behavior when
called locally
For get free the terminal the script has this syntax:
I'm trying to execute in backgroud
`commmand &` 2>&1 | echo "\n"
And the ruby code bellow is used for calling the script above:
Net::SSH.start(#hostname, #username, :password => #password) do |ssh|
channel = ssh.open_channel do |ch|
ch.exec #cmd do |ch, success|
raise "Could not execute command: #{cmd}" unless success
ch.on_data do |c, data|
begin
if !data.nil? then
print data
else
exit
end
rescue SystemExit
puts "Rescued a SystemExit exception"
end
end
ch.on_extended_data do |c, type, data|
begin
if !data.nil? then
print data
else
exit
end
rescue SystemExit
puts "Rescued a SystemExit exception"
end
end
ch.on_eof do |ch|
puts "Cmd finished with success: #{#cmd}"
$LOG.info("Cmd finished with success: #{#cmd}")
end
ch.on_close { puts "Done!" }
end
end
channel.wait
ssh.loop
end
But I haven't success until now. What I need to add to this code to always have the shell free.

use ruby Net::SSH to read a remote file via sudo

I have to read the contents of a remote file I have permissions to (sudo) read with
cat,less or tail.
I am going to be doing this in Ruby so I assume I should be using Net::SSH to do it.
The file is a log file so it can be quite big.
This is the code I am trying now:
require 'rubygems'
require 'net/ssh'
cmd = "sudo cat /var/logs/httpd/ACCESS_log.2012.03.23"
Net::SSH.start( "SERVER" , "USER", :password => "PASSWORD") do |ssh|
ssh.open_channel do |channel|
channel.request_pty
channel.exec(cmd);
channel.on_close do
puts "shell terminated"
end
channel.on_eof do |ch|
puts "remote end is done sending data"
end
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data.inspect}"
end
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for USER:/
puts "data works"
channel.send_data 'PASSWORD'
end
channel.on_data do |ch,data|
puts "in third"
puts data.inspect
end
end
channel.on_process do |ch|
puts "in process"
end
ssh.loop
end
end
When I run that I get the following output:
in process
in process
in process
data works
in process
in process
in process
in third
"\r\n"
remote end is done sending data
shell terminated
The log actually currently has a few thousand lines of data in it, because I can read it from the actual server using putty.
How do I get that out from channel.on_data ?
Thanks
I think you need to add a \n to the password you send. This works for me. Note, The place where I commented out the else clause, you could possibly get the info from there too, but it works as you have it, but with a \n in the password.
require 'rubygems'
require 'net/ssh'
cmd = "sudo cat /var/log/mail.log"
HOSTNAME = "myhost.example.com"
USERNAME = "me"
PASSWORD = "12345"
Net::SSH.start( HOSTNAME , USERNAME, :password => PASSWORD) do |ssh|
ssh.open_channel do |channel|
channel.request_pty
channel.exec(cmd);
channel.on_close do
puts "shell terminated"
end
channel.on_eof do |ch|
puts "remote end is done sending data"
end
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data.inspect}"
end
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for #{USERNAME}:/
puts "data works"
channel.send_data "#{PASSWORD}\n"
else
#puts "OUTPUT NOT MATCHED: #{data}"
end
channel.on_data do |ch,data|
puts "in third"
puts data.inspect
end
end
channel.on_process do |ch|
puts "in process"
end
ssh.loop
end
end
You are replacing a new on_data callback while executing an on_data callback. I haven't spelunked the internals of Net::SSH, but that could produce surprising behavior.
Try changing your code in your two on_data callbacks to be one, and see if that helps.
channel.on_data do |channel, data|
if data =~ /^\[sudo\] password for USER:/
puts "data works"
channel.send_data 'PASSWORD'
else
puts "in third"
puts data.inspect
if
end
As a side note, since you need sudo to read the logs, someone thinks they and that server are worth protecting. It looks like you're embedding passwords which give privileged access to the server in this ruby program. That implies anyone who can read the program gains the same privileged access. What will you do to limit access to the password in this program?
require 'net/ssh'
Net::SSH.start('host', 'user', :password => "password") do |ssh|
# capture all stderr and stdout output from a remote process
output = ssh.exec!("hostname")
puts output
# capture only stdout matching a particular pattern
stdout = ""
ssh.exec!("ls -l /home/jamis") do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
# run multiple processes in parallel to completion
ssh.exec "sed ..."
ssh.exec "awk ..."
ssh.exec "rm -rf ..."
ssh.loop
# open a new channel and configure a minimal set of callbacks, then run
# the event loop until the channel finishes (closes)
channel = ssh.open_channel do |ch|
ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success|
raise "could not execute command" unless success
# "on_data" is called when the process writes something to stdout
ch.on_data do |c, data|
$stdout.print data
end
# "on_extended_data" is called when the process writes something to stderr
ch.on_extended_data do |c, type, data|
$stderr.print data
end
ch.on_close { puts "done!" }
end
end
channel.wait
# forward connections on local port 1234 to port 80 of www.capify.org
ssh.forward.local(1234, "www.capify.org", 80)
ssh.loop { true }
end
Latest Document 17.11.25

Using the ruby gem net-ssh-multi to execute a sudo command on multiple servers at once

In a previous question I figured out how to start a password-authenticated ssh sessions on multiple servers to run a single command. Now I need to be able to execute a "sudo" command. The problem is, that net-ssh-multi does not allocate a pseudo terminal (pty), which sudo needs to run, resulting in the following error:
[127.0.0.1 : stderr] sudo: sorry, you must have a tty to run sudo
According to the documentation, a pseudo-terminal can be allocated with a method call to a channel object, however, the following code does not work: it generates the "no tty" error above:
require 'net/ssh'
require 'net/ssh/multi'
Net::SSH::Multi.start do |session|
# define the servers we want to use
my_ticket.servers.each do |session_server|
session.use session_server , :user => user_name , \
:password => user_pass
end
# execute commands on all servers
session.exec 'sudo ls /root' do |channel, stream, data|
if data =~ /^\[sudo\] password for user:/
channel.request_pty # <- problem must be here.
channel.send_data user_pass
end
end
# run the aggregated event loop
session.loop
end
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
Can you try something like this:
channel.request_pty do |c, success|
if success
command = "sudo YOUR_COMMAND"
c.exec(command) do |c, success|
# Some processing
end
end
end
In this case 'sudo' is inside.
You need to request a pty before running the command.
session.open_channel do |ch|
ch.request_pty
ch.exec "sudo ls /root"
end
Also you may remove the tty requeriment from /etc/sudoers. To do it run visudo and comment Defaults requiretty
This is what I wound up doing, thanks to #Christian and this wonderful Pastie:
Net::SSH::Multi.start do |session|
# define the servers we want to use
my_ticket.servers.each do |session_server|
session.use session_server , :user => my_ticket.user_name , \
:password => my_ticket.user_pass
end
session.open_channel do |channel|
channel.request_pty do |c, success|
raise "could not request pty" unless success
channel.exec "sudo YOUR_COMMAND"
channel.on_data do |c_, data|
if data = /\[sudo\]/
channel.send_data(#password + "\n")
end
puts data
end
end
end
# run the aggregated event loop
session.loop
end

Executing Password Change over Ruby Net-SSH

I am looking to execute a password change over Net-ssh and this code seems to hang:
Net::SSH.start(server_ip, "user", :verbose => :debug ) do |session|
session.process.popen3("ls") do |input, output, error|
["old_pass","test", "test"].each do |x|
input.puts x
end
end
end
I know the connection works because using a simple exec I can get the output from ls on the remote server, but this hangs.
Any ideas?
The last message from debug is that the public key succeeded.
This one will solve your issue ,, note this script to change password for a list of servers list in file
#~~~~~~~~~~~~~~~~~~~~~~~
# Change Password is a simple script to change the password for a list of servers
# Coded by : Sabry Saleh
# License : GPL2
#~~~~~~~~~~~~~~~~~~~~~~~
#=-Notes-=
# You have to install ruby + net-ssh gems
# sudo gem install net-ssh
#~~~~~~~~~~~~~~~~~~~~~~~
require 'net/ssh'
host = IO.readlines('test1.txt') # full path of servers' list
port = 22 # SSH port
user = 'username' # username
i = 0
while i < host.length
Net::SSH.start(host[i], user , :password => "User pass" , :port=> port) do |ssh|
ssh.open_channel do |channel|
channel.on_request "exit-status" do |channel, data|
$exit_status = data.read_long
end
channel.request_pty do |channel, success|
channel.exec("sudo passwd UserName") # Logged user shuold be root or sudoers memeber
if success
channel.on_data do |channel, data|
puts data.inspect.chomp("\r\n")
channel.send_data("New pass\n") # put the New password you need to set
sleep 0.1
end
else
puts "FAILED!!"
end
end
channel.wait
puts "SUCCESS!!" if $exit_status == 0
end
end
i += 1
end

Resources