So far in this series of posts on
ssh on macOS:
- Quick Introduction to
sshfor Mac Admins
- SSH Keys, Part 1: Host Verification
- SSH Keys, Part 2: Client Verification (this post)
- Transferring files with SSH
- SSH Tunnels
Please consider supporting Scripting OS X by buying one of my books!
In the previous post we discussed how
ssh uses public and private keys to secure the data sent back and forth, between the remote computer (host) and your machine (client). The keys allow you to be sure that you are talking to the correct host and not an imposter.
However, how does the host know that you are who you claim to be?
The default way to authenticate yourself to the
ssh host is to give it the correct username and password combination. This is and has been a common practice for computers, servers, websites for a long time and it works for
ssh as well.
You will obviously need an account on the host for this to work. In larger organizations the computers will often be connected or bound to a directory service, to centralize the user and group management. If the computers are not bound to a central directory, as is often the case with mobile computers, you can use your management system to install one or more managed users on the system.
In the Sharing Preference Pane, you can control which users or groups of users can use
ssh or ‘Remote Login’ to connect to this Mac. Behind the scenes the users and groups that can connect with
ssh are in the
com.apple.access_ssh group. You can find some scripts to manage the controls in this post.
Keys for the Client
Passwords, and user access control groups already provide a strong security, but have some disadvantages as well:
- you have to re-enter the password on every connection. This is especially tedious when you send individual commands with the
ssh hostname commandsyntax
- automation is insecure because you have to provide the password in the script (in clear text)
ssh we can use the same asymmetric keys to validate the client to the host, as we did to verify the host.
Sidenote: you will see a lot of actual keys and fingerprints in this post. While sharing the public key should not be a security issue, I deleted them all after finishing this post, just to be safe.
Creating a Key
First, you need to generate the key for your user. This is simple enough with
$ ssh-keygen -t rsa -b 2048 -C "Armin RSA 2017-07-06" Generating public/private rsa key pair. Enter file in which to save the key (/Users/armin/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/armin/.ssh/id_rsa. Your public key has been saved in /Users/armin/.ssh/id_rsa.pub. The key fingerprint is: SHA256:jRydlFk3rRTpuKshFvnzon7UnzcYiHLQhKpQvLqPrl0 Armin RSA 2017-07-06 The key's randomart image is: +---[RSA 2048]----+ | . . .+. ++ | | o . +o. .o..| | . . . + o + . | | . . . o * . o | | o . S o... | | . . . =..o. | | . E =.+ oo. | | o.. . .o+..o..| |+oo. .oo.o. ..| +----[SHA256]-----+
This command will create a new key pair of type ‘rsa’ (
-t rsa) with a bit length of 2048 (
-b 2048) and a comment of
Armin RSA 2017-07-06.
RSA is the recommended type for best cross platform compatibility. Mac hosts and clients also support ECDSA, and ED25519. (DSA is in the list but considered insecure and obsolete.) When you connect to other, non-macOS, hosts, ask the administrators for the best key type to use.
The bit length of the key determines how complex the key is. Longer bit lengths are more secure, but require more CPU power for the encryption and decryption. Different key types can have different values for the bit length, read the
ssh-keygen man page for details.
The comment is optional, but provides a way to add a label to a key which will help you identify it later. I usually add who created and when and if the key is for a specific purpose.
ssh-keygen tool will ask where to store the key pair. Since this will be our ‘generic’ key, the default name and location
id_ecdsa, etc.) is exactly what we want. Sometimes you may have to create a key specifically for a single host, then you can choose a different name here, so you do not overwrite your other keys.
Next the tool will ask you for a passphrase. This will be used to encrypt (lock) the private key, so even if someone gets their hands on the private key file, they will not be able to use it. Since the private key represents your
ssh identity it should be protected well. Use a password generator to create a long passphrase. Store the passphrase in a secure place (Password manager, you will need it again) and enter it here (twice).
Finally, the tool will show the files it generated (the private and public key), its fingerprint (with the comment) and a ascii art representation of the key, which is supposed to be more visually recognizable than the alphanumeric fingerprint.
Copying the Key to the Host
When we set up an
ssh connection to a host we need to copy its public key into our
ssh settings (the
known_hosts file). Similarly we need to copy our public key to the host.
You have to copy the public key (i.e.
id_rsa.pub) to the host and append its contents to the
~/.ssh/authorized_keys on the host in your user home directory. On some systems, the file may also be called
authorized_keys2 and/or be in a different location.
On Mac OS X versions 10.11 and older you can use this command:
$ cat ~/.ssh/id_rsa.pub | ssh [user@]mac.example.com "cat >> ~/.ssh/authorized_keys"
This will pipe the contents of your public key file into
ssh, which sends it to the remote host and appends (
>>) it to the
~/.ssh/authorized_keys file. (This will fail if the
~/.ssh directory does not exist yet on the host.)
On many Linux systems and on macOS Sierra 10.12 there is a tool to help with this. You can use
$ ssh-copy-id [user@]mac.example.com
By default, the
ssh-copy-id command will copy all available public keys to the host. You can tell it to use a specific key with the
-i option. Either way, since the key is not yet in place, the command will need to provide you with the host’s password to authenticate to the host.
ssh-copy-id command gives helpful additional information:
$ ssh-copy-id mac.example.com /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/armin/.ssh/id_ecdsa.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys Password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'mac.example.com'" and check to make sure that only the key(s) you wanted were added.
Now, when you attempt to connect to the remote host, you will be prompted to unlock your private key:
$ ssh mac.example.com Enter passphrase for key '/Users/armin/.ssh/id_ecdsa': Last login: Thu Jul 6 09:00:28 2017 from [IP Address] mac:~ armin$
and when you look at the contents of
~/.ssh/authorized_keys you will see your public key listed:
mac:~ armin$ more .ssh/authorized_keys ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAC3TR7ZYSjJO01JVNYMeXWNNgglNulQVuGGTnE2JiBULbs9In9uWrFWyqOXodi4XCs7lY92undONnN4wQKumsN/RgBnEDATKZZdh5OPI72ZGrmnPNXVUbU8mjzIJ4LpWfTu0x6nqR9zBu1LPNW9xPoQNwTogh7jIHxvEcYav1slKjeRkg== Armin ECDSA Key 2017-07-06
Note that the comment we added when creating the key is part of the public key (and its fingerprint). Considering that this file can contain multiple keys from different sources, the comment will help a lot in locating the proper key.
When a key is compromised or just not used any more, it should be removed from the host’s
authorized_keys file. This file should be audited frequently.
Agent of Keys
When we connected to the remote host after copying the key, we did not have to enter the password for the host, because the key pair confirmed our identity. However, you still had to unlock the private key with the passphrase. You stored that somewhere secure, right? Without the passphrase the key is useless.
This does simplify passwords, because you can use the same key for multiple hosts, and you don’t have to memorize all those passwords. However, entering the passphrase over and over is tedious and error prone, too. Thankfully there is a solution to this. There is a process that can store the passphrase for the duration of your login session. The process is called
ssh-agent. However you do not talk directly with this tool but use
ssh-add to manage it instead. And on macOS you don’t even have to do that, because
ssh-agent are integrated with the macOS Keychain.
The role of the
ssh-agent is to hold on to your private key passphrases for the duration of your login session. On other systems (or with Keychain integration disabled) you have to add the passphrase once per login session with
$ ssh-add ~/.ssh/id_rsa
(this will prompt for the passphrase)
You can list the passphrases that
ssh-agent is holding with
$ ssh-add -l
and clear all of them with
$ ssh-add -D
Note: on macOS Sierra (10.12), Keychain integration is not enabled by default. To enable it, add these two lines at the top of
~/.ssh/config(create the file if necessary):
AddKeysToAgent yes UseKeychain yes
With Keychain integration enabled,
ssh-agent will also look for passphrases stored in the keychain and then remember them for the rest of the session. The passphrase is stored as an application password with the path to the private key in the name. So as long as the keychain is unlocked, it will find the passphrase in the keychain and use it to unlock the private key (and remember it for the rest of the login session).
On macOS Sierra, the passphrases are not stored in standard login keychain and not synced with iCloud.
Managing Keys with multiple clients
If you have many client Macs you use to connect to the same hosts, you have two choices:
- you can create a new key pair in each client and add the public key to the host’s
- you can copy the key pair from one client to another
The first option can grow unwieldly quickly when you are managing several Macs and several users.
The second option might expose your private key when you copy them from one client to another. If you send the private key unencrypted over the network it can be intercepted by a man-in-the-middle attack. If you copy it on a USB stick to transfer, you should remember to securely delete any copies, after the transfer.
Your organization will (should) have rules on how to setup and manage keys.
Given proper key management, client verification with keys is safer than with username and passwords. Because of this some setups only allow
ssh connections an authorized key.
In this case you cannot use
ssh-copy-id to transfer and add your public key to the host. You will have to provide the public key to an administrator to add it for you. The
authorized_keys file will probably be in a different, central and locked-down location.
ssh and keys
You can write scripts that use
ssh to communicate with another host. This can yield some powerful workflows. However, if the script uses
ssh repeatedly, you do not want to have to enter the password over and over. If the script runs within a user session,
ssh-agent can provide the passphrase when necessary. But when a script has to run in background on a schedule without user interaction,
ssh-agent will not be available.
In this case you can create a key with an empty passphrase. This key will be unlocked automatically. Obviously, such a key would need to be protected very well, and its use should be audited closely. A key with an empty passphrase is the security equivalent of leaving the house key under the doormat. However, for some automated workflows, it may be the best solution.
- previous post:
sshuses public/private keys to verify the host and encrypt the connection
sshcan also use public/private keys to verify the user connecting to the host
- you generate a key with
- the public key is added to the remote host’s
- the private key remains on the client and is locked with a passphrase
ssh-agentand Keychain integration on macOS can simplify access to the private key’s passphrase
Next post: Transferring Files with