expect script + expect miss the send string + delay issue - linux

I write the active.ksh script (based on expect) in order to login automatically to some Solaris machine and execute the hostname command (login to virtual IP in order to verify which hostname is the active machine - I have two cluster solaris machines )
The problem is with expect; expect sends the password string (pass123) and it misses the Password question, and it still waits for the password.
So actually the password (pass123) was entered after password question. On most cases the expect script works fine but sometimes it missed the password.
EXAMPLE OF THE PROBLEM
./active.ksh
spawn ssh 10.10.18.61
sh: /usr/local/bin/stty: not found
This computer system, including all related equipment, networks and network devices (specifically including Internet access),is provided only for authorized uss
Password: * my remark - pass123 string was missed the Password Question pass123
Password:
THE SCRIPT
more active.ksh
#!/bin/ksh
VIP_ADDRESS=10.10.18.61
expect_for_verify_which_active_machine=`cat << EOF
set timeout -1
spawn ssh $VIP_ADDRESS
expect {
")?" { send "yes\r" ; exp_continue }
Password: {send "pass123\r"}
}
expect > {send "hostname\r"}
expect > {send exit\r}
expect eof
EOF`
expect -c "$expect_for_verify_which_active_machine"
EXAMPLE OF RIGHT RESULTS
./active.ksh
[Friday, February 24, 2012 2:32:06 PM IST] INFO Verify which is active SMU machine
spawn ssh 10.10.18.61
sh: /usr/local/bin/stty: not found
This computer system, including all related equipment, networks and network devices (specifically including Internet access),is provided only for authorized uss
yes
Password:
Last login: Fri Feb 24 14:32:06 2012 from smu1a
This computer system, including all related equipment, networks and network devices (specifically including Internet access),is provided only for authorized uss
solaris1:/ ROOT > hostname
solaris1
solaris1:/ ROOT > exit
logout
Connection to 10.10.18.61 closed.

