SSH password change automation over a list of IPs - shell

I have a bunch of linux servers. What I am trying to do is making sure all the servers have the password I want by prompting all the logins change. I am trying to implement it by writing a short bash script which includes the expect command. I am running it over a file which contains the list of IPs.
My file ips.txt contains:
10.195.166.52
10.195.166.54
My "expect" script is:
#!/usr/bin/expect -f
log_file myfile.log ;# <<< === append output to a file
set timeout 180
set username pippo
set old_password 0ldp455w0rd
set new_password n3wp455w0rd
set filedes [open "ips.txt" r]
gets $filedes ip
while {$ip != -1} {
spawn ssh $username#$ip
expect "$username"
send "$old_password\r"
expect "(current)"
send "$old_password\r"
expect "New"
send "$new_password\r"
expect "Retype"
send "$new_password\r"
expect "$username"
send "exit\r"
#expect eof ;#wait for the connection to close
}
close $filedes
What I obtain is:
You are required to change your password immediately (root enforced)
Last login: Thu Jul 19 16:14:46 2018 from 10.222.233.139
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user pippo.
Changing password for pippo.
(current) UNIX password:
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
Connection to 10.195.166.52 closed.
send: spawn id exp6 not open
while executing
"send "exit\r""
("while" body line 12)
invoked from within
"while {$ip != -1} {
spawn ssh $username#$ip
expect "$username"
send "$old_password\r"
expect "(current)"
send "$old_password\r"
expect "New"
send "$ne..."
(file "./passwd.xp" line 12)
So the script is not proceeding further than the first IP. What is happening ? I am trying variations of the expect commands without success.

Related

Why does my expect script exit prematurely?

Here is my except script:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
I run it and see OpenVPN ask for my client password. But the script exits, apparently without ever sending the password. When I try with an incorrect password it is the same (no incorrect password message). It is also exactly the same result if I delete the send -- "mypassword\r" line from the end of the expect script.
It's my first expect script so probably my syntax is wrong. Or could it be that OpenVPN is kicking me off for using an expect script to connect?
Your syntax is fine. The problem is the script has no more commands to run after you send the password, so the expect script exits and that kills openvpn.
What do you need to do after you send the password?
If you just need to keep openvpn running, then do this:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
set timeout -1
expect eof
-1 means "infinite", and expect eof means you are waiting for the spawned process to exit before the expect script can exit.

expect telnet to multiple cisco devices and execute show run

I have the following scripts which is supposed to pull the IP address from a file device-list.txt and then telnet to the device, login and execute a show run. The script logs in to the device successfully, but gives me an error after that. Any ideas where im going wrong?
for device in `cat device-list.txt`; do ./test4 $device;done
cat test4
#!/usr/bin/expect -f
set hostname [lindex $argv 0]
set user myusername
set pass mypassword
set timeout 10
log_file -a ~/results.log
send_user "\n"
send_user ">>>>> Working on $hostname # [exec date] <<<<<\n"
send_user "\n"
spawn telnet $hostname
expect "Username:"
send "$user\n"
expect "Password:"
send "$pass\n"
expect "#"
send “term len 0\n”
send “show running-config\n”
expect “end\r”
send “\n”
send “exit\n”
User Access Verification
Username: myusername
Password:
ROUTER#usage: send [args] string
while executing
"send “term len 0\n”"
(file "./test4" line 26)
Your problem is due to incorrect double quotes. You have to use double quotes ", not smart quotes “
send “term len 0\n” ; # Wrong
send "term len 0\r"; # Correct
Note :
Basically, Expect will work with two feasible commands such as send and expect. If send is used, then it is mandatory to have expect (in most of the cases) afterwards. (while the vice-versa is not required to be mandatory)
This is because without that we will be missing out what is happening in the spawned process as Expect will assume that you simply need to send one string value and not expecting anything else from the session.
So, we recommend to use expect after each send, to make sure our commands actually sent to the spawned process. (You have not used expect after sending some commands such as term len 0\r). Also, use \r instead of \n.

