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"
Related
I have a simple CLI tool asking for a master password, and printing a string $USER $PASSWORD only if the master password is correct.
How to reproduce?
Here is a script just for demonstrating my use-case, the real script is in fact a CLI tool on which I have no control:
#!/usr/bin/env sh
printf "Enter master password: "
read -s password
echo
[ "$password" == "MasterPassword" ] && echo "user1 Passw0rd!"
Example of usage:
$ ./my-cli-tool
Enter master password: ********
user1 Passw0rd!
Issue
I don't want the password (Passw0rd!) to be printed on screen. I want to print only the user (user1), and just copy the password (Passw0rd!) to the clipboard (let's say with xclip -sel clipboard).
What I have tried?
If the first line (Enter master password) were not there, I would have done:
./my-cli-tool |
while read -r USER PASSWORD
do
echo $USER
echo -n $PASSWORD | xclip -sel clipboard
done
But my issue is that I should type the master password when the prompt asks for, and so the first line is always printed. I have tried to run ./my-cli-tool | tail -1: the prompt is not shown, although if I type the master password, it only prints user1 Passw0rd!, so I can do the command above to copy the password into the clipboard.
Question
Do you have any idea to:
always show the prompt on screen for the master password
only print the user
copy the password to the clipboard
Expected output
Basically, I would like that kind of output:
$ ./my-cli-tool | solution
Enter master password: ********
user1
And have Passw0rd! copied into my clipboard.
I've simply modified your answer a little bit -
./my-cli-tool | {
x=$(dd bs=1 count=1 2>/dev/null)
while [ "$x" != : ]; do
printf %c "$x";
x=$(dd bs=1 count=1 2>/dev/null)
done
printf %s ": "
while read -r USER PASSWORD
do
echo $USER
echo -n $PASSWORD | xclip -sel clipboard
done
}
Lemme know if it works.
EDIT: Updated logic. Uses dd.
Ok so I'm sorry if this is a no-brainer, if it is unclear, or if it is impossible but here is my code, I am trying to get the /usr/bin/expect line of code to inherit/get variables from the main script. Its not working because it was started with a different shell (I think) and so it's not loading the variables in. This is my first post so if I am doing anything wrong please kindly correct me ;)
Thanks in advance,
--A\\/
#!/bin/bash
red="\x1b[0;31m"
nc="\x1b[0m"
####### commented out till scipt is finished
# if [ $EUID -ne 0 ]; then
# echo -e "${red}[!]${nc} | script must be run as root"
# exit
# fi
read -p '[?] | ip address of device: ' ip
read -p '[?] | nickname to set for this config: ' name
read -p '[?] | username to generate keys for: ' usr
read -p '[?] | password for that user: ' pw
export $usr
export $pw
export $ip
export $name
/usr/bin/expect -c 'spawn ssh $usr#$ip; expect"(yes/no)? "; send "yes\r"; expect "Password:"; send "$pw"; interact'
You have to 1. export them correctly, and 2. use $env(foo) to refer to them:
#!/bin/bash
var="World"
export var # No $
expect -c 'spawn echo Hello $env(var); interact'
I'm trying to create a bash script that asks for password when you try to see the password file, but I'm stucked. This is my code:
#!/bin/bash
# Read Password
echo -n Password:
read -s PASSWORD
passwords() {
echo "
PASSWORDS
"
}
if [ "$PASSWORD"="root" ]; then
passwords
exit
else
echo "Wrong password"
exit
fi
I've tried a lot of things, for example if [ "$PASSWORD"!="root" ] instead of else but none of them worked.
Here is a shorter version:
#!/bin/bash
passwords(){
echo "PASSWORDS"
}
## Read Password
read -p "Enter password: " -s PASSWORD
desired_password="root"
[ "$PASSWORD" == "$desired_password" ] && passwords || echo "Wrong password"
As #vdavid said, you can add a space around the equal sign or even better, as you have bash shell, it is recommended to use double-bracket for your if statement. Check this: Is there any difference between '=' and '==' operators in bash or sh
Also you can add:
printf "/n" so your script will behave like a typical Linux prompt for password - information will output in new line
non-zero exit code in case of wrong password (exit 1)
Basically, after those improvements code looks like this:
#!/bin/bash
# Read Password
echo -n Password:
read -s PASSWORD
printf "\n"
passwords() {
echo "PASSWORDS"
}
if [[ "$PASSWORD" == "root" ]]; then
passwords
exit 0
else
echo "Wrong password"
exit 1
fi
Note that I used "==" instead of "=", but for double-bracket they both do the same job.
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 $?
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.