Menu for selecting SSH keys to load
Overview
In this post I want to explain to you how and why I created a selection menu to load ssh identities of my choosing.
Using ssh keys for authentication in terminal sessions makes life simpler when setup correctly. If you are like me, you have collected a bunch of ssh identities for work, home, test and perhaps some cloud services. Alltogether these can number up quickly.
Having all these ssh identities added to your ssh-agent can have the disadvantage that your connection cannot establish because too many wrong keys have been presented to your targeted host before the one that opens the lock to that system is presented.
One approach is to increase the number of retries before the ssh server of your targeted system shuts the door on you. Not sustainable when using a lot of test machines that are destroyed as quickly as you have created them.
From a security perspective, I do not want ALL my identities presented to systems that I do not own/manage. Therefore I do not add ALL my identities to my ssh-agent for “just in case …”. When a “Publick key denied” message is thrown back, I will have to load the identity that enables access to that system.
Using the ~/.ssh/config file can be of great help to assist with many hosts and identities. Although you can work with wildcards, managing your connections here can be cumbersome.
As a solution to this dilemma I have chosen to work with a menu that presents me all the private identities I have on my system. From here I select the key I want to add and the script will start the process to add the key to the ssh-agent. If protected with a password, I will be prompted for the password to unlock the private key.
Let’s dive in deeper how I added this approach to my system and how it helps me.
The script step by step
Location of ssh identities
The default location of ssh identies for Linux or MacOS based systems using openssh is under your homedirectory in the hidden folder .ssh. In case you have a different location for your identities, you can change this value to support your needs:
1
SOURCE_DIR="${HOME}/.ssh"
Read files into an array
Next step is to load all private key identity files. These are filtered out
1
2
ARR_FILES=( $(grep -d skip -l "BEGIN" ${SOURCE_DIR}/*) )
What happens here:
ARR_FILESis the variable where the output of the grep command is stored in- Arrays are defined by using
( )after the=
- Arrays are defined by using
grepis the command used to search in the files for a specific string-d skipspecifies that directories have to be skipped-lspecifies to list the file names that contain the string we are looking for"BEGIN"is the sting we are looking for. Any private key has in the header this word${SOURCE_DIR}/*specifies the location where to filter the files we want to check
Option to exit the menu
The scripts provides a menu list of all files we can work with. We need to have an option to exit the menu. So we add an additonal item to the array for that purpose.
1
ARR_FILES+=( "Exit selection ..." )
To add an additional value to the array we use the + before the = and include the additional value between ( ).
Start the logic
For ease of use I use functions to make the logic easier to comprehend. I grouped the work in the function fnCreateMenu that is assisted by another function fnAddSSH.
At the bottom of the script I call the main function:
1
fnCreateMenu "${ARR_FILES[@]}"
What happens here is:
- The function
fnCreateMenuis called - All values of the array
ARR_FILESare passed to it by specifying the selector[@]which means ALL
Building the menu
The function fnCreateMenu builds the menu. What happens I will discuss step by step:
1
select option; do # in "$@" is the default
select optionprovides the user of the script the choicedo # in "$@" is the defaultwhere#is the total number of items in the arrayin "$@" is the defaultin the list of items presented by the arry. The part “is the default” is not clear to me.
1
2
3
4
if [ "$REPLY" -eq "$#" ];
then
echo -e "Exiting..."
break;
- This
ifstatement compares if the input"$REPLY"is equal-eqto the total number of items in the array"$#"e.g. the last option we have added to the list of files. thenif it is trueecho -e "Exiting..."Echo that we will exit the loopbreak;to break out of the loop
1
2
3
4
5
elif [ 1 -le "$REPLY" ] && [ "$REPLY" -le $(($#-1)) ];
then
echo -e "You selected $option which is option $REPLY"
fnAddSSH
break;
- An additional condition with
elifto select the correspondig option listed thenif it is true- Provide some output
- Call the function
fnAddSSH(details below) - When function
fnAddSSHis completed,breakout of the function to exit.
- Call the function
1
2
else
echo -e "Incorrect Input: Select a number 1-$#"
- What happens
elseif the before conditions are not met- Provide output that an incorrect input is provided and a range to choose from
1-$#where#is the total number of items in the array
- Provide output that an incorrect input is provided and a range to choose from
1
2
fi
done
fito close the end of theifloopdoneto end theselectstatement
Adding the key to the ssh agent
The function fnAddSSH adds the selected key to the ssh agent
1
2
3
4
fnAddSSH() {
echo -e "Adding SSH Key $option"
ssh-add $option
}
- Provide what key
$optionwill be added- where
$optionis the filename with path
- where
ssh-add $option- where
ssh-addis the actual command to add the key to the ssh agent, and $optionis the key name with full path
- where
This concludes the explanation of the script.
Accessible system wide
The whole purpose is making it easier to load identities to the ssh-agent. Preferably form anywhere on your system where you are currently working. For example in your GitHub repostiory somewhere in a directory tree. The best way is to add this script to a directory that is part of your $PATH.
When using the bash shell the .bashrc file will automatically add the directory bin to your $PATH if the folder exists under the root of your homedirectory.
1
2
cp add-ssh.sh ~/bin/add-ssh
chmod +x ~/bin/add-ssh
- Copy the file from the location where it is to your
bindirectory under your homedirectory~/bin/ - Make the file executable with
chmod +x
Now the script can be called from anywhere on your system and will present you the options to choose from.
The full script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env bash
SOURCE_DIR="${HOME}/.ssh"
ARR_FILES=( $(grep -d skip -l "BEGIN" ${SOURCE_DIR}/*) )
ARR_FILES+=( "Exit selection ..." )
fnAddSSH() {
echo -e "Adding SSH Key $option"
ssh-add $option
}
fnCreateMenu () {
select option; do # in "$@" is the default
if [ "$REPLY" -eq "$#" ];
then
echo -e "Exiting..."
break;
elif [ 1 -le "$REPLY" ] && [ "$REPLY" -le $(($#-1)) ];
then
echo -e "You selected $option which is option $REPLY"
fnAddSSH
break;
else
echo -e "Incorrect Input: Select a number 1-$#"
fi
done
}
fnCreateMenu "${ARR_FILES[@]}"
Find it on GitHub:
CrossCloudGuru/menu-for-selecting-ssh-keys-to-load: Menu for selecting SSH keys to load