Expect script for checking ssh connection for a list of ips

Can anyone help me in creating an expect script to just do an SSH on a list of servers and check if it was fine. I do not need to interact, do not need to fire any command on each server, I just want to do an ssh and come out, with a return code mentioning whether it was successful or not.
Expect is important here, as I do not have an option to setup a passwordless connection. Also there is a possibility that passwordless connection is setup on some of those servers.
I tried something like:
#!/usr/local/bin/expect
set timeout 10
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set prompt "(>|%|\\\\\\\$|#|]|) \$"
spawn ssh "$user\#$ip"
expect "Password:"
send "$password\r"
send "echo hello\r"
expect "hello"
send "exit\r"
But this gets stuck on the first server, and does nothing after that.
Thanks,
Piyush
A generalized idea can be having a procedure to spawn the ssh and close the connection which will maintain the connections local to the scope, so that global spawn_id won't get affected at all.
#!/usr/bin/expect
proc isHostAlive {host user pwd} {
# We escaped the `$` symbol with backslash to match literal '$'
# Commonly used prompt types
set prompt "#|%|>|\\\$"
set timeout 60
spawn ssh $user#$host
expect {
timeout {return FAIL}
"(yes/no)" {send "yes\r";exp_continue}
"password:" {send "$pwd\r";exp_continue}
-re $prompt
}
set msg "Hello World"
send "echo $msg\r"
expect {
timeout {return FAIL}
"\r\n$msg"
}
send "exit\r"
expect {
timeout {return FAIL}
eof
}
return PASS
}
# Lists to maintain the each host's information
set serverList {server1 server2 server3}
set userList {user1 user2 user3}
set pwdList {pwd1 pwd2 pwd3}
# Looping through all the servers and checking it's availability
foreach server $serverList user $userList pwd $pwdList {
puts "\n==>Status of $server : [isHostAlive $server $user $pwd]\n"
}
With exp_continue, we can handle even if any host does not have password. Basically exp_continue will cause the expect to run again. So, among the mentioned phrase whichever comes, it will be handled. i.e. if expect sees (yes/no), it will send yes, if expect sees password, it will send the password value and so on. Then expect will continue to wait for the whole set of phrases again.
The reason why I have added yes/no is because if suppose the host's RSA fingerprint needs to be saved.
After successful login, I am echoing Hello World and expecting for the echoed message. If you have noticed, I have used \r\n$msg in the expect statement. Why do we need \r\n here ?
Here is why. Whenever we send command that will be seen by the expect also and it will try to match against that too. If it matched, it will proceed as such.
Sample echo command output
dinesh#dinesh-VirtualBox:~/stackoverflow$ echo Hello World
Hello World
dinesh#dinesh-VirtualBox:~/stackoverflow$
The string we want to expect is already available in the send command. So, to make sure the matching expect string is only from the actual echoed response, I have added \r\n which will help us in matching what is necessary.
Then at last of the proc, I am sending exit command which will close the ssh connection and to match the same eof (End Of File) is used. In all sort of failure cases, the procedure will return FAIL.

Handle multiple statements in an Expect script

