removing quotations and brackets from an array when printing (cannot use puts) - ruby

I have this:
[1] pry(main)> ip = ["123.456.789.666"]
=> ["123.456.789.666"]
[2] pry(main)> p ip
["123.456.789.666"]
=> ["123.456.789.666"]
How do I get it so that it comes out 123.456.789.666? The end goal is to take whatever IP is in my array to ping with. The ping fails because it's doing ping "123.456.789.666".

You can pass the IP only as a string or IPAddr object (but in general it will be a string for net/ping gem). Looks like there are some problems with this gem when host specified as an IP.
If you need to check the availability of a server - use system ping:
#5 - number of retry, ip[0] - your ip from the array
`ping -q -c 5 #{ip[0]}`
'All is OK' if $?.exitstatus == 0

Related

Ruby array.each and RestClient skipping array elements

I've a array of elements and for each element, I've to make a get call using RestClient. However, I see a few of the array elements are being skipped.
total_hosts = []
puts "total_hosts initially = " + total_hosts.size.to_s
require 'rest-client'
total_hosts = %w{10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4 10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9 10.0.0.10 10.0.0.11 10.0.0.12 10.0.0.13 10.0.0.14 10.0.0.15}
puts "total_hosts after adding = " + total_hosts.size.to_s
total_hosts.each { |host|
begin
sleep 1
RestClient.get("http://#{host}/get_my_build") { |response|
case response.code
when 200
if response.body.include?("Your build number is 10")
puts "Latest build on #{host}"
else
puts "Older build on #{host}"
end
when 404
puts "Exception on #{host} not reachable"
end
}
rescue => e
puts "Exception on #{host}"
end
}
puts "job completed"
Output :
Latest build on 10.0.0.3
Latest build on 10.0.0.4
Exception on 10.0.0.5
Latest build on 10.0.0.6
Latest build on 10.0.0.7
Latest build on 10.0.0.8
Latest build on 10.0.0.9
Exception on 10.0.0.10
Latest build on 10.0.0.11
Latest build on 10.0.0.12
Latest build on 10.0.0.13
Latest build on 10.0.0.14
Latest build on 10.0.0.15
I could see hosts 1 and 2 are missing in the output. If I run the same script after some time they might be listed in the output but some other hosts are missing.
Whenever your script encounters an HTTP Response that is not 200 or 404, your script does nothing. So, if a host has a temporary error, or a redirection, or tells you to try again, or simply gives you some information, or any of the other ~60 possible responses, you simply ignore that host completely.

convert space separated string in bash to list in ruby

I have a space separated string of IPs that I am exporting to a Vagrantfile which I want to iterate over.
export IP="10.10.10.10 10.10.10.11"
I want to perform an operation on this so it becomes a list in the vagrantfile to iterate over.
["10.10.10.10", "10.10.10.11"]
What is the way to do this?
Try figure it out yourself in bash:
$ export IP="10.10.10.10 10.10.10.11"
$ irb # interactive ruby
> puts ENV['IP'] # make sure IP is not nil
10.10.10.10 10.10.10.11 # output
> IPs = ENV['IP'].split
> puts IPs
Vagrantfile is a Ruby script, so you can use ENV['IP'].split in it
The following should be robust. You would not need to worry about padding by space characters at the beginning or the end of the string, or about irregular sequences of space characters.
"10.10.10.10 10.10.10.11".scan(/\S+/)
# => ["10.10.10.10", "10.10.10.11"]
You can just use Split directly as from the examples,
" now's the time".split
=> ["now's", "the", "time"]
>> "10.10.10.10 10.10.10.11".split
=> ["10.10.10.10", "10.10.10.11"]
>> "10.10.10.10 10.10.10.11".split
=> ["10.10.10.10", "10.10.10.11"]
>> " 10.10.10.10 10.10.10.11".split
=> ["10.10.10.10", "10.10.10.11"]
>> "".split
=> []
Read the Docs here
To include variables in vagrant file, Refer this
Use split in a simple way
"10.10.10.10 10.10.10.11".split(' ')
=> ["10.10.10.10", "10.10.10.11"]

How to issue multiple commands to stdin in linux shell with <<<printf

