Why is this while loop not looping? - bash

I have the following bash script deploy.sh:
#!/usr/bin/env bash
# Some useful resources:
# while read ip user pass; do : http://unix.stackexchange.com/questions/92664/how-to-deploy-programs-on-multiple-machines
# -o StrictHostKeyChecking=no: http://askubuntu.com/questions/180860/regarding-host-key-verification-failed
# -T: http://stackoverflow.com/questions/21659637/how-to-fix-sudo-no-tty-present-and-no-askpass-program-specified-error
# echo $pass |: http://stackoverflow.com/questions/11955298/use-sudo-with-password-as-parameter
while read ip user pass; do
echo $ip
sshpass -p "$pass" ssh $user#$ip -o StrictHostKeyChecking=no -T "
echo 'yo'
"
echo 'done'
done < servers.txt
servers.txt contains:
53.12.45.74 my_username my_password
54.12.45.74 my_username my_password
57.12.45.74 my_username my_password
‌‌
From my understanding, the while read ip user pass; do […] done < servers.txt should loop over all three lines of servers.txt.
However, when I try to run it, it only performs one iteration:
ubuntu#server:~$ bash deploy.sh
53.12.45.74
yo
done
ubuntu#server:~$
Why?
If the loop is simply:
while read ip user pass; do
echo $ip
done < servers.txt
it does perform all three iterations:
ubuntu#server:~$ bash deploy.sh
53.12.45.74
54.12.45.74
57.12.45.74
ubuntu#server:~$

sshpass is taking control of stdin or possibly replacing it and causing while loop to lose input from redirected stdin.
To work around this issue, avoid reading from stdin.
First, load the file into an array using a while loop.
while read line; do
entries+=("$line")
done < servers.txt
Next, use for loop to parse the lines and execute sshpass within this loop.
for line in "${entries[#]}"; do
set $line
ip=$1
user=$2
pass=$3
echo $ip
sshpass -p "$pass" ssh $user#$ip -o StrictHostKeyChecking=no -T "
echo 'yo'
"
echo 'done'
done
The second loop doesn't read from stdin.
But I will recommend Rany Albeg Wein answer using a separate descriptor than the current stdin.
while read ip user pass <&3; do
echo $ip
sshpass -p "$pass" ssh $user#$ip -o StrictHostKeyChecking=no -T "
echo 'yo'
"
echo 'done'
done 3<servers.txt

#!/usr/bin/env bash
# Some useful resources:
# while read ip user pass; do : http://unix.stackexchange.com/questions/92664/how-to-deploy-programs-on-multiple-machines
# -o StrictHostKeyChecking=no: http://askubuntu.com/questions/180860/regarding-host-key-verification-failed
# -T: http://stackoverflow.com/questions/21659637/how-to-fix-sudo-no-tty-present-and-no-askpass-program-specified-error
# echo $pass |: http://stackoverflow.com/questions/11955298/use-sudo-with-password-as-parameter
while read ip user pass; do
if [ -n "$ip" ]; then
echo "IP[$ip] USER[$user] PASS[$pass]";
# dont forget to uncomment this two lines here:
#sshpass -p "$pass" ssh $user#$ip -o StrictHostKeyChecking=no -T "
#echo 'yo'
echo ""; # Just a blank line"
echo "done.";
else
echo "Empty value.";
fi
done < servers.txt
So, now it works.

Related

Automating ssh-add private keys with a common password

