Expect script to read IP from hosts.txt and commands from commands.txt and implement script further to grab general info of server - scripting

There are two files:
1.hosts.txt
2.commands.txt
I intent to write a script to grab 1st IP from hosts.txt and run 1st command from commands.txt.
Then,script goes to 2nd IP form hosts.txt and run 2nd command from commands.txt
is it possible?if,yes how?
i tried writing below expect script but failed to get desired output
Below is the code and the optput
#!/usr/bin/expect -f
#This Script will Commission Multiple Gateway with TUI
set fid [open ./hosts.txt r]
set contents [read -nonewline $fid]
close $fid
# Get the commands to run, one per line
set q [open "commands.txt"]
set commands [split [read $q] "\n"]
close $q
foreach host [split $contents "\n"] {
#Loggin into the Gateway as Normal user
spawn -noecho ssh -o StrictHostKeyChecking=No expect#$host
expect "password:"
send "$pass\n"
#foreach commands [split $commands "\n"]
# Iterate over the commands
foreach cmd $commands {
expect "% "
send "$cmd\r"
}
}
**the output of this script:**
script logs into the server one by on which are there in hosts.txt but run all the commands mentioned in commands and not as i want which is stated above
Note:i set variables for username passowrd which i mention in along with ./ while executing command so leva that part

This is where you'll use Tcl's "multi-list" foreach formulation:
set hosts [split $contents \n]
foreach host $hosts cmd $commands {
# handle unequal size lists
if {$host eq "" || $cmd eq ""} then continue
spawn -noecho ssh -o StrictHostKeyChecking=No expect#$host
expect "password:"
send "$pass\r"
expect "% "
send "$cmd\r"
expect "% "
send "exit\r"
expect eof
# or, instead of the above expect-send pairs, do
# spawn -noecho ssh -o StrictHostKeyChecking=No expect#$host $cmd
# send password
# expect eof
}

Related

Automated ssh-copy-id for a non-root user using expect

I'm a newbie to Linux, Bash and Expect
I'm currently working on a script which automatizes the ssh-keygen and ssh-copy-id processes
This is the wanted behavior:
Create SSH keys from root, for a non-root user( the rsa one )
Copying them on a given remote server
By SSH, ssh-copy-id the remote-host's keys back to the non-root user
Basically, to create a password-less ssh connection between them, all automatized on the main server.
I'd like to avoid the "su nagios" command, because it will interrupt the script waiting for a command ( or if it's not true, help me to use it correctly ).
The expect script:
#!/usr/bin/expect
set user [lindex $argv 0]
set server [lindex $argv 1]
send_user $user
send_user $server
spawn ssh-copy-id $user#$server
expect {
"(yes/no)? " {
send "yes\r"
exp_continue
}
"password: " {
send "nagios\r"
send_user "\n"
}
}
expect "$ "
spawn ssh -v $user#$server "ssh-copy-id nagios#192.168.174.142"
expect {
"(yes/no)? " {
send "yes\r"
exp_continue
}
"password: " {
send "nagios\r"
send_user "\n"
}
}
expect "$ "
exit
What I'm trying to do:
to create the keys in the right folder:
ssh-keygen -t rsa -N "" -f /home/nagios/.ssh/id_rsa
to change the text regarding user and domain
sed -i s/"root#localhost.localdomain"/"nagios#localhost.localdomain"/g /home/nagios/.ssh/id_rsa.pub
then I just copy them for the ssh-copy-id command..
cp -Rfv /home/nagios/.ssh/id_rsa /root/.ssh/
cp -Rfv /home/nagios/.ssh/id_rsa.pub /root/.ssh/
Are the keys generated binded to the user who created them?
Is that "root#localhost.localdomain" just a "comment" as I've read while browsing on internet?
Is there a way to use ssh-copy-id from root for the non-root user?
Errors I'm facing :
/usr/bin/ssh-copy-id: ERROR: Host key verification failed.
expect: spawn id exp5 not open
while executing
"expect "$ ""
(file "./copyRemoteKeys.sh" line 36)
May someone help me to debug, fix, or even help me to understand better how these things works?

2 step login via ssh using expect

