In my last post about The Easiest Way to Build a Network Automation Lab, I have introduced the topic of SSH key-based authentication. Although that post was specific to Networking, this particular post is quite applicable to anything Linux and UNIX-like. I will specifically be performing all procedures on Centos 7 but by no means it will not apply to other Linux distributions. There should be minimal differences, if not, none at all. Let's dive in.
Just to introduce the setup, I'll be using just 2 hosts using Vagrant. One SSH server and one SSH client. I'm sharing my vagrant file below in case you're using vagrant, otherwise, it is not necessary. You can use any 2 Linux hosts that you have.
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.provider "virtualbox" do |vb| vb.memory = "4096" end config.vm.define "ssh-client" do |sshc| sshc.vm.host_name = "ssh-client" sshc.vm.network "private_network", ip: "192.168.56.179" end config.vm.define "ssh-server" do |sshs| sshs.vm.host_name = "ssh-server" sshs.vm.network "private_network", ip: "192.168.56.180" end end
Let's get started with the steps.
Step 1. Generate the SSH key pair (RSA key pair).
[vagrant@ssh-client ~]$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/vagrant/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/vagrant/.ssh/id_rsa. Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Tg4w3Q6AYlzYmGM3MSeVWg6MvPTtaHlNhpjqoKVCCoo vagrant@ssh-client The key's randomart image is: <snip> [vagrant@ssh-client ~]$
This procedure generates a key pair. One public key, and one private key. Both will be used to encrypt/decrypt each other in a process called Public Key Cryptography.
Please note that in this case, I have entered a passphrase for the key. It's pretty much like a password to your keys. To be specific, it is applied to your private key file. Please note that this is different from having a password on your account. Without this passphrase, you won't be able to unlock your private key. This means you won't be able to decrypt messages using your private key. This effectively provides you with 2 layers of protection, first is your private key, and second is your passphrase used to unlock that private key.
Optionally you can also specify the key size (number of bits) using the -b option as well as an optional comment (explicitly set the comment which defaults to username@hostname - vagrant@ssh-client as above) using the -C option.
[vagrant@ssh-client ~]$ ssh-keygen -t rsa -b 4096 -C "email@example.com"
Now let's view the public part of our key pair.
[vagrant@ssh-client ~]$ cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDjZ8MDk/5obYv1W1TvnWeyFiYM/xewZjCvfsr4Wz8XjAbzIMVz9PPAfU<snip>Fr/UbehVHvhcGOw52jMtb vagrant@ssh-client
Copy this long string as we will use this to allow our client on the server.
In this step, we will have to include the public key of our client on the authorized keys on the server. Let's perform the following on the server.
[vagrant@ssh-server ~]$ echo "<paste your public key here>" >> ~/.ssh/authorized_keys
There is a file named authorized_keys in every host you will SSH in to. To allow key-based authentication to that host we will have to add our client's public key to that file. Each key is added per line, so as you can see I added our public key by appending it to the file.
You may verify the public key was added by viewing the file.
[vagrant@ssh-server ~]$ cat ~/.ssh/authorized_keys
A shortcut for this step is by using the command ssh-copy-id. You can use this as long as you have an existing password-based log in on the remote server.
[vagrant@ssh-client ~]$ ssh-copy-id <remote username>@192.168.56.180
Step 3. Attempt to log in to the SSH server
[vagrant@ssh-client ~]$ ssh firstname.lastname@example.org The authenticity of host '192.168.56.180 (192.168.56.180)' can't be established. ECDSA key fingerprint is SHA256:h2C6TybYg6EmPIp6AgCYH/iGWuftqqE64hNLdCf6pOM. ECDSA key fingerprint is MD5:8a:80:fc:51:6d:3d:4b:ad:9f:64:7c:06:3e:4d:12:04. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.56.180' (ECDSA) to the list of known hosts. Enter passphrase for key '/home/vagrant/.ssh/id_rsa': Last login: Wed Mar 14 08:22:33 2018 from 10.0.2.2 [vagrant@ssh-server ~]$
Since this is our first time to log in to the server we are prompted to confirm if we wish to continue connecting. This also means that the remote server will be added to our known_hosts file. Since this is a lab environment, I just answered yes without checking, but in production, you may have to double check the presented signature. Finally, we are asked to enter our passphrase and we are able to log in to our SSH server.
Going back to our client I can see that we indeed have an entry created for our SSH server (192.168.56.180) in our known_hosts file.
[vagrant@ssh-client ~]$ cat ~/.ssh/known_hosts 192.168.56.180 ecdsa-sha2-nistp256 <snip>HYtCi9mhWKQ5oGu9dxbkJSSwz3YkMlmlh/yYRNC7j9DSONjbD/yj9N8=
This looks all good, but it seems that we are missing the whole point of automation here which is to avoid any human intervention when we log in (I mean when our scripts/programs log in if we start using them). The passphrase we have set on our keys does provide the required security suitable for production use. However, it will prevent us from letting our scripts go off unattended. The solution to this challenge is to use SSH key agents which leads us to our next step.
Step 4. Configure an SSH key agent
To use an SSH key agent we will be using the built-in ssh-agent tool. As a further step, we will include it in our bashrc file so that it loads all the time and we don't have to think about it.
Let's try to append our little shell script to our bashrc file.
[vagrant@ssh-client ~]$ cat >> ~/.bashrc
if [ -z "$SSH_AUTH_SOCK" ] ; then exec /usr/bin/ssh-agent $SHELL fi
Press enter then CTRL+D
Validate the new lines.
[vagrant@ssh-client ~]$ tail -n 3 ~/.bashrc if [ -z "$SSH_AUTH_SOCK" ] ; then exec /usr/bin/ssh-agent $SHELL fi
Source the bashrc file for the changes to take effect.
[vagrant@ssh-client ~]$ source ~/.bashrc
Finally, load the private key.
[vagrant@ssh-client ~]$ ssh-add ~/.ssh/id_rsa Enter passphrase for /home/vagrant/.ssh/id_rsa: Identity added: /home/vagrant/.ssh/id_rsa (/home/vagrant/.ssh/id_rsa)
You will be asked to enter your passphrase, however, this is the only time you will have to do so. Subsequent SSH connections using this private key will not require you to re-enter your passphrase. Let's test it to confirm.
[vagrant@ssh-client ~]$ ssh email@example.com Last login: Wed Mar 14 08:30:14 2018 from 192.168.56.179 [vagrant@ssh-server ~]$ exit logout Connection to 192.168.56.180 closed. [vagrant@ssh-client ~]$ ssh firstname.lastname@example.org Last login: Wed Mar 14 10:24:58 2018 from 192.168.56.179 [vagrant@ssh-server ~]$ exit logout Connection to 192.168.56.180 closed. [vagrant@ssh-client ~]$
As you can see, I have logged in to the SSH server twice and was never asked for a passphrase. This confirms that our private key is loaded and unlocked when we first entered the passphrase.
That's about it for this blog. In my next post, I'll be discussing how to test the same SSH key-based authentication that we have done here using our network automation lab.