Bash while loop iterates only once whenever body contains ssh [duplicate] - bash

This question already has answers here:
ssh breaks out of while-loop in bash [duplicate]
(2 answers)
Closed 7 years ago.
I'm reading host information from a text file and pass it to an ssh command:
The text file contains the host, user and password for the ssh command
while read LINE
do
R_USER=$(echo $LINE | cut -d ',' -f 1)
R_HOST=$(echo $LINE | cut -d ',' -f 2)
PY_SCRIPT=$(echo $LINE | cut -d ',' -f 4)
ssh $R_USER#$R_HOST 'touch /home/user/file_name.txt'
done </path_name/file_name
As it turns out the while loop is only executed once even if the host text file contains multiple host information.
When I remove the ssh command the while loop gets executed as much as there are lines in the host information text file.
Not sure why this is so.
Any information on this?
Roland

The default standard input handling of ssh drains the remaining line from the while loop.
To avoid this problem, alter where the problematic command reads standard input from. If no standard input need be passed to the command, read standard input from the special /dev/null device:
while read LINE
do
R_USER=$(echo $LINE | cut -d ',' -f 1)
R_HOST=$(echo $LINE | cut -d ',' -f 2)
PY_SCRIPT=$(echo $LINE | cut -d ',' -f 4)
ssh $R_USER#$R_HOST 'touch /home/user/file_name.txt' < /dev/null
done </path_name/file_name
Or alternatively, try using ssh -n which will prevent ssh from reading from standard input. For instance:
ssh -n $R_USER#$R_HOST 'touch /home/user/file_name.txt'

If the file is white space separated
host1 user password
host2 user password
then a simple read loop:
while read -r Server User Password
do
/usr/bin/ssh -n $User#$Server touch /home/user/file_name.txt
done </path/to/file.list
But you will be prompted for the password. You cannot pass the "Password" to ssh so I'd suggest storing passwordless ssh-keys and placing them on each host for the user you are connecting as. If you are running this command from a script you can ssh as these users on each host by placing your public key in the user's ~/.ssh/authorized_keys (or authorized_keys2) file. If you have the ssh-copy-id command you could do this by:
ssh-copy-id user#hostname
which would copy YOUR ssh-key to their authorized_keys file so you could then ssh as them. This is assuming you have permission but then again you have their password so what permission do you need?

Related

How to make shell script to automatically input value

I'm trying to copy ssh public key to all hosts on my network with the following little script
#!/bin/bash
for ip in $(nmap -n -sn 192.0.2.0/24 -oG - | awk '/Up$/{print $2}'); do
ssh-copy-id vagrant#$ip
done
However, it asks for the password for each IP, which count is unknown until execution of the script. How to make this script to automatically enter the password (which in this case is same for all the hosts)?
Use the sshpass. Note that it is not a good idea to store passwords in the scripts, but it will do the job for the setup:
#!/bin/bash
for ip in $(nmap -n -sn 192.0.2.0/24 -oG - | awk '/Up$/{print $2}'); do
sshpass -p password ssh-copy-id vagrant#$ip
done

Auto answer ssh-copy-id in shell script

I'm writting a shell script and I want to automate login into a remote machine using ssh-copy-id, so manually when I print :
ssh-copy-id -i /root/.ssh/id_rsa $2#$4 -p $3 | echo $1
$1 refer to password,
$2 refer to username,
$3 refer to port, and
$4 refer to ip,
It is ok with that, the problem is that I have to automate inserting password after :
ssh-copy-id -i /root/.ssh/id_rsa $2#$4 -p $3
I add this "| printf $1", but it does not work it shows "password:" in the screen and still wait for the password ..
I hope you understand me and thank you.
As #Leon pointed out, you had the pipeline backwards. But even if you do it with the correct order, it will still not work because ssh-copy-id (and all other programs from openssh) do not read passwords from their stdin. The solution is to use the $SSH_ASKPASS environment variable. You can do that as follows: first, create an auxiliary script, say /var/tmp/ssh-pass.sh (actually find a better name than that), with the following contents:
#!/bin/sh
echo "$PASS"
Then you can use the following command to accomplish what you've asked for:
PASS="$1" SSH_ASKPASS="/var/tmp/ssh-pass.sh" setsid -w ssh-copy-id -i /root/.ssh/id_rsa "$2"#"$4" -p "$3"
Explanation: we use setsid -w to disassociate the ssh-copy-id process from the currently used terminal. That forces ssh-copy-id to run the executable specified in the $SSH_ASKPASS in order to obtain the password. We have specified our own script in that variable, so ssh-copy-id will execute just that. Now the script is supposed to provide the password to ssh-copy-id by printing it to its stdout. We use the $PASS variable to the password to the script, so the script just prints that variable.
2020 / Mac OS X:
Install sshpass (original answer)
brew install hudochenkov/sshpass/sshpass
Run ssh-copy-id using sshpass and with the password as an arg
sshpass -p $1 ssh-copy-id -i ~/PATH/TO/KEY $2#$4 -p $3
If you want to turn off strict host checking as well, use the -o flag, which is passed to the underlying ssh:
sshpass -p hunter2 ssh-copy-id -o StrictHostKeyChecking=no -i ~/PATH/TO/KEY $2#$4 -p $3
I tried the solution by #redneb, and installed setsid through util-linux by following this answer, but kept receiving a password denied.
I found this strategy to work for uploading my SSH key while setting up multiple raspberry pis in successino. In my script, I also run ssh-keygen -R raspberrypi.local each time too, to avoid the The ECDSA host key for raspberrypi.local has changed error.

