Do you ever find yourself trying to remember all the server names or IP addresses along with the usernames you need to log in to them? And what if you're using SSH key-based authentication (like I discussed here) and you're using different keys on different group of servers. It's actually not practical to maintain all of this information externally. In fact, you really are supposed to manage this in SSH itself.
The standard way to handle this is within the SSH (client) config file. You'll see how this is done in a bit but first, let me introduce you to our lab scenario. It's very simple but it proves the point of this topic. If you're like me who uses vagrant, below is the vagrant file that I will be using for this lab scenario.
# -*- 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-1" do |sshs1| sshs1.vm.host_name = "ssh-server-1" sshs1.vm.network "private_network", ip: "192.168.56.180" end config.vm.define "ssh-server-2" do |sshs2| sshs2.vm.host_name = "ssh-server-2" sshs2.vm.network "private_network", ip: "192.168.56.181" end end
Below is how it will look like. There is only 1 network segment and ssh-client has direct access to ssh-server-1 and ssh-server-2.
If you're not using vagrant then just create 3 Linux hosts. One will be used as the SSH client and the other 2 as the SSH servers as you can see with our vagrant file. I'm using Centos 7 here but feel free to use your distro of choice.
Now I already have a full post on setting up SSH authentication. In particular, SSH key-based authentication, which is the recommended way of using SSH logins. I won't repeat all the steps here, in fact, I would ask you to go through the previous blog post of mine How To Configure SSH Key-based Authentication on Linux. This contains all the necessary steps to configure the SSH client and server and will serve as our base set up for this lab scenario.
In that previous post, I was only using 1 SSH client and 1 SSH server. You will need to perform the same thing for our SSH client here (ssh-client) and our first SSH server (ssh-server-1). You can leave ssh-server-2 alone in the meantime. We will start with ssh-server-2 after you're done following the steps in the previous blog post.
NOTE: While going through the lab in the previous post, I suggest to just focus on the steps to get it done. Ignore the parts talking about Network Automation. This blog is applicable to a Network Automation use case, where the SSH servers are networking devices. However, this blog is meant to be simple and not require additional lab components than it has to. But there is nothing stopping you from applying the topic in this post to Testing SSH Key-based Authentication for Network Automation.
If you're done following the steps as mentioned above then we can now proceed with ssh-server-2. We will repeat the steps with ssh-server-2, however, we will generate a different SSH key pair for it so that we can have some variance between the 2 SSH servers.
Let's go through this again step by step.
Step 1. Generate the SSH key pair (RSA key pair) for ssh-server-2.
[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): /home/vagrant/.ssh/id_rsa_server2 Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/vagrant/.ssh/id_rsa_server2. Your public key has been saved in /home/vagrant/.ssh/id_rsa_server2.pub. The key fingerprint is: SHA256:QD3zS/woGEjVKh4N2eMP9qsDIa5UaY7LZU5R0yOLgYs vagrant@ssh-client The key's randomart image is: <snip> [vagrant@ssh-client ~]$
As you can see I have specified a different key file name for server2 - id_rsa_server2. This means that when we configure the login parameters for the 2 SSH servers later they will use different keys.
Let's view the public part of our new key pair.
[vagrant@ssh-client ~]$ cat ~/.ssh/id_rsa_server2.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGNsrQ4li4OpTnpZqWhPaZ03cLCHwX0LYatLBivEtwJF5kfOzNyCbdSS<snip>pwiOBCefhE8zs9Ff7xCXIOtzfyrYer5 vagrant@ssh-client
Again, copy this long string so we can allow our client on ssh-server-2.
Step 2. Allow the SSH client on ssh-server-2 by copying our second public key.
[vagrant@ssh-server-2 ~]$ echo "<paste the second public key here>" >> ~/.ssh/authorized_keys
Again, you may verify the public key was added by viewing the file.
[vagrant@ssh-server-2 ~]$ cat ~/.ssh/authorized_keys
Step 3. Attempt to log in to ssh-server-2.
[vagrant@ssh-client ~]$ ssh -i /home/vagrant/.ssh/id_rsa_server2 email@example.com Enter passphrase for key '/home/vagrant/.ssh/id_rsa_server2': Last login: Fri Apr 6 14:19:31 2018 from 10.0.2.2 [vagrant@ssh-server-2 ~]$
As you can see I did something different here. I specified an SSH private key to use while I log in to ssh-server-2. If I didn't do this it will use the default one - id_rsa (the one we first created from the previous blog).
Step 4. Add the second private key to our SSH agent.
[vagrant@ssh-client ~]$ ssh-add ~/.ssh/id_rsa_server2 Enter passphrase for /home/vagrant/.ssh/id_rsa_server2: Identity added: /home/vagrant/.ssh/id_rsa_server2 (/home/vagrant/.ssh/id_rsa_server2)
Let's test the login and make sure we can log in without typing in our passphrase.
[vagrant@ssh-client ~]$ ssh -i /home/vagrant/.ssh/id_rsa_server2 firstname.lastname@example.org Last login: Fri Apr 6 14:34:46 2018 from 192.168.56.179 [vagrant@ssh-server-2 ~]$
Alright, we're done with the SSH access. Now we can move to the fun part. As you have noticed, it will be tedious to keep specifying our second SSH private key whenever we log in to ssh-server-2. If we were using varying usernames, or for example a non-standard SSH port number on some of our servers, then we will have to explicitly supply those in our SSH parameters as well. This is where the SSH config file comes in. It's a file wherein we can specify the different hosts we log in to along with any specific login parameters for each host.
NOTE: We will be editing files here which is something I was trying to avoid in my previous blogs. My preferred editor is vi, which I understand is quite hard to grasp in case you are new to Linux that's why I never used it in any of my blog posts. However, there are times that using an editor makes more sense. Now is that time. If you don't know vi don't worry I'll limit the steps to the bare minimum. There is an alternative editor which is easier to use - nano. The thing is that it's not installed by default in the vagrant box I'm using in my examples. I opted to just use the built-in vi editor as our use of it will be kept simple. I thought this was more simple as opposed to installing another editor just because it's more beginner friendly.
Now let's open the SSH config file using the vi editor.
[vagrant@ssh-client ~]$ vi ~/.ssh/config
The editor will open a blank file. To start editing the file type i. This will switch the vi editor to insert mode (editing mode). From here you can start typing and editing the file. If this is too unfamiliar to you, just pretend that you are using a normal word editor.
Under insert mode type the following:
Host server1 Hostname 192.168.56.180 User vagrant Host server2 Hostname 192.168.56.181 User vagrant IdentityFile ~/.ssh/id_rsa_server2
Once you are done press ESC. Then the colon character : (note it's colon : not semi-colon ;). Finally, type wq and you should see exactly these characters at the bottom left of your screen :wq. At this point you can press ENTER. wq basically means write + quit. If you made some mistake and find it difficult to revert for whatever reason, you may follow exactly the same steps as I just mentioned but omit the w. So it should look like :q at the bottom left of your screen. This means you will simply quit the vi editor.
Now as you can see the parameters in the config file we just wrote are almost identical except for the explicit IdentityFile parameter for ssh-server-2 which I named just server2 in this case. This pretty much resembles what we are doing on the command line, only that we are placing them all in a config file which makes all these definitions repeatable.
Also, just to point out the use of simpler names - server1 and server2, I did this to show you that it's possible to create some kind of an alias for your hosts. The Hostname is the real host while Host is just a name used to refer to that host along with its specific parameters. You can essentially create shortcuts/shorthands, whatever you might want to call it, for your SSH logins.
With this simple example, you can see how this can become powerful, and extremely useful especially if you are managing a large number of servers or hosts.
Before we wrap it up let's test our access to see if our config file is working.
[vagrant@ssh-client ~]$ ssh server1 Last login: Fri Apr 6 15:31:56 2018 from 192.168.56.179 [vagrant@ssh-server-1 ~]$ exit logout Connection to 192.168.56.180 closed. [vagrant@ssh-client ~]$ ssh server2 Last login: Fri Apr 6 15:58:06 2018 from 192.168.56.179 [vagrant@ssh-server-2 ~]$ exit logout Connection to 192.168.56.181 closed. [vagrant@ssh-client ~]$
And there you have it. We just made our SSH life a lot easier.
Now I'm not sure if you noticed but our example is just about having separate definitions for hosts with varying parameters. But how about if we have multiple hosts which have the same parameters? Wouldn't it be more efficient to just have a single definition to cover them all? This type of scenario is actually very common and it's covered as well by our handy SSH config file.
You can do something like this.
Host 192.168.56.* # Standard parameters across all hosts covered by this range
Go ahead and try it out! Convert the existing scenario that we did here to the example as above. That way you can play around and learn things by yourself. It should be a good exercise.