ssh key generation and copy ssh key to other machines - linux

I am running the following script to generate and copy ssh key to other machines in my cluster for password-less ssh.
#!/bin/bash
#create host and password file
echo enter host-name,password in "host pass" format:
read hopa
echo "$hopa"> /root/host1.txt
sed -i 's/,/\n/g' /root/host1.txt
# SSH Key
#echo -en "y" | ssh-keygen -f id_rsa -t rsa -N ''
# Passwordless ssh
echo -en "y" | ssh-keygen -f id_rsa -t rsa -N ''
declare -A hp
while IFS=' ' read -r host pass
do
hp["$host"]="$pass"
done < /root/host1.txt
for host in "${!hp[#]}"
do
pass="${hp["$host"]}"
sshpass -p "${pass[i]}" ssh-copy-id -i id_rsa -o "StrictHostKeyChecking no" -f root#"${host[i]}" -p 22
done
When prompted for hostnames and their passwords:
My input:
cephadmin 1234,ceph2 1234,ceph3 1234,cephclient 1234
Output:
The key fingerprint is:
SHA256://CzhoYLtmVVRWoJTKfTkJV9BQbeKypzGoXBLV62KKw root#cephadmin
The key's randomart image is:
+---[RSA 3072]----+
| o+o+==.o|
| . .oB.*. .|
| + * B .. |
| . . B = . |
| o S + . . |
| . . + . . |
| E o *.=. |
| . =.*o+o |
| . oo .+o |
+----[SHA256]-----+
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "id_rsa.pub"
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking no' -p '22' 'root#cephadmin'"
and check to make sure that only the key(s) you wanted were added.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "id_rsa.pub"
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking no' -p '22' 'root#ceph2'"
and check to make sure that only the key(s) you wanted were added.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "id_rsa.pub"
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking no' -p '22' 'root#ceph3'"
and check to make sure that only the key(s) you wanted were added.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "id_rsa.pub"
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking no' -p '22' 'root#cephclient'"
and check to make sure that only the key(s) you wanted were added.
When I try logging into cephclient machine with ssh cephclient I am being prompted for password
[root#localhost ~]# ssh cephclient
root#cephclient's password:
How can I make sure that my script works properly and I get the desired output, i.e. login to other machines without password?

first time it will ask password. From Second time onwards it will not ask password.

Related

Answer "Yes" or "No" ONLY IF a command request it

I a writing a script. There is a command that colud request to answer yes or no to override a certain file.
I want to automate the script to answer YES or NO ONLY IF the command request it (i don't want to echo yes inside the command).
The command I am referring to is ssh-keygen, which requires to override the key in case already exists.
In my mind there is something like this...
if (ssh-keygen requests input) --> Sends yes to the ssh-keygen
In particular, I am using the following command:
ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q
Even if i am using -q, still asks yes or no to override the file.
Thanks in advance
i suggest using the echo command to send "yes" to the command.
echo "yes" | ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q
I would suggest you to check the yes command.
It is sending a loop of y.
If you want to force the acceptation of the command it should work.
But if the keygen request for an input (filename, etc.) it will also send 'y'.
the command would be :
yes | ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa -q

Testing active ssh keys on the local network

I am trying currently to achieve a bash script that will validate if SSH keys on a server are still linked to known hosts that are active on the local area network. You can find below the beginning of my bash script to achieve this:
#!/bin/bash
# LAN SSH KEYS DISCOVERY SCRIPT
# TRYING TO FIND THOSE SSH KEYS NOW
cat /etc/passwd | grep /bin/bash > bash_users
cat bash_users | cut -d ":" -f 6 > cutted.bash_users_home_dir
for bash_users in $(cat cutted.bash_users_home_dir)
do
ls -al $bash_users/.ssh/*id_* >> ssh-keys.txt
done
# DISCOVERING THE KNOWN_HOSTS NOW
for known_hosts in $(cat cutted.bash_users_home_dir)
do
cat $bash_users/.ssh/known_hosts | awk '{print $1}' | sort -u >>
hosts_known.txt
sleep 2
done
hosts_known=$(wc -l hosts_known.txt)
echo "We have $hosts_known known hosts that could be still active via SSH
keys"
# TIME TO TEST WHICH SSH servers are still active with the SSH keys
# AND THIS IS WHERE I AM FROZEN...
# Would love to have bash script that could
# ssh -l $users_that_have_/bin/bash -i $ssh_keys $ssh_servers
# Would also be very nice if it could save active
# SSH servers with the valid keys in output.txt in the format
# username:local-IP:/path/to/SSH_key
Please feel very comfortable to edit/modify the bash script above if it can serve better the goals described.
Any help would be very appreciated,
Thanks
The following works cool:
</etc/passwd \
grep /bin/bash |
cut -d: -f6 |
sudo xargs -i -- sh -c '
[ -e "$1" ] && cat "$1"
' -- {}/.ssh/known_hosts |
cut -d' ' -f1 |
tr ',' '\n' |
sed '
/^\[/{
s/\[\(.*\)\]:\(.*\)/\1 \2/;
t;
};
s/$/ 22/;
' |
sort -u |
xargs -l1 -- sh -c '
if echo "~" | nc -q1 -w3 "$1" "$2" | grep -q "^SSH"; then
echo "#### SUCCESS $1 $2";
else
echo "#### ERROR $1 $2";
fi
' --
So:
Start with /etc/passwd
Filter all "bash_users" as you call them
Filter user home directories only cut -d: -f6
For each user home directory sudo xargs -i -- run
Check if the file .ssh/known_hosts inside the user home directory exists
If it does, print it
Filter only hosts names
Multiple hosts signatures may share same key and are separated by a comma. Replace comma for newline
Now a sed script:
If a line starts with a [ that means it has a format of [host]:port and I want to replace it with host port
If the line does not start with a [ I add 22 to the end of the line so it's host 22
Then I sort -u
Now for each line:
I get the ssh version from ssh echo "~" | nc hostname port returns smth like "SSH-2.0-OpenSSH_6.0" + newline + "Protocol mismatch".
So if the line returned by nc hostname port starts with SSH that means there is ssh running on the other side
I added timeout for unresponsive hosts, but I think nc -w timeout option may also be used. Probably also nc -q 1 should be specified.
Now the real fun is, when you add the max-procs option to the last xargs line, you can check all hosts simultaneously. On my host I have 47 unique addresses and xargs -P30 checks them ALL in like 2 seconds.
But really there are some problems. The script needs root to read from all users known_hosts. But worse, the known_hosts may be hashed. It would be better to firstly know the list of hosts on your network, and then generate known_hosts from it. It would look like ssh-keyscan -f list_of_hosts > ~/.ssh/known_hosts or similar. Generaly ssh-keygen -F hostname should be used if a host exists in known_hosts, sadly there is no listing command. known_hosts file format may be found in ssh documentation.

Multiple ssh in a Single command

I need to pipe multiple ssh commands in order to run commands on a remote machine.
The commands are working fine with a single ssh but not after piping ssh.
E.g
ssh abc#remotemachine1.com "a=hello ; echo \$a"
return hello
but
ssh abc#remotemachine1.com ssh abc#remotemachine2.com"a=hello ; echo \$a"
produces no output.
Similarly:
ssh abc#remotemachine1.com "mountedDir=\$(df \tmp | grep -vi filesystem | rev | cut -d ' ' -f 1); mount | grep -w \$mountedDir"
Is working fine producing the following output :
/dev/sda2 on / type xfs (rw,relatime,attr2,inode64,noquota)
but
ssh abc#remotemachine1.com ssh abc#remotemachine2.com "mountedDir=\$(df \tmp | grep -vi filesystem | rev | cut -d ' ' -f 1); mount | grep -w \$mountedDir"
is throwing the following error:
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
Note: Passwordless ssh is established from my machine to remotemachine1.com and from remotemachine1.com to remotemachine2.com
If for some reason you do not want to modify your ssh_config file, you need to use ssh -t which will cause a real TTY to be allocated on machine 2, like so:
ssh -t abc#remotemachine1.com ssh abc#remotemachine2.com"a=hello ; echo \$a"
Be wary, as using this method implies that all the SSH login authentication procedures will happen at remotemachine1.com, so if you have security concerns, you are better off with #allo 's answer.
ssh abc#remotemachine1.com ssh abc#remotemachine2.com"a=hello ; echo \$a"
Looks wrong. If you want to jump from remotemachine1 to remotemachine2 have a look at the ProxyJump option in the ssh config. You can give it on the command line using the -o option of the ssh binary.
It finally worked after I added multiple escape characters
ssh abc#remotemachine1.com " ssh abc#remotemachine2.com \" a=hello ;echo \\\$a \" "
And
ssh abc#remotemachine1.com " ssh abc#remotemachine2.com \" mountedDir=\\\$(df /var | grep -vi filesystem | rev | cut -d ' ' -f 1); mount | grep -w \\\$mountedDir | grep -vi 'noexec' \" "

ssh-add from bash script and automate passphrase entry

I am trying to do ssh-add from script (don't care about about security at the moment).
Now ssh prompts for passphrase, which needs to be automated, so i read couple of things like this and found expect.
And now i do following:
eval `ssh-agent -s`
script tmp.sh defined as :
#!/usr/bin/expect
spawn ssh-add /root/.ssh/id_rsa
expect "Enter passphrase for /root/.ssh/id_rsa:"
send "my_pass"
interact
./tmp.sh
ssh-add -l
If ssh-add would have worked it shows something like
4096 SHA256:wlfP/nhVSWXLcljBOen5GSYZXJGgfi/XJWfZeBwqRsM id_rsa (RSA)
But instead i get The agent has no identities. Seems like ssh-agent looses it's context.
Am open to other solutions to do this.
Personally, I find the use of expect a bit cumbersome. The following approach found how to make ssh-add read passphrase from a file rather informative.
So if your version of ssh-add allows the -p argument and you are not worried about security then this should work:
#!/bin/bash
# store a file somewheres with your passphrase. For example's sake
# I'll just use $HOME/.myscrt
<$HOME/.myscrt ssh-add -p ~/.ssh/id_rsa
Now if -p is not an option for you, I found the second method mildly ingenious:
#!/bin/bash
# Same passfile and some minor enhancements from the OP of the linked
# solution
PASS="$(<$HOME/.myscrt)"
# the following is just a one-liner method of making an executable
# one-line script echoing the password to STDOUT
install -vm700 <(echo "echo $PASS") "$PWD/ps.sh"
# then the magic happens. NOTE: your DISPLAY variable should be set
# for this method to work (see ssh-add(1))
[[ -z "$DISPLAY" ]] && export DISPLAY=:0
< id_rsa SSH_ASKPASS="$PWD/ps.sh" ssh-add - && shred -n3 -uz $PWD/ps.sh
When I tested the script I called "j", see below:
$ cd /tmp
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/me/.ssh/id_rsa): /tmp/id_rsa
Enter passphrase (empty for no passphrase): asdfasdf
Enter same passphrase again: asdfasdf
Your identification has been saved in /tmp/id_rsa.
Your public key has been saved in /tmp/id_rsa.pub.
The key fingerprint is:
ed:1a:ae:c7:ac:47:5e:31:98:8e:18:8f:1c:67:94:6d jimconn#redapt-240
The key's randomart image is:
+--[ RSA 2048]----+
| o |
| o E |
| . . o |
| o o o.o |
| . O oS .o |
| + o o.. |
| =... |
| .*o |
| o=o |
+-----------------+
$ echo 'asdfasdf' > ~/.myscrt
$ chmod 0600 ~/.myscrt
$ ls -altr ~/.myscrt
-rw------- 1 me me 9 Feb 16 19:00 /home/me/.myscrt
$ cat ~/.myscrt
asdfasdf
$ ls -ltr
total 12
-rw-r--r-- 1 me me 400 Feb 16 18:59 id_rsa.pub
-rw------- 1 me me 1766 Feb 16 18:59 id_rsa
-rwx------ 1 me me 151 Feb 16 19:04 j
$ cat j
#!/bin/bash
PASS="$(<$HOME/.myscrt)"
install -vm700 <(echo "echo $PASS") "$PWD/ps.sh"
cat id_rsa | SSH_ASKPASS="$PWD/ps.sh" ssh-add - && shred -n3 -uz $PWD/ps.sh
$ ./j
‘/dev/fd/63’ -> ‘/tmp/so/ps.sh’
Identity added: (stdin) ((stdin))
$ ls
id_rsa id_rsa.pub j
So, one thing to quickly note about this method is that listing the identities loaded into ssh-agent will only show that stdin was loaded:
$ ssh-add -D
All identities removed.
$ ssh-add -l
2048 ed:1a:ae:c7:ac:47:5e:31:98:8e:18:8f:1c:67:94:6d (stdin) (RSA)
$ ./j
‘/dev/fd/63’ -> ‘/tmp/so/ps.sh’
Identity added: (stdin) ((stdin))
$ ssh-add -l
2048 ed:1a:ae:c7:ac:47:5e:31:98:8e:18:8f:1c:67:94:6d (stdin) (RSA)
UPDATED BECAUSE THE FIRST ONE DID NOT WORK
I did not try this, but if it is realy about expect loosing the context, it might be a good idea to set it up later:
auto-passphrase-add.expect (replacing tmp.sh)
/usr/bin/expect
spawn ./ssh-agent-ssh-add.sh /root/.ssh/id_rsa
expect "Enter passphrase for /root/.ssh/id_rsa:"
send "my_pass"
interact
ssh-agent-ssh-add.sh
#!/bin/sh
eval `ssh-agent -s`
ssh-add "$#"

Automate SSH Configuration for new Hadoop cluster

Guessing this has been done many times, but I could not find a clean answer, so I am appealing to your expertise for a better solution:
Objective: I am setting up a moderate sized RHEL Hadoop cluster and want to automate the configuration of SSH connectivity between all nodes. On the first node I have a list of all of the IPs in the file 'remote_ips', and I have the hduser password in the file 'hduser_pw' (chmod 600).
Step 1) Create "hduser_pw" password file on each host
for x in $(cat remote_ips); do sshpass -p$(cat hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$x "echo $(cat hduser_pw) > hduser_pw; chmod 600 hduser_pw"; done
Step 2) Generate RSA keys for each node in cluster:
for x in $(cat remote_ips); do sshpass -p$(cat hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$x "echo -e 'y'| ssh-keygen -t rsa -N \"\"
"; done
Step 3) Copy the file 'remote_ips' to each node in the cluster:
for x in $(cat remote_ips); do sshpass -p$(cat hduser_pw) scp -o StrictHostKeyChecking=no remote_ips hduser#$x:~; done
Step 4) For each node, copy the RSA public key to "authorized_keys" in every other node:
for x in $(cat remote_ips); do sshpass -p$(cat hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$x 'for y in $(cat remote_ips); do cat /home/hduser/.ssh/id_rsa.pub | sshpass -p$(cat hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$y '\''cat >> .ssh/authorized_keys'\'' ; done '; done
Is there a better way to do this? Really appreciate your help.
EDIT: Here are my revisions:
I incorporated the feedback from #janos and #likewhoa --> I handled the UUOC with redirection, looped across each IP, removed any pwds from the shell history with variables, etc. Thanks so much!
hduser_pw=$(< hduser_pw)
remote_ips=$(< remote_ips)
for x in $remote_ips; do
echo "Create hduser_pw password file on node: ${x}"
sshpass -p$hduser_pw scp -o StrictHostKeyChecking=no hduser_pw hduser#$x:~
echo "chmod 600 hduser_pw on node: ${x}"
sshpass -p$hduser_pw ssh -o StrictHostKeyChecking=no hduser#$x "chmod 600 hduser_pw"
echo "Generate RSA keys for: ${x}"
sshpass -p$hduser_pw ssh -o StrictHostKeyChecking=no hduser#$x "echo y | ssh-keygen -f ~/.ssh/id_rsa -t rsa -N \"\""
echo "SCP the file remote_ips to node: ${x}"
sshpass -p$hduser_pw scp -o StrictHostKeyChecking=no remote_ips hduser#$x:~
done
for x in $remote_ips; do
for y in $remote_ips; do
echo "ssh-copy-id from node ${x} to node ${y}:"
sshpass -p$hduser_pw ssh -o StrictHostKeyChecking=no hduser#$x "sshpass -p${hduser_pw} ssh-copy-id -i ~/.ssh/id_rsa.pub hduser#${y} -o StrictHostKeyChecking=no";
done
done
Instead of running $(cat hduser_pw) and $(cat remote_ips) multiple times,
it would be better to run them only once and, save in variables and reuse.
For example:
hduser_pw=$(cat hduser_pw)
remote_ips=$(cat remote_ips)
# Step 1) Create "hduser_pw" password file on each host
for x in $remote_ips; do
sshpass -p$hduser_pw ssh -o StrictHostKeyChecking=no hduser#$x "echo $hduser_pw > hduser_pw; chmod 600 hduser_pw"
done
Can you spot the security problem? The echo $hduser_pw will be saved in the shell history, and may also be visible in ps printings. It's better to avoid it. Instead of this:
ssh server "echo $hduser_pw > hduser_pw; chmod 600 hduser_pw"
You can do like this:
ssh server "cat > hduser_pw; chmod 600 hduser_pw" < hduser_pw
That's a bit hacky and may be confusing.
A simpler option is to scp and then ssh (for the chmod):
scp hduser_pw server:
ssh server "chmod 600 hduser_pw"
Other simplifications:
Instead of echo -e 'y' | ... you can simplify to echo y | ...
Instead of the messy Step 4, take a look into ssh-copy-id if it's available (usually it is in Linux systems)
First off UUOC, you really should use a while loop and re-directions instead of CAT.
Use this instead for the following steps which merges all steps into one big while loop.
while read ips; do sshpass -p$(<hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$ips "echo $(<hduser_pw) > hduser_pw; chmod 600 hduser_pw"; sshpass -p$(<hduser_pw) ssh -o StrictHostKeyChecking=no hduser#$ips ssh-keygen -f ~/.ssh/id_rsa -t rsa -N \"\"";sshpass -p$(<hduser_pw) scp -o StrictHostKeyChecking=no remote_ips hduser#$ips:;done <remote_ips
I'll let you fix Step 4 on your own. GL!
Few pointers.
Use while loops when reading through lines
ssh-keygen -f ~/.ssh/somekey to avoid 'echo y | ssh-keygen ...'
Use redirect '
Good luck! Perhaps this is best if you use some kind of Configuration Management tools to automate this and more.

Resources