Script to get info from multiple servers - bash

I want to collect info from a number of servers whether their grub.conf contains the "elevator" parameter or not.
Now, password-less key authentication is something I can not do as of now. I am okay with specifying password in script.
Can someone please help me achieving this?
This is what I did:
#!/bin/bash
GRUB="/etc/grub.conf"
_pair="$(</home/wadhwaso/login.txt)"
Server_info="/tmp/server_info"
for e in $_pair
do
# extract user and ips for each $e in $_pair
IFS='#'
set -- $e
_user="$1"
_ip="$2"
sleep 2
echo "Connecting to server '${_ip}' via SSH..."
ssh ${_user}#${_ip} "sudo grep -q "elevator=noop" "$GRUB" >/dev/null"
if [ $? -eq 0 ]; then
echo "Present on ${_ip}" | tee -a "${Server_info}"
else
echo "not present on ${_ip}" | tee -a "${Server_info}"
fi
done
I don't want to give password every time, and as password-less authentication is not present in my environment and will not be possible, I have to pass the password in script itself which really doesn't bother me, I can do that. I know it could be done through expect but I messed everything every time I tried using it.
I tried using expect the way it was told in 1st answer but I failed.

The "expect" tool sounds perfect for what you need: http://expect.sourceforge.net/
To install expect (on ubuntu, for example), do:
sudo apt-get install expect
Here's a code snippet showing you how to use expect as part of a bash script:
#!/bin/bash
IP="111.111.11.1"
login="root"
password="some_password"
dest_dir="/etc/"
expect_sh=$(expect -c "
spawn ssh $login#$IP
expect \"password:\"
send \"$password\r\"
expect \"#\"
send \"cd $dest_dir\r\"
expect \"#\"
send \"chmod +x $server_side_script"
expect \"#\"
send \"./$server_side_script\r\"
expect \"#\"
send \"cd /lib\r\"
send \"cat $file_count\r\"
expect \"#\"
send \"exit\r\"
")
echo "$expect_sh"
Alternatively, you can put the logic in a separate expect script for neater syntax and source it from your bash script:
#!/usr/bin/expect
set login "root"
set addr "111.111.1.1"
set pw "root"
spawn ssh $login#$addr
expect "$login#$addr\'s password:"
send "$pw\r"
expect "#"
send "cd /etc\r"
# Then other things you need to do
As for the "3rd thing" with RSA key fingerprint: SSH will warn you if connecting to a host you haven't seen before (because of possibility of man-in-the-middle attacks), theoretically you should verify that the fingerprint matches what you anticipate it to be to verify that the host is who the host claims to be.
Hope this helps =)

Related

Automate ssh connection with expect in a bash script

Im trying to do a shell script using expect to automate connection because it's not allow to use ssh keys. Its my first script with expect and I find a little bit confuse for me. In this case I can connect by ssh but I can't close the connection, I don't know how to do, any idea ??
#!/bin/bash
user=pepe
host=server
pass=`cat /tmp/password.txt`
COMMAND=df
PASSWORD=$pass expect -c "
spawn ssh $user#$host
expect "*assword*"
send \$env(PASSWORD)\r
expect {
"*be*" {send $COMMAND\r;interact}}
"
The immediate fix is to specify the command as an argument to ssh -- then the connection will be made and the command will run and the connection will close
#!/bin/bash
user=pepe
host=server
pass=`cat /tmp/password.txt`
COMMAND=df
PASSWORD=$pass expect -c "
spawn ssh $user#$host $COMMAND
expect \"*assword*\"
send \$env(PASSWORD)\r
expect eof
"
A couple of things to note:
expect eof will gracefully wait for the spawned command to exit
I've escaped the internal double quotes.
Next step is to tidy up the quoting/escaping. When embedding code for another language in a shell script, using a quoted heredoc is the best way to avoid quoting hell. This means that all the shell variables need to passed to expect via the environment
#!/bin/bash
export user=pepe
export host=server
export pass=`cat /tmp/password.txt`
export cmd=df
expect << 'END_EXPECT'
spawn ssh $env(user)#$env(host) $env(cmd)
expect "*assword*"
send $env(pass)\r
expect eof
END_EXPECT
Last thing I'd do is to reduce the exposure of the password. Expect is built on the tcl language, which is fully capable of reading files:
#!/bin/bash
export user=pepe
export host=server
export pass=`cat /tmp/password.txt`
export cmd=df
expect << 'END_EXPECT'
set fh [open /tmp/password.txt]
gets $fh pass
close $fh
spawn ssh $env(user)#$env(host) $env(cmd)
expect "*assword*"
send $pass\r
expect eof
END_EXPECT
And make sure the permissions on that password file are as restrictive as possible:
mv /tmp/password.txt ~/.local/share/pass.txt
chmod 0400 ~/.local/share/pass.txt