I'm trying to create a script that logs in via ssh, but to make it a littler harder I have to ssh into one server and then ssh into another to get to the server I want to be on. The code I have so far works fine to get me into the first server, but doesn't work when it then tries to ssh into the next.
#! /usr/bin/expect -f
# set servers to ssh into
set server1 "foo"
set server2 "bar"
# get username
send_user -- "Enter username: "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
# get password
# don't ouput password to the user
stty -echo
send_user -- "Enter password for \"$user\": "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set password $expect_out(1,string)
# start the login
spawn ssh $user#$server1
expect "assword:"
send -- "$password\r"
# problems happen after here
# required to ssh from the <something> into <something else>
# the host name varies, e.g. host1, host2, host3, etc.
# so this just detects the last character
expect -re "\$ $"
send -- "ssh $user#$server2\r"
# sometimes it outputs this, sometimes it doesn't
expect "(yes/no)?"
send -- "yes\r"
expect "assword:"
send -- "$password\r"
interact
It's the same username and password for both servers.
You made mistake in
expect -re "\$ $"; # WRONG
If you want to match the literal dollar sign along with end of line, you have to use
expect -re "\\\$ $"; # CORRECT
which will match literal dollar sign and a space at the line end.
Alternatively, to match some common known prompt, you can define a variable like
set prompt "#|>|%|\\\$ $"; # We escaped the `$` symbol with backslash to match literal '$'
The last dollar sign represents line-end.
While the expect is used, we have to accompany with -re flag to specify that as a regular expression.
expect -re $prompt
If we don't know whether a particular word will arrive in the output or if we are not sure about the order of occurrence, then it is advised to use exp_continue.
send -- "ssh $user#$server2\r"
# sometimes it outputs this, sometimes it doesn't
expect {
"(yes/no)?" {send -- "yes\r";exp_continue}
"ssword:" {send -- "$password\r"}
}
expect -re $prompt

How to pass multiple servers to an expect script?