I'm exercising with DVWA high level command injection. I know there is a hole for |, but I'm looking for a way to get an output like the following:
root#vwksOffensive:~# ping -c 4 10.0.0.1 ; ls
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3066ms
a a_post Documenti Immagini Modelli Musica pocl rockyou.txt Scrivania WebScarab.properties
Add b hash JavaSnoop.properties mtu plain Pubblici Scaricati Video
root#vwksOffensive:~#
starting from
<<<printf "[ping ip argument] \u003B the_command_I_choose"
My problem is that can do this:
root#vwksOffensive:~# ping -c 4 <<<printf "10.0.0.1"
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3055ms
root#vwksOffensive:~#
but I'm not able to do this:
root#vwksOffensive:~# ping -c 4 <<<printf "10.0.0.1 \u003b ls"
PING 10.0.0.1 \u003b ls (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 \u003b ls ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3075ms
root#vwksOffensive:~#
and not even this:
root#vwksOffensive:~# ping -c 4 <<<printf "10.0.0.1 ; ls"
PING 10.0.0.1 ; ls (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 ; ls ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3075ms
root#vwksOffensive:~#
I searched deeply and the most similar, if it could be said so, is the first answer to this question:
How to make a bash function which can read from standard input?
Unfortunately, this did not help me completely, so I decided to post here my question because I'm sure that, in my ignorance, I'm missing something.
The solution must contain the char ; or any other needed special char coded in unicode. The forbidden, substituted with blank, char are the following:
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
if the "command you choose" is in a variable like this:
mycmd="ls -l"
then you can wrap your command and other commands in a sub-shell surrounded by this: ( )
The output for the sub-shell can be re-directed into any other command that you want. e.g. tail, grep, > $log, etc.
The ip addresses that you want to send ping can also be in a variable name:
myip=10.0.0.1
(ping $myip ; $mycmd ) > logfile.txt
It is not clear why you would want to send it in with <<< instead of directly on the command line. If you must use <<<, then still try wrapping the commands in () parenthesis and put a carriage return after the <<< 10.0.0..
and the "ls -l". The parenthesis will tell bash that the command is not done until the closing parenthesis is seen. The carriage return will stop the <<< from consuming the next command.

How can I get the output of an ssh command?

I'd like to programmatically check if someone has their SSH keys set up correctly for GitHub. I understand that I can use `ssh -T git#github.com` in Ruby. However, I'd like to keep the ssh output in a variable.
My current code is:
github_response = `ssh -T git#github.com`
unless github_response.start_with?('Hi')
puts 'Please set up your GitHub ssh keys'
end
`ssh -T git#github.com` outputs the response (starting with "Hi"). However the github_response variable is nil.
How can I assign the output of `ssh -T git#github.com` to github_response?
Your example failed because the Hi xxx! You've successfully authenticated.... message is not from stdout, but stderr.
> require 'open3'
=> true
> stdin, stdout, stderr, wait_thr = Open3.popen3('ssh -T git#github.com')
=> [#<IO:fd 8>, #<IO:fd 9>, #<IO:fd 11>, #<Thread:0x007f89ee1149a8 sleep>]
> stdout.gets
=> nil
> stderr.gets
=> "Hi halfelf! You've successfully authenticated, but GitHub does not provide shell access.\n"
You could add -v for verbose output, it will then dump much of the connection info to stdout. From that log you can scrape to find whether the server accepted any of the keys the ssh client offered

Multiple SSH hops with Net::SSH (Ruby)

So here's my setup:
Laptop -> Host 1 -> Host 2 -> Host 3
Laptop can reach Host 1, but not Host 2 or Host 3
Host 1 can reach Host 2, but not Host 3
Host 3 can reach Host 2, but not Host 1
What I'm trying to do is set up remote forwards so that a process running on Host 3 will be routed to running service on Laptop. I've successfully done this using the following code:
Run from Laptop:
require 'rubygems'
require 'net/ssh'
threads = []
config = {:user => "user", :remote_port => 3333, :service_port => 2222}
threads << Thread.new{
Net::SSH.start("host1", config[:user]) do |ssh|
puts "Forwarding port #{config[:remote_port]} on host1 to #{config[:service_port]} on localhost"
ssh.forward.remote(config[:service_port], "localhost", config[:remote_port], "127.0.0.1")
ssh.exec! "ssh #{config[:user]}#host2 -R #{config[:remote_port]}:localhost:#{config[:remote_port]}"
ssh.loop {true}
end
}
threads << Thread.new{
Net::SSH.start("host3", config[:user]) do |ssh|
puts "Creating local forward for port #{config[:service_port]} on host3 to port #{config[:remote_port]} on host2"
ssh.exec! "ssh #{config[:user]}#host2 -L #{config[:service_port]}:localhost:#{config[:remote_port]}"
ssh.loop {true}
end
}
threads.each {|t| t.join}
In one thread, I'm setting up a remote forward from Laptop to Host 1 and then another remote forward from Host 1 to Host 2. In a separate thread, I'm starting another connection from Laptop to Host 3, then running a local forward from Host 3 to Host 2.
The only way I can connect from Laptop to Host 3 is because of my .ssh/config file, which automatically routes me through Host 1 and Host 2 when I try to connect to Host 3 from Laptop.
What I want to do is cut out the second thread where I'm connecting from Laptop to Host 3 so that I can remove the dependency on the .ssh/config file. I want to do all of my connections from within the first thread.
So basically I need to do multiple hops that originate from Laptop. I can initiate the first connection from Laptop to Host 1 and then execute a command on Host 1, but after that I can't get any further. What I need to do is initiate the connection from Laptop to Host 1, set up the forward on Host 1, connect to Host 2 from Host 1 and then set up the second forward on Host 2.
Is this possible to do with net/ssh?
Thanks for your help!
I fixed this by writing out an SSH config file, then specifying the config file when initiating the connection. The config file contains proxy commands that will automatically route through the necessary hosts to reach my destination.
Example:
def write_config_file
File.open('confi_file', 'w') { |f|
f << "host host1\n"
f << "HostName host1.something.net\n"
f << "user user_name\n"
f << "\n"
f << "host host2\n"
f << "HostName host2.something.net\n"
f << "ProxyCommand ssh host1 nc %h %p 2> /dev/null\n"
f << "user user_name\n"
f << "\n"
f << "host host3\n"
f << "HostName host3.something.net\n"
f << "ProxyCommand ssh host2 nc %h %p 2> /dev/null\n"
f << "user user_name\n"
}
end
write_config_file
Net::SSH.start("host3", "user_name", :config => './config_file') do |ssh|
#whatever you need to do...
end
I wrapped the connection in a begin/rescue blocked and trapped ctrl+c input, and in the trap block I delete the config file and shut down the connection.

Resources