I am new to Expect scripting.
I wrote an Expect script for ssh in a Linux machine, where I am facing a problem in ssh'ing to different Linux machines. Below I have copied the script.
!/usr/local/bin/expect
set LinuxMachine [lindex $argv 0]
spawn ssh root#$LinuxMachine
expect "root#$LinuxMachine's password:"
send "root123\n"
expect "[root#Client_FC12_172_85 ~]#"
send "ls"
interact
When I supply 10.213.172.85 from command line the expect in the 4th line, it reads as "root#10.213.172.85's password:" and logs in successfully
But some Linux will expect
The authenticity of host '10.213.172.108 (10.213.172.108)' can't be established.
RSA key fingerprint is da:d0:a0:e1:d8:7a:23:8b:c7:d8:40:8c:b2:b2:9b:95.
Are you sure you want to continue connecting (yes/no)
In this case the script will not work.
How can I have two Expect statements in one Expect command?
You can use exp_continue in such a case:
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"root#$LinuxMachine's password:" {
send "root123\r"
expect "[root#Client_FC12_172_85 ~]#"
send "ls\r"
interact
}
}
In the above, the Expect block waits for either the yes/no question OR the prompt for password. If the latter, it moves on with providing password, expecting prompt, sending ls command and giving control back.
If the former, it will answer 'yes' and repeat the expect block, ready to find the prompt for a password (or even the yes/no question again, for that matter - not that you will need that).
I would also include some timeouts with meaningful messages in case some expect option does not match as expected, but the above should work.
As a side comment, you don't want to set a root password in a script... I recommend using ssh key authentication.
We like to call it "long log in". There are ssh options that don't check the host keys:
send -- "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no username#host\n"
expect {
"Password" {
send -- "$passwd\n"
}
Part of the Bash script that calls on the expect sets the password:
echo -n "Enter LDAP password: "
read -s passwd
echo

Expect script erroring from Nagios, but good from cli

I'm now learning Expect to try to automate a check on a remote system under Nagios Core, and while the script runs perfectly from the 'nagios' user account (which is what Nagios runs its commands under), the script errors under a Nagios run. In fact, the script run from the command line worked stright away without much drama - but now I've spent 3 days trying to figure out what's going wrong under Nagios.
I've determined that the CLI version sees the response from the send "aaa test-server... command, but the Nagios run doesn't and just times out. I've written things to the logfile to try to determine what I've done wrong, and I just can't see it. (I've disabled responses back to Nagios because that'll confuse it, but they're seen and used in the script.)
Sequence I'm trying to automate:
nagios:~# ssh accountname#192.168.1.20
accountname#192.168.1.20's password:
Last login: Wed Mar 27 03:43:03 2013 from 192.168.1.14
(Aruba3400) >en
Password:***********
(Aruba3400) #aaa test-server mschapv2 tester account1 password1
Authentication Successful
(Aruba3400) #exit
(Aruba3400) >exitConnection closed by foreign host.
Connection to 192.168.1.20 closed.
Script:
#!/usr/bin/expect
# turn on logging output
log_user 0
log_file /tmp/output.txt
# set Variables
set password SomeThingOrOther
set useracct accountname
set ipaddr 192.168.1.20
set timeout 9
match_max -d 10000
# now connect to remote UNIX box (ipaddr) with given script to execute
spawn -noecho /usr/bin/ssh $useracct#$ipaddr
# Look for password prompt
expect "assword:"
# Send password aka $password
send "$password\n"
# send blank line (\r) to make sure we're at the prompt
expect "Aruba3400"
send "\r"
#send_log "log: 1st password entered.\n"
# Look for password prompt again, send Enable req
expect "Aruba3400"
send "en\r"
expect "assword:"
send "$password\r"
expect "(Aruba3400) #"
# send blank line (\r) to make sure we're at the prompt
send "\r"
expect "#"
send_log "log: Sent auth command.\n"
send "aaa test-server mschapv2 tester account1 password1\r"
# at next line, nagios run times out; command line runs fine
expect -timeout 9 "Successful"
# printout some debugs to logs whether it works or times out
send_log "log 0: $expect_out(0,string)\n"
send_log "log x: $expect_out(buffer)\n"
#---stock exit here so I can see some output, fix it later----
set TIME [exec date +%H:%M:%S]
log_user 1
send_user "Time: $TIME\n"
send_log "-----------------------\n"
exit 0
Output of logs file:
-----------------------
log: Sent auth command.
log 0: Successful
log x: aaa test-server mschapv2 tester account1 password1
Authentication Successful <-- this is from the cli (works!)
Time: 16:00:02
-----------------------
log: Sent auth command.
log 0: #
log x:
(Aruba3400) # <-- this is under Nagios (timeout)
Time: 16:00:29
-----------------------
Can anyone provide me some clues as to what is going wrong here?
Thanks in advance!!

Resources