I'm trying to use an expect script to change my password on multiple servers, but I'm a little confused as to how to pass the list of servers through to it.
The script that I'm using is as follows:
#!/usr/bin/expect -f
# wrapper to make passwd(1) be non-interactive
# username is passed as 1st arg, passwd as 2nd
set username [lindex $argv 0]
set password [lindex $argv 1]
set serverid [lindex $argv 2]
set newpassword [lindex $argv 3]
spawn ssh -t $serverid passwd
expect "assword:"
sleep 3
send "$password\r"
expect "UNIX password:"
sleep 3
send "$password\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect eof
And I'm trying to run it as such:
[blah#blah ~]$ ./setkey1 blah password 'cat serverlist' meh
which gives me the following output:
spawn ssh -t cat serverid passwd
ssh: cat serverid: Name or service not known
send: spawn id exp6 not open
while executing
"send "$password\r""
(file "./setkey1" line 13)
So I then tried running the following for loop:
[blah#blah ~]$ for i in serverid; do `cat serverid`; ./setkey1 blah password $i meh; done
Which gave me the following:
-bash: staging01v: command not found
spawn ssh -t serverid passwd
ssh: serverid: Name or service not known
send: spawn id exp6 not open
while executing
"send "$password\r""
(file "./setkey1" line 13)
If I try using the expect script, and just enter in one server name, it works as...um...expected.
What am I doing wrong?
There are many ways to solve this problem. I'd change the order of your arguments to be able to pass in multiple servers.
In the expect program:
foreach {username password newpassword} $argv break
set servers [lrange $argv 3 end]
foreach serverid $servers {
# your existing code goes here
}
Then from the shell, invoke it like this
./setkey1 userid pass newpass $(cat servers.txt)
If you use bash, you can do
./setkey1 userid pass newpass $(<servers.txt)
I did something similar here is my way
set servers "server1 server2 server3"
set users "user1 user2 user3"
set passwords "password1 password2 password3"
set newpasswords "new1 new2 new3"
foreach server $servers user $users password $passwords newpassword $newpasswords
{
Your commands using the variables server/user/password/newpassword
}
this will run the commands in a loop for each element in the sets
so first server with first user with first password in a loop then goes to 2nd etc..

How to allow entering password when issuing commands on remote server?

Here's my script:
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 SERVER"
exit 255
fi
ssh $1 'su;apachectl restart'
I just want it to log in to the server and restart apache, but it needs super-user priviledges to do that. However, after it issues the su command it doesn't stop and wait for me to enter my password. Can I get it to do that?
See if this works for you! This solution takes the password before doing the SSH though...
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 SERVER"
exit 255
fi
read -s -p "Password: " PASSWORD
ssh $1 'echo $PASSWORD>su;apachectl restart'
The -s option prevents echoing of the password while reading it from the user.
Take a look at expect. The best way to perform such operations is through an expect script. Here is a sample that I just typed up to give you a head start, but all it does right now is show you how to handle a password in expect.
#!/usr/bin/expect -f
set timeout 60
set pswd [lindex $argv 0]
send "su\r"
match_max 2000
expect {
-re "assword:$" {
sleep 1
set send_human {.2 .15 .25 .2 .25}
send -h -- "$pswd\r"
exp_continue
} "login failed." {
send "exit\r"
log_user 1
send_user "\r\nLogin failed.\r\n"
exit 4
} timeout {
send "exit\r"
log_user 1
send_user "\r\nCommand timed out\r\n"
exit 1
} -re "(#|%)\\s*$" {
# If we get here, then su succeeded
sleep 1
send "apachectl restart\r"
expect {
-re "(#|%)\\s*$" {
send "exit\r"
log_user 1
send_user "\r\nApache restart Successful\r\n"
exit 0
} timeout {
send "exit\r"
log_user 1
send_user "\r\nCommand timed out\r\n"
exit 1
}
}
}
}
Modifying your command to:
ssh -t $1 'sudo apachectl restart'
will open a TTY and allow sudo to interact with the remote system to ask for the user account's password without storing it locally in memory.
You could probably also modify your sudo config on the remote system to allow for execution without a password. You can do this by adding the following to /etc/sudoers (execute visudo and insert this line, substituting <username> appropriately.)
<username> ALL=NOPASSWD: ALL
Also, I'm a security guy and I really hope you understand the implication of allowing an SSH connection (presuming without a passphrase on the key) and remote command execution as root. If not, you should really, really, really rethink your setup here.
[Edit] Better still, edit your sudoers file to allow only apachectl to run without a password. Insert the following and modify %staff to match your user's group, or change that to your username without the percent sign.
%staff ALL=(ALL) NOPASSWD: /usr/sbin/apachectl
Then your command should simply be changed to:
ssh $1 'sudo apachectl restart'

Transitioning Expect script from Telnet to SSH only

Any help would be appreciated. I need to transition inherited expect scripts from Telnet to SSH only logins. First of, I'm a router guy that inherited all our expect script templates, written a while back. So far, with little modifications, they've been able to run smoothly. Our client wanted to move away from Telnet and so a few months ago we prepped all the Cisco routers and switches to support both Telnet and SSH. Until now, our scripts had been fine. However, Telnet support and servers will go away soon and I need to figure out how to reconfigure all the script templates to work in SSH only environments.
So, here's an example of a simple template to get a sh ver output:
#!/usr/local/bin/expect -f
#
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
####################################################################
# Info for command line arguments
set argv [ concat "script" $argv ]
set router [ lindex $argv 1 ]
####################################################################
set timeout 15
set send_slow {1 .05}
spawn telnet $router
match_max 100000
expect Username:
sleep .1
send -s -- "user\r"
expect Password:
sleep .1
send -s -- "pass\r"
expect *
send -s -- "\r"
expect *
sleep .2
send -s -- "sh ver\r"
expect *
sleep .2
send -s -- "end\r"
expect *
sleep .2
send -s -- "wr\r"
expect *
sleep .2
send -s -- "exit\r"
expect *
sleep .2
expect eof
Instead of:
spawn telnet $router
match_max 100000
expect Username:
sleep .1
send -s -- "user\r"
expect Password:
sleep .1
send -s -- "pass\r"
Try to use:
spawn ssh -M username#$router
while 1 {
expect {
"no)?" {send "yes\r"}
"sername:" {send "username\r"}
"assword:" {send "password\r"}
">" {break}
"denied" {send_user "Can't login\r"; exit 1}
"refused" {send_user "Connection refused\r"; exit 2}
"failed" {send_user "Host exists. Check ssh_hosts file\r"; exit 3}
timeout {send_user "Timeout problem\r"; exit 4}
}
}