How to enter a password into another process prompt from Ruby - ruby

I am writing an application that needs to run command on a remote Raspberry PI using a revssh script. revssh is a custom script that implements to some level the Revssh protocol concepts. it uses ssh reverse tunneling to send commands from the server to the clients.
I am using Ruby 2.1, I tried to do this using IO.popen but it does not work, so I tried the following:
# revssh (short for reverse ssh ) enables the execution of remote commands
# from the server on connected clients, like the 'psu_pi_analytics' here. but it requires
# to enter a root password each time you want to run a command using 'revssh -c'
IO.popen('revssh -c psu_pi_analytics uname -a', 'w+') do|io|
io.puts 'password' # enter the password when prompted
puts io.gets
end
this code work if the command to execute run on the local machine, but not in my case.
So any thoughts, or suggestions.
What important here is how to deal with the new connection created by the revssh script using ssh, which is managed in the terminal if the script is run directly from the terminal.
Edit:
By not work I mean it still prompts for the password, even if I puts the password to the io.

You can use an Expect-like library (e.g. RExpect, Expect4r) for interacting with other processes.
Another question related to this: Is there an Expect equivalent gem for Ruby?

Related

Redirect input to a Java application inside a bash script

I am using the ftm cli by Oracle. When I run a command, it asks for a password by saying "Enter a password". I am writing a script which uses this command and so I want to enter the password programatically.
Here's what I have tried -
echo "password" | java -jar ftmcli.jar list
But this doesn't work. Any idea what to do to make it work?
Without being able to look into the source code of ftmcli, I presume that Oracle is using Console.readPassword() from the java.io.Console class. However, there is a snag with this function:
public final class Console
Methods to access the character-based console device, if any,
associated with the current Java virtual machine.
Whether a virtual machine has a console is dependent upon the
underlying platform and also upon the manner in which the virtual
machine is invoked. If the virtual machine is started from an
interactive command line without redirecting the standard input and
output streams then its console will exist and will typically be
connected to the keyboard and display from which the virtual machine
was launched. If the virtual machine is started automatically, for
example by a background job scheduler, then it will typically not have
a console.
If this virtual machine has a console then it is represented by a
unique instance of this class which can be obtained by invoking the
System.console() method. If no console device is available then an
invocation of that method will return null.
By piping/redirecting the stdout of echo to the JVM, the Java method System.console() will return null and hence not read anything from your redirection.
Look at the following questions which handle the same problem, but on Java side: "How to handle java passwd reading when System.console() returns null?" or How to pipe input to Java program with bash
On your side, there is more or less nothing you can really do. Executing echo $password | java -jar ftmcli.jar list or java -jar ftmcli.jar list <<< $password will always fail as long Oracle doesn't change the way how ftmcli reads the password from stdin.
If you are ready for a one time password setup, then follow the below steps.
run java -jar ftmcli.jar --save-auth-key
You will be prompted for teh password and once it is given, the file will be uploaded and a ftmclikeystore file is created under ftmcli folder.
Now as long as if you don't change the user, then ftmcli will take the password from this file.
Found this little but fantastic tool called expect does the magic:
$ ./expect.script
spawn java -cp /tmp/brm-test/oraclepki.jar:/tmp/brm-test/BRMActions.jar com.oracle.installer.brm.BRMInstallHelper 7 /tmp/brm-test/client upgC
Enter Password for the wallet:
$
Perhaps it's not very much visible in the snippet above, but it is working:
$ cat /tmp/brm-test/client/.wp
upgC=MyMag1cPa$$word#
What is in the expect.script?
$ cat expect.script
#!/usr/bin/expect
stty -echo
spawn java -cp /tmp/brm-test/oraclepki.jar:/tmp/brm-test/BRMActions.jar com.oracle.installer.brm.BRMInstallHelper 7 /tmp/brm-test/client upgC
expect "Enter Password for the wallet:"
send "MyMag1cPa$$word#\r"
interact
Seems this can be used also in Chef, see this cookbook.
I hope it will help,
Jarek

Writing Automated scripts to configure device

