Additional SSH client security

Following on from last week's post, this post is going to look at some additional steps you can take to keep your SSH connections secure.

Using ssh-agent

If you're using key based authentication, it's a good idea to encrypt your private key using a passphrase. Unfortunately one of the big downsides to doing this is having to decrypt your key before using it. If you have a complex passphrase this can quickly become annoying if you regularly make new connections.

One way round this problem is using ssh-agent to cache your keys in memory. This makes it possible to use an encrypted private key multiple times without having to repeatedly enter your passphrase. To start ssh-agent run the following command:

$ eval $(ssh-agent)
Agent pid 4948

This will start an ssh-agent process in the background and set the following environment variables:

  • SSH_AUTH_SOCK: the path to the socket file used to communicate with the agent.
  • SSH_AGENT_PID: the process id of the ssh-agent

Once the agent is running you can use ssh-add to cache a private key:

$ ssh-add ~/.ssh/id_rsa
Enter passphrase for /home/bob/.ssh/id_rsa:
Identity added: /home/bob/.ssh/id_rsa (/home/bob/.ssh/id_rsa)

This will decrypt the key and store it in memory. Once you've done this ssh should be able to use your private key without you having to re-enter your passphrase:

$ ssh remotehost
Last login: Fri Jun 15 19:41:40 2018 from somehost.example.com
$

You can verify which SSH keys you've currently got loaded using the -l option:

$ ssh-add -l
2048 2f:c6:62:4f:f8:e4:a7:56:df:f8:93:8d:47:a1:d2:bc .ssh/id_rsa (RSA)

If you want to unload a private key, you can use the -d option:

$ ssh-add -d .ssh/id_rsa
Identity removed: .ssh/id_rsa ( bob@somehost.example.com)

Alternatively you can use the -D option to remove all identities.

Locking and unlocking

Once you've decrypted a private key, ssh-agent will store the key in memory. To prevent anyone using the key while it's loaded you can lock the agent using the -x option. This will prompt for a password, and then prevent any access to the key:

$ ssh-add -x
Enter lock password:
Again:
Agent locked.

$ ssh remotehost
Enter passphrase for key '/home/bob/.ssh/id_rsa':

To use the keys loaded in the agent again, use the -X option to unlock the key:

$ ssh-add -X
Enter lock password:
Agent unlocked.
$ ssh remotehost
Last login: Fri Jun 15 21:10:15 2018 from somehost.example.com
$

Verifying remote hosts

When you first connect to a host via ssh you will get a message similar to the following:

$ ssh remotehost
The authenticity of host 'remotehost (192.168.10.10)' can't be established.
ECDSA key fingerprint is 61:13:87:c6:13:0c:b4:d4:0c:d6:86:53:b3:b7:a1:42.
Are you sure you want to continue connecting (yes/no)?

Ideally you should verify the fingerprint matches the fingerprint for the host your connecting to. The ssh-keygen command can be used to display the fingerprint for a ssh host key:

[root@remotehost ~]# ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key
256 61:13:87:c6:13:0c:b4:d4:0c:d6:86:53:b3:b7:a1:42   (ECDSA)

If you type yes the public key of the remote host will be added to ~/.ssh/known_hosts. ssh will then verify the public key of the host your connecting to matches when making subsequent connections. If the public key doesn't match, ssh will display a message similar to the following:

$ ssh remotehost
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
8f:c4:e1:75:a2:cc:9d:09:b2:49:d9:6f:76:f3:7d:f3.
Please contact your system administrator.
Add correct host key in /home/bob/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/bob/.ssh/known_hosts:1
RSA host key for remotehost has changed and you have requested strict checking.
Host key verification failed.

Using VisualHostKey

Verifying hex strings is fine for a computer, however spotting visual patterns is generally easier for humans. One alternative to using a host key fingerprint is enabling ASCII art representations of public keys. This can be done using the VisualHostKey option:

$ ssh -o VisualHostKey=yes remotehost
Host key fingerprint is 61:13:87:c6:13:0c:b4:d4:0c:d6:86:53:b3:b7:a1:42
+--[ECDSA  256]---+
|     .*@=o.      |
|     oo.@=       |
|      E+=.o      |
|     . . = o     |
|      . S .      |
|       .         |
|                 |
|                 |
|                 |
+-----------------+

ssh-keygen can also be used to display ASCII art for a specific key:

$ ssh-keygen -lv -f /etc/ssh/ssh_host_ecdsa_key
256 61:13:87:c6:13:0c:b4:d4:0c:d6:86:53:b3:b7:a1:42   (ECDSA)
+--[ECDSA  256]---+
|     .*@=o.      |
|     oo.@=       |
|      E+=.o      |
|     . . = o     |
|      . S .      |
|       .         |
|                 |
|                 |
|                 |
+-----------------+

Hashing known_hosts

By default the public keys of systems you've connected to are stored in ~/.ssh/known_hosts in the following format:

{markers (optional)} {hostnames} {keytype} {base64-encoded key} {comment}

For example:

remotehost ssh-rsa AAAAB3NzaC1yc2EAAAA1sjcQhbQbmIvDvkpeAlHjQsidHJkI6mQBJFOw==

Because known_hosts stores the hostname of systems you've been connected to, anyone who managed to compromise your private key could use the file to enumerate systems which you may have configured key based authentication on. This can be prevented by setting the HashKnownHosts option in ~/.ssh/config:

HashKnownHosts yes

Once this options is set all new host entries in known_hosts will be hashed before they are written to the known_hosts file. The resulting lines will look similar to the following:

|1|GSPSrSFBcYBQ8rOQxMqpnYMSGUs=|ajy4mVNhFqIfeMc0rzMunrGb2YI= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNo...

Setting HashKnownHosts will only affect new known host entries. Existing entries can be hashed by running ssh-keygen -H:

$ ssh-keygen  -H
/home/bob/.ssh/known_hosts updated.
Original contents retained as /home/bob/.ssh/known_hosts.old
WARNING: /home/bob/.ssh/known_hosts.old contains unhashed entries
Delete this file to ensure privacy of hostnames

It's also worth noting you can use ssh-keygen -F to check if a host exists in you're known_hosts file after you've hashed it:

$ ssh-keygen -F remotehost
# Host remotehost found: line 1 type ECDSA
...