Maybe it will be easier to script this process using empty. It's a small utility similar to expect but more convenient in some cases. You can use it to just "type text into an application", and you can ignore the application's prompts (though you don't have to). Logging to a remote machine and executing hostname could look like this:
./empty -f -L templog ssh 10.10.18.61 hostname
echo YOUR_SECRET_PASSWORD | ./empty -s -c
cat templog
The first one starts your command in the background. The other one sends it data (your password) from stdin. The whole session (including passwords!) is logged to file templog where you will find also the result of hostname. There are also mechanisms for finer-grained control, but they may be a bit harder to set up.
As some people already pointed out, ssh keys are the way to go, this is just an ugly and unsafe workaround (it's risky due to shell history being preserved, users being able to see your password in ps results, and in my example the password is additionally saved in the templog file etc).

Related

How to ssh from one server to multiple server and switch to root and then change root password

From server A, i want to ssh multiple server using a non root user as direct root login is disabled to all server.
then i need to su - to switch to root
perform some normal operations, like changing directory, listing file etc.
and at the end change the root password using passwd command
I have expect installed on all servers and i am able to ssh from server A to any of the other servers but stuck in switching to root user and performing other operations as listed above.
#!/bin/bash
ssh user#ip<<'ENDSSH'
./su2root
#random operations
pwd
whoami
#random operations
./changepass
ENDSSH
su2root
#!/usr/bin/expect -f
spawn su -
set password "rootpass"
expect "assword:"
send "$password\r"
changepass
#!/usr/bin/expect -f
set newpassword "newrootpass"
spawn passwd
expect "*assword*"
send "$newpassword\r"
expect "*assword*"
send "$newpassword\r"
expect eof
exit 0
expecting to accomplish it using shell scripting only. Thanks in advance.
My initial comment on your question noted that you should be looking into running expect locally and spawning ssh sessions to the remote servers.
While not exactly answering your question, here is a simple expect script that logs into a remote system (one of my systems at home named valhalla), uses sudo -i to become root and executes the id command to show the user UID/GIDs. Note that I use ssh keys for logins which is why there is no expect for the initial login password.
The remote systems do not need expect in this example.
#!/usr/bin/expect
# vi: set ts=2 sw=2 noai ic showmode showmatch:
spawn ssh valhalla
expect "valhalla:"
send "sudo -i\r"
expect "assword for *"
send "XXXXXX\r"
expect "# "
send "id\r"
expect "(root)"
exit 0

shell scripting "expect"ing various responses from ssh

So I have a shell script which sshs into a machine, then using expect passes a password via expect and then runs a matlab script. The problem is I would like to do this for a whole range of machines on the network and if it's one that's I've not connected before asking me for a password it tells me that it can't authenticate the machine as asks me if I want to connect and awaits a yes\no response.
I want my shell script to be able to handle situations where it just asks for the password and where it also manages to deal with the yes\no. I don't know how to go about doing this with "expect" as when it doesn't receive what it expects it just hangs.
Also for some reason my additional code to handle the yes no response doesn't work when testing it with machines I know I haven't logged in with. This is format with which they reply.
"The authenticity of host 'seven (128.40.41.89)' can't be established.
RSA key fingerprint is d6:9d:d0:61:5f:bb:36:9a:74:2c:f6:b9:a7:79:03:98.
Are you sure you want to continue connecting (yes/no)?"
And this is my code which for some reason doesn't work with my addition, but works fine with just the "assword:" bit which I found on stack exchange. I can see the terminal and it asks the yes\no question, but then my script doesn't respond. I'm guessing I'm somehow using the expect wrong, but I have no idea how.
#!/bin/sh
ssh seven
expect "(yes\no)? "
send "yes\r"
expect "assword:"
send "my password goes here\r"
matlab -nodisplay -nojvm -nosplash -nodesktop -r \
"try, run('\path\matlab_script.m'), catch me, fprintf('%s / %s \n',me.identifier,me.message), end, exit"
One possible workaround is running ssh with StrictHostKeyChecking set to no:
ssh seven -o StrictHostKeyChecking=no
This will autimatically add unknown host keys to the list of known hosts.

How can a BASH script automatically elevate to root on a remote server, without using sudoers nopasswd option?

o's!
Maybe you can help me with this. I can't find an answer to my specific questions, because there is an obvious solution which I'm not allowed to use. But first things first, the context:
In my company, which is a service provider, we administrate a bunch of
Linux servers. Some of my colleagues has for a long time been running
a BASH script from a source server, that then performs some tasks over
SSH on a number of remote Linux servers. The tasks it performs has to
be executed as root, so what the script does is it authorizes the
source server as root on the remote Linux servers via SSH (the remote
servers has the source servers public SSH key). Then what happened is
a new security policy was enforced and now root login over SSH is
denied. So the mentioned method no longer works.
The solution I keep finding, which we are by policy not allowed to do, is to create an entry in the sudoers file allowing sudo to root without password for the specific user.
This is the terms and they have to obey that. The only procedure that is allowed is to log on to the target server with your personal user, and then sudo su - to root WITH password.
Cocky as I apparently was, I said, "It should be possible to have the script do that automatically", and the management was like "Cool, you do it then!" and now I'm here at Stack Overflow,
because I know this is where bright minds are.
So this is exactly what I want to do with a BASH script, and I do not know if it's possible or how it's done, I really hope you can help me out:
Imagine Bob, he's logged into the source server, and he wants to
execute the script against a target server. Knowing that root over SSH
doesn't work, the authorization part of the script has been upgraded.
When Bob runs the script, it prompts him for his password. The
password is then stored in a variable (encrypted would be amazing) and
the script then logs on the target server as his user (which is
allowed) and then automatically elevates him to root on the target
server using the password he entered on the source server. Now the
script is root and it runs its tasks as usual.
Can it be done with BASH? and how?
UPDATE:
The Script:
## define code to be run on the remote system
remote_script='sudo -S hostname'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")
The error:
[worker#source ~]$ sh elevate.sh
Enter password for : abc123
elevate.sh: line 10: syntax error near unexpected token `<'
elevate.sh: line 10: `ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")'
First: Because it exposes plaintext passwords to the remote system (where they can be read by an attacker using diagnostic tools such as strace or sysdig), this is less secure than correctly using the NOPASSWD: flag in sudoers. If your security team aren't absolute idiots, they'll approve a policy exemption (perhaps with some appropriate controls, such as having a dedicated account with access to a setuid binary specific to the command being run, with authentication to that account being performed via public key authentication w/ the private key stored encrypted) rather than approving use of this hack.
Second: Here's your hack.
## define code to be run on the remote system
remote_script='sudo -S remote_command_here'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh "$host" "$remote_script" < <(printf '%s\0' "$password")
Allright, this is not the final answer, but I think I'm getting close, with the great help of CharlesDuffy.
So far I can run the script without errors on a remote server, that already has the publickey of my source server. However the command I execute doesn't create a file as I tell it to on the remote system.
However the script seems to run and the password seems to be accepted by the remote system.
Also I have to change in the sudoers on the remote host the line "Defaults requiretty" to "Defaults !requiretty", else it will tell me that I need a TTY to run sudo.
#!/bin/bash
## define code to be run on the remote system
remote_script='sudo -S touch /elevatedfile'
## local system
# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password
# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -T 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")
UPDATE: When I tail /var/log/secure on the remote host I get the following after executing the script, which seems like the password is not being accepted.
May 11 20:15:20 target sudo: pam_unix(sudo:auth): conversation failed
May 11 20:15:20 target sudo: pam_unix(sudo:auth): auth could not identify password for [worker]
May 11 20:15:20 target sshd[3634]: Received disconnect from 10.0.1.39: 11: disconnected by user
May 11 20:15:20 target sshd[3631]: pam_unix(sshd:session): session closed for user worker
What I see on the source server, from where I launch the script:
[worker#source ~]$ bash elevate.sh
Enter password for : abc123
[sudo] password for worker:
[worker#source ~]$
Just make a daemon or cron script running as root, that in turn will check for any new scripts in specified secure location (ie. DB that it only has READ access to), and if they exist, it will download and execute them.

Telnet to login with username and password to mail Server

I am having some issues trying to connect through telnet to a mail server.The main problem that I am having is that I need to create a script that logs me to the destination and I can't find a way to echo the password.
What I tried:
telnet host -l username ; echo 'password'
And still it asks for my password.Is there any way to fix this or I am doing something wrong?
First of all, you can use eval:
eval "{ echo user_name; sleep 1; echo pass; sleep 1; echo '?'; sleep 5; }" | telnet host_address
Make sure to replace user_name, pass, ? which is the command you want to run and host_address where your telnet host is listening; for me it is a local IP.
It’s surprisingly easy to script a set of command and pipe them into the telnet application. All you need to do is something like this:
(echo commandname;echo anothercommand) | telnet host_address
The only problem is the nagging login that you have to get through… it doesn’t show up right away. So if you pipe in an “echo admin” and then “echo password,” it will happen too quickly and won’t be sent to the server. The solution? Use the sleep command!
Adding in a couple of sleep 3 commands, to wait three seconds, solves the problem. First we’ll echo the username and password, and then we’ll echo the reboot command, and each time we’ll wait three seconds between. The final command will reboot the server immediately:
(sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
You can put this into a shell script and run it whenever you want. Or you can add it to your cron like this (on OS X or Linux):
crontab -e
Add this line somewhere:
1 7 * * * (sleep 3;echo admin;sleep 3;echo mypassword;sleep 3;echo system reboot;sleep 3;) | telnet host_address
This will reboot your router at 7:01 AM each morning.
AFAIK, you won't be able to automate telnet that way. But it is still possible - even if it is a very bad idea (I'll elaborate on that later).
First why does your try fail :
you launched a telnet command reading from stdin (I suppose terminal) and writing to stdout and stderr (I suppose also a terminal)
if your telnet is reasonably recent, it tries to protect your authentication and asks your password from /dev/tty (for security reasons)
when that command has ended you write password on your own terminal
What should you do instead :
launch telnet with automatic authentication disable (on mine it is telnet -X SRA)
feed its input with the commands you want to pass
wait some delay before entering input, at least for login and password, because if you don't telnet clear input before reading and discards your inputs
Here is an example that allowed me to telnet to my own machine :
sh << EOF | telnet -X SRA localhost
sleep 2
echo my_user_name
sleep 1
echo my_password
# sleep 1 # looks like it can be removed
echo echo foo and bar
sleep 1
EOF
It correctly logs me into my box, executes echo foo and bar (essential command :-) ) and disconnects
Now why you should never do that :
you write a password in clear text in a script file which is poor security practice
you use telnet to do batch processing when it is not intended to be used that way : the script may not be portable to another telnet version
If you really want to pass command in a batch way to a remote server, you should instead try to use ssh which :
has options to process authentication securely (no password in script, nothing in clear text)
is intended to be used in batch mode as well as interactively
If you cannot use ssh (some sysadmin do not like to have uncontrolled input ssh connections) you could also try to use rsh. It is older, far less secure, but at least was designed for batch usage.
Thanks to Harvix answer, I got knew that there is also expect alternative native for shell, called sexpect. Get it from here. Then create this script (I call it telnetpass):
#!/bin/bash
# This script is used for automatically pass authentication by username and password in telnet prompt
# Its goal is similar as sshpass, but for telnet, so I call it telnetpass
. ~/.private/cisco_pw # should contain PASSWORD variable
export SEXPECT_SOCKFILE=/tmp/sexpect-telnetpass-$$.sock
sexpect spawn telnet $1
sexpect expect -cstring 'Username:'
sexpect send -enter $USER
sexpect expect -cstring 'Password:'
sexpect send -enter $PASSWORD
sexpect interact
Then you can run: telnetpass Host125 and got pass the authentication automatically
Trying 198.51.100.78 ...
Connected to Host125.
Escape character is '^]'.
User Access Verification
Username: ashark
Password:
host-125>
I like this solution more than using sleep commands as suggested in another answers, because sleep solutions sometimes fail.
Have you tried using the expect command ?? You will have to create a script where you identify the 'expected' response from the server e.g. 'Password:' and then supply the password in the script. The following will explain: https://en.wikipedia.org/wiki/Expect - A good example is also shown here: http://en.kioskea.net/faq/4736-shell-script-for-telnet-and-run-commands
Try eval:
eval "{ echo;
sleep 3;
echo $user;
sleep 1;
echo $pass;
sleep 1;
echo '?';
sleep 1; }" | telnet your_host
In this example, my remote command is '?' (help).
The sleeps (maybe not all of them nor these times; trial-error...) are needed to avoid telnet misses some inputs.
The user and password are passed as variables ($user and $pass). Take into account security recommendations to store the password if you are scripting.

ssh without key and collect the output using bash script

I want to create a bash script that will login to all the linux servers in my network using ssh and collect the output of 'uptime' command to a local file. There is no keypair installed between these local server and the remote servers. So I need to give the password (username and password is same for all the remote servers) in the script itself. I know this is not a secure way to do it, but it is just for learning purpose. I see 'expect' command can be used for the ssh login with password but confused how to use it together with the 'uptime' command that provide the server status. So my requirement is
1. I have local server test1 which contains a text file 'server_status.txt'
2. I need a script in test1 that will try to login to all the remote servers (say 192.168.0.1 to 192.168.0.50) using the same username and password. It will execute the command 'uptime' once logged in to the remote servers and store the output to the local file 'server_status.txt'
REVOKE: paste your public key into the server's /path2userthatshouldlogon/.ssh/authorized_keys and run the your commands remotely using ssh user#host commandtoexecute
due to connection wanted to be established without key.
UPDATE: have a look at sshpass if you really want to need passwords, which is NOT RECOMMENDED
Note: Doing this is poor practice. If you are testing around with this then you are learning a bad habit. Don't do this in production on servers you care about.
You'll want to execute the expect call as a $? and be sure to store the $USER and $SERVER variables or just replace them:
uptime=$(expect -c 'spawn ssh $USER#$SERVER send "uptime"; exit;')
echo $uptime

Resources