My requirement is like this:
I need to log in to a remote device (say Router/switch) and execute following commands.
telnet xx.xx.xx.xx
//give password here
sys
interface g x/x/x
shut
desc free-port
exit
There are Hundreds of devices for which I cannot waste time doing above damn thing 100 times. I need to write a automated script which does it. so My questions are as follows:
I use Windows system, so What is the best scripting language to be used : Ruby / shell script / perl ? (I was formerly ROR Developer, so i know Ruby, Linux terminal. Now I am working in networking domain. )
What I thought was : Put all Devices into an array and using for loop, call devices one by one and execute above said commands.
I don't have knowledge of scripting, so please guide me further. I don't know where to start from.
Step 1: decide the file structure of your program.
For example, this is the simplest structure
if_admin/
|--config.yml
|--run.rb
Step 2: write a config file or a bunch of config files that contain the different parts of the commands you need to run on the targets.
For example, you can use a yaml file like this:
xx.xx.xx.xx:
password: s3cret
router-shelf: x
slot: x
port: x
yy.yy.yy.yy:
...
Step 3: implement what you want to do
require 'yaml'
require 'net/telnet'
config = YAML.load_file('./config.yml')
config.each do |host, conf|
telnet = Net::Telnet.new('Host' => host)
telnet.login(conf['password'])
telnet.puts <<-CMD
sys
interface g #{conf['router-shelf']}/#{conf['slot']}/#{conf['port']}
shut
desc free-port
CMD
telnet.close
end
If you can use expect script , you are in luck.
#!/usr/bin/expect
set timeout 60
set cmds [list "ssh host1 ..." "ssh host2 ..." "ssh host3 ..."]
foreach cmd $cmds {
spawn -noecho bash -c $cmd
expect {
-re "password" {
exp_send "$env(PASS_WORD)\"
exp_continue
}
eof { wait } ; # at this time the last spawn'ed process has exited
}
}
Here is the rough idea of above script :-
set cmds [list.... will be used as list to store set of commands.
foreach will iterate though those commands
spawn will spawn process for each of the command. you can write multiple command with single telnet in bash, just break down commands using \ (backslash) so it is easily readable and extendable.
expect block will pass password whenever it encounter certain regex.
eof will wait till all commands in spawn process are finish.
set timeout -1 will keep loop running. i think default time for expect script is 10secs.
You can create one more foreach loop for host-list.
I think this will be enough to get you started for your automation process.
As to the question of "What is the best scripting language to be used", I would say go with one that does what you need and one that you're comfortable with using.
If you want to go with Perl, one module that you could use is Net::Telnet. Of course, you'll need Perl itself. I'd recommend using Strawberry Perl, which should already have Net::Telnet installed.
Another possible route is to use putty, which is a SSH and telnet client. You could combine that with TTY Plus, which provides an interface that uses tabs for different putty sessions. And it lets you issue commands to multiple putty sessions. This is one possibility that wouldn't involve a lot of code writing.

Execute multiple commands via SSH and PowerShell

I successfully managed to connect to a Cisco IE-2000-L switch via SSH. I used the Renci SSH.NET library.
Starting guide: http://vwiki.co.uk/SSH_Client_(PowerShell)
My working code is
# Load SSH library (for .NET 4.0 and PowerShell 3)
$DllPath = "D:\temp\Renci.SshNet.dll"
[void][reflection.assembly]::LoadFrom( (Resolve-Path $DllPath) )
# Connect to switch (Cisco IE2000-L) with IP, port, username, password
$SshClient = New-Object Renci.SshNet.SshClient('172.20.91.30', 22, 'admin', 'mypassword')
$SshClient.Connect()
# execute one command on Cisco switch
$SshCommand = $SshClient.RunCommand('show arp')
# show result
$SshCommand.Result
# close SSH connection
$SshCommand.Dispose()
$SshClient.Disconnect()
$SshClient.Dispose()
My problem is
The above code sends just one command. But I want to execute several commands consecutively without closing and reopening a session.
If I add a second command right after the first one
# execute one command on Cisco switch
$SshCommand = $SshClient.RunCommand('show arp')
$SshCommand = $SshClient.RunCommand('show start')
...the script hangs and never finishes. What am I doing wrong?
Minor relevant information
My main goal is to send multiple commands at once to a Cisco switch
I already tried Plink together with batch cmd input. It's not reliable enough. It works sometimes and sometimes not.
I already tried telnet scripting. Too awkward.

Best way to run an entire script via ruby -e

I've been wanting to run some ruby scripts on remote computers (in a bash shell)
I could create a sequence of bash commands of ruby -e "<command>", but some of these scripts are over 100 lines.
ruby -e with a HEREDOC or %{} & eval() doesn't work well with the mixture of single and double quotes.
Is there a better way to attempt this?
Edit:
The protocol being used is Apple Remote Desktop, which executes these commands in the scope of the remote shell.
If I understand you correctly, you want to run local ruby script on remote machine via SSH or similar protocol. If the script is non-interactive (i.e. doesn't require any user input), you could create it locally and deliver through stdin.
In other words, first write the script and save it locally as, say, foo.rb. Then:
ssh remotehost ruby < foo.rb
That with start the SSH session and execute the remote ruby interpreter. With no arguments, the ruby interpreter executes commands from standard input, and thus we feed SSH with the program on stdin.
As I also want to run ruby scripts via ARD (which I don't think can embed a ctrl-D), I first thought you could combine joraff's solution (to his own problem) with Kelvin's:
cat << ENDOFSCRIPT | ruby
#Here be code ...
ENDOFSCRIPT
Which saves creating/deleting a file.
But there's an even better way:
It turns out (duh) that ARD embeds an EOF or just otherwise terminates what it sends in such a way that you can simply do:
ruby
#Paste whole script here
Works at least in ARD 3.6.1. Win!
This worked:
cat << 'EOF' > /tmp/myscript.rb
# ruby script contents go here. any syntax is valid, except for your limit string (EOF)
EOF
ruby /tmp/myscript.rb;
rm /tmp/myscript.rb;
Since this isn't relying on how an interpreter binary handles stdin-style commands, it will work for most other languages as well (python, perl, php).
Why not send the script over first?
scp foo.rb remotehost:
ssh remotehost "ruby foo.rb"
You could even clean up the file after:
ssh remotehost "rm foo.rb"

Ruby scripting - Telnet Hangs During Login

I am trying to do some basic scripting using ruby to log in to a windows machine via telnet and pull some files over using the dos command line ftp. When I do this manually everything goes swimmingly but when I try it via ruby I'm getting an error in the login call.
Here is my test program in its entirety:
require 'net/telnet'
tn = Net::Telnet::new("Host"=>"xxx.xxx.xxx.xxx", "Timeout"=>25, "Output_log"=>"output_log.log", "Dump_log"=> "dump_log.log", "Prompt"=>"C:.*>")
tn.login("administrator", "xxxxxxx") {}
tn.cmd('dir')
exit
The contents of output_log don't betray anything as being wrong:
Trying 208.10.202.187...
Connected to 208.10.202.187.
Welcome to Microsoft Telnet Service
login: administrator
password:
*===============================================================
Welcome to Microsoft Telnet Server.
*===============================================================
C:\Documents and Settings\Administrator>
Same for the dump_log which has very similar but awkwardly formatted contents. When I run the program it sits around for a while and then outputs the following error:
PS C:\code\tools\deployment> ruby test.rb
C:/Ruby/lib/ruby/1.8/net/telnet.rb:551:in `waitfor': timed out while waiting for more data (Timeout::Error)
from C:/Ruby/lib/ruby/1.8/net/telnet.rb:685:in `cmd'
from C:/Ruby/lib/ruby/1.8/net/telnet.rb:730:in `login'
from test.rb:3
Which leads me to suspect that the telnet class is not recognizing the command prompt. I've tried several different regex strings for the Prompt parameter, including the default and nothing seems to help.
I think the prompt field needs to be a regexp, not a string
Try
tn = Net::Telnet::new("Host"=>"xxx.xxx.xxx.xxx", "Timeout"=>25,
"Output_log"=>"output_log.log", "Dump_log"=> "dump_log.log",
"Prompt"=> /C:.*>/)

Resources