Bash Scripting, send "logout" command being skipped during ssh session

Essentially I connect to remote host, authenticate, run command, logout. However logout command is being skipped.
/usr/bin/expect << EOD
spawn ssh $_host
expect "Password: ";
send "$_pass\r";
expect "$_host>";
send "sh arp | inc $_host2\r";
expect "$_host>";
send "logout\r";
EOD
echo "blah blah"
What i get is my expected output from arp command however, blah blah will be entered into the terminal of the remote host. It seems the logout command is being skipped, somewhat new to bash scripting but it seems that expect doesn't instantly see "$_host>" when executing and skips it? Appreciate any feedback.
Don't use expect here at all. Even if you must get the password from a variable, using sshpass for the purpose will avoid mixing two separate languages (bash and TCL).
SSHPASS="$_pass" sshpass -e "$_host" "sh arp | inc $_host2"
I believe the \r ( carrige return ) should be \n ( enter / new line ) ?
send "logout\r"; -> send "logout\n";
If that will help - I would replace it in the entire script ..
Suggesting:
/usr/bin/expect << EOD
match_max 1000000
spawn ssh $_host
expect "Password: ";
send "$_pass\n";
expect "$_host>";
send "sh arp | inc $_host2\n";
expect "$_host>";
send "logout\n";
EOD
echo "blah blah"

Bash expect two commands in same process

I´m getting crazy trying to make this works.
I need to use expect to set a couple passwords. The problem that I have is that I have to run two commands in the same processor.
Here the code
yum install -y expect
secret="price_aws_git_secret.txt"
eval $(ssh-agent)
pass=$(cat $secret)
expect << EOF
spawn ssh-agent ssh-add price_aws_github
expect "Enter passphrase"
send "$pass\r"
spawn git clone git#github/repo.git
expect "Are you sure you want to continue connecting"
send "yes\r"
expect eof
EOF
The first command add into the ssh-agent the ssh key, and the second command the git clone need to be in the same process to get that agent.
Looking documentation and examples I´m not able to see how expect can works with two commands in the same process.
Any suggestion?
Thanks!
Suggestions about using ssh keys better aside, to answer your question. You don't need to interact with two processes at the same time. You need to handle the first one, then start the second:
expect << EOF
spawn ssh-agent ssh-add price_aws_github
expect "Enter passphrase"
send "$pass\r"
# assume this works. if not, there's more work to do
expect eof
close
spawn git clone git#github/repo.git
expect "Are you sure you want to continue connecting"
send "yes\r"
expect eof
EOF

expect: launching scp after sftp