Waiting for input from script that is running remotely via ssh

There is a script I'm running that I can not install on the remote machine.
clear && printf '\e[3J'
read -p "Please enter device: " pattern
read -p "Enter date: (YYYY-MM-DD): " date
pfix=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1)
mkdir /home/user/logCollectRes/"${pfix}"
ssh xxx.xxx.xxx.xxx 'bash -s' < /usr/local/bin/SearchAdvanced.sh ${pattern} ${date} ${pfix}
In that script, I would like to be able to use read.
ls -g *"${pattern}"*
read -p "Select one of these? [y/n] " "found";
I've tried adding the -n on the read as well as the -t -t option on ssh. As you can see the script presents information that is only seen once the script starts, so I can't use the read on local machine.
EDIT: So lets say server B stores syslogs for 5K computers. The file names are given by using the internal IP of the device and the date at the end.
/var/log/remotes/192.168.1.500201505050736.gz
/var/log/remotes/192.168.1.500201505050936.gz
/var/log/remotes/192.168.1.500201505051136.gz
/var/log/remotes/192.168.1.600201505050836.gz
/var/log/remotes/192.168.1.600201505051036.gz
/var/log/remotes/192.168.1.600201505051236.gz
I'd like to be able to select the IP address from the main script, list all the files matching that IP address, and then select which I want to scp to my local machine.
After speaking with some coworkers I found the answer to be running two scripts: The first pulls the ls -g result and directs the answer to a variable on the local machine. I then print that output with the read option of selecting on of the files. The 2nd script will take that answer and scp the file from the remote machine
In the main script
ssh xxx.xxx.xxx.xxx 'bash -s' < /usr/local/bin/SearchAdvanced.sh ${pattern} ${date} > ${result}
then as a follow up
printf "${result}"
read -p "Select file: "

How can I pipe the hostname into a call to SSH?

I'm trying to write a script to automate connecting to various remote instances but am having a hard time piping the resulting hostname to as an argument to ssh. I'm basically trying to do the following:
echo "example.com" | xargs -I {} ssh {}
I also tried a bunch of combinations but to no avail. The closest I got was with the following but it loses interactivity.
echo "example.com" | xargs -0 ssh -t -t
The end goal is to be able to have a script that will return a hostname/ipaddress that can then be connected to via ssh. For example:
my_random_script | ... ssh
Maybe, you want to run:
ssh `echo example.com`
ssh `your_random_script`
When you insert some command into back-quotes, this command is executed, and result stored in unnamed variable. For save/reuse, you can invoke something like:
VAR=`your_random_script`
ssh $VAR

bash: while read loop - stop if there is variable sshpass

i have a problem with my bash script. I read line by line the variable lvm_path_exec, that works. I confirmed it with echo "lvmpath".
But as soon as i place a sshpass command into the while statement the script only process the first line which got grepped.
If there is no sshpass command all lines of lvmpath_exec get processed.
Do you see the error?
lvmpath_exec=$(sshpass -p "${password[$i]}" ssh ${user[$i]}#${ip[$i]} -p ${port[$i]} lvdisplay | grep datatest -A 3 | grep Path | awk '{ print $3 }')
echo "$lvmpath_exec" | while read lvmpath
do
lvmname=datatest
snap=_snapshot
snapname=$lvmname$snap
lvcreate=$(sshpass -p "${password[$i]}" ssh ${user[$i]}#${ip[$i]} -p ${port[$i]} lvcreate -L20G -s -n $snapname $lvmpath)
snap_path=$(sshpass -p "${password[$i]}" ssh ${user[$i]}#${ip[$i]} -p ${port[$i]} lvdisplay | grep $snapname -A 3 | grep Path | awk '{ print $3 }')
transfer=$(sshpass -p "${password[$i]}" ssh ${user[$i]}#${ip[$i]} -p ${port[$i]} "dd if=$snap_path | gzip -c" > /tmp/$snapname)
delsnap=$(sshpass -p "${password[$i]}" ssh ${user[$i]}#${ip[$i]} -p ${port[$i]} lvremove -f $snap_path)
done
UPDATE
I fixed it:
replace
echo "$lvmpath_exec" | while read lvmpath
with
for lvmpath in $lvmpath_exec
But shouldnt it work with while read too?
sshpass works by manipulating stdin to fool ssh into thinking it is getting the password from an interactive user. When you use a ... | while style loop, the loop iterates for every line coming from stdin, which sshpass wipes out after the first call, that's why only the first line gets executed. The for loop doesn't use stdin, that's why it doesn't have this problem.
As man sshpass explains, this tool is inherently insecure and you should really be using public key authentication instead. Also keep in mind that it has other ways of passing the password, using the -p flag is the least safe method of all, and any other method would be safer, for example the -e flag seems trivially easy. I know you might insist you have a legitimate use case, but this is so important I'm just gonna quote from the man page:
First and foremost, users of sshpass should realize that ssh's insis‐
tance on only getting the password interactively is not without reason.
It is close to impossible to securely store the password, and users of
sshpass should consider whether ssh's public key authentication pro‐
vides the same end-user experience, while involving less hassle and
being more secure.
The -p option should be considered the least secure of all of sshpass's
options. All system users can see the password in the command line
with a simple "ps" command. Sshpass makes a minimal attempt to hide the
password, but such attempts are doomed to create race conditions with‐
out actually solving the problem. Users of sshpass are encouraged to
use one of the other password passing techniques, which are all more
secure.
have you tried this..have not tried though
export SSHPASS=password[$i]
sshpass -e ssh -oBatchMode=no user[$i]#{ip[$i]} ..

Resources