The Secure Shell (SSH) protocol is famous for its almost impenetrable security with the proper use of keys. After configuring public key authentication, we can simply use our private keys (identities) to access remote servers. But which private key do we use for a given session?
In this tutorial, we explore several approaches to using multiple private keys for Linux SSH access. First, we briefly mention the obvious manual method. Next, we turn to the local $HOME/.ssh/config client configuration options, which can also be applied globally in ssh_config. Of course, all configuration methods require adding and maintaining proper records. Finally, we discuss automated key managers and custom SSH calls as possible ways to organize multi-key usage.
For brevity and security reasons, we only consider the newest iteration of SSH version 2 (SSHv2) as implemented by OpenSSH. Also, the -v verbose switch of the SSH client is used for verification. Most options apply to interactive connections, but also scp and other clients.
2. Manual Choice
With a library of private keys, we can always use the real-life approach of manually picking the correct key for our needs:
$ ssh -i KEY_PATH xost
Here, we employ the -i switch of our SSH client to specify the KEY_PATH. In fact, similar options exist for most clients. Further, there are commonly default key paths when none are specified:
$ ssh -v xost [...] debug1: Will attempt key: /home/baeldung/.ssh/id_rsa debug1: Will attempt key: /home/baeldung/.ssh/id_dsa debug1: Will attempt key: /home/baeldung/.ssh/id_ecdsa debug1: Will attempt key: /home/baeldung/.ssh/id_ecdsa_sk debug1: Will attempt key: /home/baeldung/.ssh/id_ed25519 debug1: Will attempt key: /home/baeldung/.ssh/id_ed25519_sk debug1: Will attempt key: /home/baeldung/.ssh/id_xmss [...]
While easier and more straightforward than others for a small number of keys, this option can get tedious very fast. With many keys in different paths, organizing all of them properly and by purpose might even make indexing necessary.
In any case, at one point, an organization is essential.
3. Based on Priority
To avoid having to specify a key each time, we can list them all in any of the SSH client configuration files:
$ cat $HOME/.ssh/config IdentityFile ~/.ssh/custom_id IdentityFile ~/.ssh/xost_id IdentityFile ~/.ssh/test $ ssh -v xost [...] debug1: Will attempt key: /home/baeldung/.ssh/custom_id explicit debug1: Will attempt key: /home/baeldung/.ssh/xost_id explicit debug1: Will attempt key: /home/baeldung/.ssh/test explicit [...] debug1: Trying private key: /home/baeldung/.ssh/xost_id no such identity: /home/baeldung/.ssh/xost_id: No such file or directory [...]
In this case, we see an explicit key specification with IdentityFile similar to the -i switch, so no defaults were tried. In addition, unlike the situation with the hard-coded defaults, there is a notification when a key is missing from a given path.
The global key list is iterated as is, so the priority of a key is relative to its row number. On the negative side, each identity will be checked and tried on every connection. This can lead to several issues:
- incorrect key applied to a given connection, i.e., security risk
- each key is checked and tested on every connection, leading to possible slowdowns
- priority is one-dimensional, which might not be enough for many scenarios involving multiple private keys
Although we can use IdentitiesOnly to limit the exposure, let’s try to avoid these potential drawbacks with other methods.
4. Hostbased Selection
The OpenSSH client configuration supports Host blocks for defining host-specific options linked to a given internal name:
$ cat $HOME/.ssh/config Host xost-main Hostname xost IdentityFile ~/.personal/xost_id User baeldung $ ssh -v xost-main [...] debug1: Reading configuration data /home/baeldung/.ssh/config debug1: /home/baeldung/.ssh/config line 1: Applying options for xost-main debug1: Reading configuration data /etc/ssh/ssh_config debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files debug1: /etc/ssh/ssh_config line 21: Applying options for * debug1: Connecting to xost [192.168.6.66] port 22. debug1: Connection established. [...] debug1: Authenticating to 192.168.6.66:22 as 'baeldung' [...] debug1: Trying private key: /home/baeldung/.personal/xost_id [...]
Here, ssh applies the options for the name xost-main from the local configuration file:
Moreover, we can have multiple hosts in our configuration, each with its own internal name and settings:
$ cat $HOME/.ssh/config Host xost-main Hostname xost IdentityFile ~/.personal/xost_id User baeldung Host xost-dev Hostname xost IdentityFile ~/.dev/xost_dev_id User dev
Now, invoking ssh xost-dev would use the options from the last three lines of the file. There are many other host-specific options, which we can apply in each Host block.
Alternatively, we can depend on an external mechanism to keep and apply keys as necessary.
5. ssh-agent and Key Managers
In most cases, there is a minimal set of operations a key manager or agent can perform:
- add key or key pair
- remove key
- get keys
- sign message
- lock or unlock with a passphrase
Essentially, key managers and agents are a secure datastore for private keys for use without delay and explicit identity specification:
$ ssh-agent bash $ ssh-add /path/to/private.key $ ssh-add -l 3072 SHA256:RiQ[...]8Nk baeldung@xost (RSA) $ ssh -v xost [...] debug1: Offering public key: baeldung@xost RSA SHA256:RiQ[...]8Nk agent debug1: Server accepts key: baeldung@xost RSA SHA256:RiQ[...]8Nk agent debug1: Authentication succeeded (publickey). [...]
In this regard, setting the IdentitiesOnly option of ssh to yes limits the keys supplied for a given connection to avoid sending every identity. Moreover, we can further enhance the functionality of agents.
In fact, projects like keychain and ssh-ident act as keychains and a front-end to ssh-agent. Keychains avoid the need to manually add or remove private keys, instead loading them as configured. This is an upgrade to the AddKeysToAgent option.
6. Custom Command Options (Git)
Regardless of whether an application has its own SSH implementation or just calls one, we can sometimes decide on a private key to apply in the application that uses it.
$ git clone -c "core.sshCommand=ssh -i ~/.ssh/work_id"
On the other hand, we can persist the option for our repository with git config or in its configuration file:
$ git config core.sshCommand "ssh -i ~/.ssh/work_id"
This way, we always select a specific key for a given repository.
In this article, we talked about ways to organize the use of multiple private keys with SSH. Generally, we discussed several strategies and how to apply them in practice.
In conclusion, we can always select a private key manually, but, depending on the scenario and security requirements, we can also employ tools and configurations to prioritize key usage.