I could really use some help. I'm still pretty new with expect. I need to launch a scp command directly after I run sftp.
I got the first portion of this script working, my main concern is the bottom portion. I really need to launch a command after this command completes. I'd rather be able to spawn another command than, hack something up like piping this with a sleep command and running it after 10 s or something weird.
Any suggestions are greatly appreciated!
spawn sftp user#host
expect "password: "
send "123\r"
expect "$ "
sleep 2
send "cd mydir\r"
expect "$ "
sleep 2
send "get somefile\r"
expect "$ "
sleep 2
send "bye\r"
expect "$ "
sleep 2
spawn scp somefile user2#host2:/home/user2/
sleep 2
So i figured out I can actually get this to launch the subprocess if I use "exec" instead of spawn.. in other words:
exec scp somefile user2#host2:/home/user2/
the only problem? It prompts me for a password! This shouldn't happen, I already have the ssh-keys installed on both systems. (In other words, if I run the scp command from the host I'm running this expect script on, it will run without prompting me for a password). The system I'm trying to scp to, must be recognizing this newly spawned process as a new host, because its not picking up my ssh-key. Any ideas?
BTW, I apologize I haven't actually posted a "working" script, I can't really do that without comprimising the security of this server. I hope that doesn't detract from anyones ability to assist me.
I think the problem lies with me not terminating the initially spawned process. I don't understand expect enough to do it properly. If I try "close" or "eof", it simply kills the entire script, which I don't want to do just yet (because I still need to scp the file to the second host).
Ensure that your SSH private key is loaded into an agent, and that the environment variables pointing to that agent are active in the session where you're calling scp.
[[ $SSH_AUTH_SOCK ]] || { # if no agent already running...
eval "$(ssh-agent -s)" # ...then start one...
ssh-add /path/to/your/ssh/key # ...load your key...
started_ssh_agent=1 # and flag that we started it ourselves
}
# ...put your script here...
[[ $started_ssh_agent ]] && { # if we started the agent ourselves...
eval "$(ssh-agent -s -k)" # ...then clean up nicely when done.
}
As an aside, I'd strongly suggest replacing the code given in the question with something like the following:
lftp -u user,123 -e 'get /mydir/somefile -o localfile' sftp://host </dev/null
lftp scp://user2#host2 -e 'put localfile -o /home/user2/somefile' </dev/null
Each connection handled in one line, and no silliness messing around with expect.

How to use error codes inside bash script

Apologies if this has been asked before, I was unable to find an answer...
I have created a bash script for OS X to mount an AFP share. The script works successfully and allows a user to type their username and password into a graphical popup using cocoa dialog
My issue is that if the username and password are entered incorrectly, I want to be able to display a new pop up window explaining this.
I was trying to do this based on the exit status of the script but the script returns 0 regardless of whether or not the mount was successful
The script is as follows;
cd=/etc/cocoaDialog.app/Contents/MacOS/cocoaDialog
rvUsername=($($cd standard-inputbox --title "Network Mount" --informative-text "Please enter your username"))
username=${rvUsername[1]}
rvPassword=($($cd secure-standard-inputbox --title "Network Mount" --informative-text "Please enter your password"))
password=${rvPassword[1]}
mkdir "/Volumes/Test"
expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://servername/Software" /Volumes/Test
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE
If I run this via Terminal or Textmate, I receive the following (as an example)
spawn /sbin/mount_afp -i afp://server/Software /Volumes/Test
User: username
Password:
mount_afp: the volume is already mounted
Program exited with code #0 after 7.32 seconds.
So mount_afp is giving me a message, which I would then like to use in my script...but the exit code is 0 and I don't know how else to get hold of that message to use
Any ideas? (Hope that makes sense!)
To get the exit code of the spawned command, use something like this:
# wait for the command to end : wait for the prompt $ followed by space
expect "\\$ "
send "echo \$?\r"
expect -re "(\\d+)" {
set result $expect_out(1,string)
}
send_user "command exit with result code : $result"
This will take the content of the variable $? (which is the exit code of the prevously ended command) and save it to $result.
Thanks for the responses all, helped point me in the right direction
I ended up taking this approach and setting the expect command as a variable
output=$(su -l $loggedInUser -c expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://$serverName" /Volumes/mount
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE)

Resources