I have searched but the solutions I found were only focused on using a single key file.
Suppose I have a set of private keys which require the same password (and that I am confortable with such a setup from a security perspective).
How could I go about making a bash script that reads a password from stdin and invokes ssh-add for each of the private keys using the same password?
I do not have -p available for ssh-add.
I am trying to avoid writing the password to a file (even if temporary).
I came with this so far but I am unsure how to go or if this is possible:
#!/bin/bash
if [ $(ps ax | grep [s]sh-agent | wc -l) -gt 0 ] ; then
printf "> 'ssh-agent' is running.\n"
else
printf "> 'ssh-agent' needs to be running. Exiting.\n"
exit 0
fi
unset password
prompt="> Please input your password: "
while IFS= read -p "$prompt" -r -s -n 1 char
do
if [[ $char == $'\0' ]]
then
break
fi
prompt='*'
password+="$char"
done
printf "\n> Adding key files...\n"
## declare an array variable
declare -a arr=("key-1.ppk" "key-2.ppk" "key-3.ppk")
## now loop through the above array
for i in "${arr[#]}"
do
#echo "$i"
ssh-add "$i" < <(echo "$password")
done
Also tried the following to pass into stdin:
echo $password | ssh-add "$i"
I also thought about using this in the loop:
{
/usr/bin/expect << EOF
spawn ssh-add $HOME/.ssh/$i
send "$password\r"
expect eof
EOF
}
But then it would ask me to input a password for each individual key, defeating the purpose of automation.
Unless there is a way for a single spawn ssh-add to receive one single password via expect (only one prompt) and add more than one key with it?
If you have same password for all the keys then you can pass multiple keys to ssh-add and it prompts for password only once and adds all those keys to the ssh-agent.
eg:
$> arr=("id_ecdsa" "new_test_key")
$> ssh-add ${arr[#]}
Enter passphrase for id_ecdsa:
Identity added: id_ecdsa (test_user#host_test)
Identity added: new_test_key (test_user#host_test)
$> ssh-add -l
256 SHA256:urYhdMK9UZyLl+p8cC7ehdImYfvsmtJFtQmESWoczmM test_user#host_test (ECDSA)
256 SHA256:53obuQkRzLGW5iUJdmFPNvSK1quUSlCi4gbQkKsJinY test_user#host_test (ECDSA)
Password can be passed via environment variable :
#!/usr/bin/env bash
read password
ssh_askpass=$HOME/.ssh_askpass
echo 'echo "$password"' > $ssh_askpass; chmod 700 $ssh_askpass
ppk=key-1.ppk
export password
SSH_ASKPASS="$ssh_askpass" ssh-add $ppk < /dev/null
rm -f "$ssh_askpass"

Inserting text through ssh without multiple password entries

I'm trying to setup a DNS server through ssh and am able to send text to the config files, but it requires a password for each line.
ssh -t $newDNS "sudo sed -i '4iforwarders { $IpDNS; };' /etc/bind/named.conf.options"
ssh -t $newDNS "sudo sed -i '/^forwarders/i listen-on port 53 { $IpDNS; };' /etc/bind/named.conf.options"
ssh -t $newDNS "sudo sed -i '/^listen-on/i allow-query { localhost; $subDNS; };' /etc/bind/named.conf.options"
ssh -t $newDNS "sudo sed -i '/^forwarders/a recursion yes; }/etc/bind/named.conf.options"
I think you either need to install a ssh key onto each server or use expect and type your password once.
The ssh key solution:
Read this link, but do not set up a password for your RSA key.
The expect solution:
#!/bin/bash
# This function is sent to the server. I've used it for testing
function dosomething {
ls
}
# The script expects a file with servers separated with newlines.
FILE=$1
# If the file does not exist, it exits.
[ -f "${FILE}" ] || exit 1
# I didn't know your password and username. If you would like me to add it,
# please sent me a DM or paste it as comment ;-)
read -sp "username: " USERNAME
echo
read -sp "password: " PASSWORD
echo
# While the content of ${FILE} isn't empty...
while IFS= read -r SERVER; do
# .. use expect to spawn ssh, login with ${PASSWORD}, send the dosomething to
# the server, execute dosomething and exit.
expect <<-EOF
spawn ssh ${USERNAME}#${SERVER}
expect "*: " { send "${PASSWORD}\r" }
expect "*$ " { send "$(typeset -f dosomething)\r" }
expect "*$ " { send "dosomething\r" }
expect "*$ " { send "exit\r" }
EOF
done < ${FILE}
# You're up ;-)
exit $?

a part string is missing when echo in bash

The bash script is:
echo Updating hostname and IP address ...
echo ${config_template_ip}
echo "ssh-keygen -R ${config_template_ip} -f /home/testusr/.ssh/known_hosts"
echo ${config_template_ip}
The output is
Updating hostname and IP address ...
10.100.224.250
-f /home/testusr/.ssh/known_hosts
10.100.224.250
why didn't "ssh-keygen -R ${config_template_ip} " on STDOUT, and how to solve that?

bash script with parameters in a while loop [duplicate]

This question already has answers here:
Read user input inside a loop
(6 answers)
Closed 4 years ago.
the script is:
#!/bin/bash
SCRIPTLOG=~/tmp/logfile
[ ! -d ~/tmp ] && mkdir ~/tmp
while getopts ":u:g:y" options; do
case $options in
u )
user=$OPTARG;;
g )
group=$OPTARG;;
* )
echo "wrong option: ${OPTARG}"
exit 1;;
esac
done
set -e
add_etx_members ()
{
user="${1}"
group="${2}"
set -u
echo "Users to add to ${group}:"
echo
echo "--- BEGIN ---"
printf "%-10s\n" $user
echo "--- END ---"
echo
while true; do
read -r -p "Continue? [y/n]: " REPLY
case $REPLY in
[yY]) echo;break ;;
[nNqQ]) echo;exit ;;
*) printf "\033[31m%s\033[0m\n" " invalid input: ${REPLY}"
esac
done
echo "here commands"
}
add_etx_members "${user}" "${group}" 2>&1 | tee -a ${SCRIPTLOG}
when I run single execution from command line it works (reaches "echo here commands"):
$ myscpt1 -u test -g ABC
Users to add to ABC:
--- BEGIN ---
test
--- END ---
Continue? [y/n]: y
here commands
$
but when I run it in while loop, it fails:
$ echo -e "ABC\nSDE"|while read a;do myscpt1 -u test -g ${a};done
Users to add to ABC:
--- BEGIN ---
test
--- END ---
invalid input: SDE
$
Although, the commands are the same as from single run:
$ echo -e "ABC\nSDE"|while read a;do echo myscpt1 -u test -g ${a};done
myscpt1 -u test -g ABC
myscpt1 -u test -g SDE
because the read inside the script is consuming the same input (the standard input is inherited from caller) as the while read calling the script.
may be resolved redirecting input inside script
exec < /dev/tty
or just for the while read
while ...;
done < /dev/tty
Note that while true could be changed by while read
or from outside depending on what you need
echo -e "ABC\nSDE"|while read a;do myscpt1 -u test -g ${a} < /dev/null;done

bash read does not wait for input

I have this code:
#!/bin/bash
for some_host in $(cat some_list); do
echo $some_host
ssh $some_host sudo cat /etc/some.conf |grep -i something_to_grep
printf "\nPut the input: " read some_input
echo $some_input
done
When I run it, it just continues without waiting for my input. I need to copy/past something form ssh output for further action :/
Change
printf "\nPut the input: " read some_input
to
read -p "Put the input: " some_input
Example
for host in '1.1.1.100' '1.1.1.101' '1.1.1.102'
do
read -p "Enter your input for ${host} " host_input
echo "${host} says ${host_input}"
done

Resources