There is a time when being cool it is simply too much work. Sure we all want to impress a newbie by typing long cryptic commands in a black screen and then say “I’m in”, but at the end of the day, let’s accept it, we don’t have many visitors except the little nephews who are lurking around trying to use our machines to surf porn. In this post we’ll see how to reduce our number of keystrokes when connecting through ssh by the usage of configuration files.
In the above example we are saying to ssh to connect to '10.1.1.42' using the username 'bender' and the keyfile '~/.ssh/id_rsa_lab.pub'. All those options could be saved in the ssh configuration file and avoid typing them, so we can simply execute something like:
$ ssh lab
And so it begins. OpenSSH uses two default configuration files (/etc/ssh/ssh_config and ~/.ssh/config) but you could use any file you like and pass it to ssh through the option '-F'. The configurations options are loaded in the following order:
- Command line arguments, options passed directly to ssh when invoking it, for example ssh -Y
- ~/.ssh/config: user default configuration file, this file only applies to the current user.
- /etc/ssh/ssh_config: global file for all users and all connections; only the admin can modify it.
Once a parameter is set its value will not change, in other words, if the same option is given multiple times with different values, ssh will use the first value and ignore the rest. Having that in mind it is easy to understand that command line arguments override the settings of the personal ssh configuration file (~/.ssh/config) which in turn also override the settings provided by the global configuration file (/etc/ssh/ssh_config). The same principle applies inside each configuration file and therefore global settings should be always located at the end of the files.
The ssh configuration file is divided in entries, each entry sets the specific options for a given server and it starts by the keyword 'Host' followed by a name, that name will be the alias of the connection that you will use to invoke it. Actually, it is not a name but a pattern so you could define entries like 'Host lab*', more on this in a second. If you want to specify global options for all the connections use the '*' wildcard, that is, 'Host *'. As usual, comments are entered by ‘#’ symbol and blank lines are ignored.
The following is a simple configuration file:
Host lab HostName 10.1.1.42 User bender PubkeyAuthentication yes IdentityFile ~/.ssh/id_rsa_lab #Options applicable to all connections Host * ForwardAgent yes ForwardX11 yes
The first entry (Host lab) sets the options to be used for the server denoted as 'lab' the address is 10.1.1.42, the user name is 'bender', it should use public key authentication and the key is the file '~/.ssh/id_rsa_lab'.
The second entry (Host *) sets the parameters global to all connections, in this case: use X11 forwarding (so we can start graphical applications) and forward the ssh-agent credentials (that is for another post).
If now we want to connect to 10.1.1.42 we will simply type:
$ ssh lab
which is equivalent to
$ ssh -i ~/.ssh/id_rsa_lab -X firstname.lastname@example.org
Using patterns we can avoid duplicate options, let’s say we want to define another connection to the same server 'lab' but for a different user, this time for root. We could create a new entry having all the same fields except User or we could use a pattern that will match both defintions. For instance, we can define 'lab' for the regular user, 'lab_root' for the root and use the pattern 'lab*' for both. Remember to always put first specific options and then the more general ones.
#Options only for the regular user Host lab User bender #Options only for root Host lab_root User root #Options applicable for both, the regular user and the root Host lab* HostName 10.1.1.42 PubkeyAuthentication yes IdentityFile ~/.ssh/id_rsa_lab #Options applicable to all connections Host * ForwardAgent yes ForwardX11 yes
To connect as a root simply type:
$ ssh lab_root
Most likely, you will be prompted for a password, this is normal, you need to install your public key into the target server. And this is what starts making all this very useful: all ssh programs will use the settings of the configuration files; from now on we will be very cheap with our keystrokes (saving them for important usage like playing HHGTTG). If you don’t have a key pair yet it is time to generate the default set, to be honest, the key generation should have been the first step but that was a boring way to start a post.
Generating default RSA key pair:
$ ssh-keygen -t rsa
This will generate the following keys:
~/.ssh/id_rsa: private key, keep it safe.
~/.ssh/id_rsa.pub: public key, which you will install onto the servers you want to connect to.
Note: if you enter a passphrase instead of leaving it blank then you should make use of ssh-agent. I’ll discuss passphrases and the ssh-agent in a following post.
Since I am bit paranoid, I usually create a key pair for each server I want to log into. For that you need to specify the output file otherwise the default keys (id_rsa, id_rsa.pub) will be overwritten. For example, for the server 'lab' I will generate:
$ ssh-keygen -t rsa -f ~/.ssh/id_rsa_lab
This will generate the following keys:
~/.ssh/id_rsa_lab: private key.
~/.ssh/id_rsa_lab.pub: public key to install in 'lab'.
Continue generating as many keys as your paranoia level dictates and lets you sleep at night.
The actual key installation consist in appending the public key to the file '~/.ssh/authorized_keys' of the target system, you can do this manually (by editing the file) or faster and easier by using ssh-copy-id:
$ ssh-copy-id -i ~/.ssh/id_rsa_lab.pub lab
This is equivalent to:
$ cat ~/.ssh/id_rsa_lab.pub | ssh email@example.com 'cat >> ~/.ssh/authorized_keys'
No passwords should be requested anymore when connecting to 'lab' or copying files to/from it. Try it by creating a file and copying it:
$ touch test.txt
$ scp test.txt lab:~/
That was a very simple usage but yet it is very convenient. What else can you configure? Everything really, all the input parameters that ssh accepts can be statically set in the configuration files. To spice things up, let’s use the config file to define ssh multi-hop connections, that is to access a system through another system. Let’s take as an example my home set up.
As you can see from the above picture, in my house I have set up a PC (actually a virtual machine) named 'firewall' as the solely entry point to my network. In order to connect to the rest of systems I will always connect through 'firewall' and therefore I only have to configure my router to open a single port instead of one port per machine. (The router should be configured to use dynamic DNS and to forward all incoming traffic on port 40022 to 192.168.1.200 port 22).
If I intent to connect from the office or from a public place (e.g. a cafeteria), the entries for such a scenario will look like this:
Host firewall HostName firewall159357.dyndns.org Port 40022 User cartman PubkeyAuthentication yes IdentityFile ~/.ssh/id_rsa_firewall Host mycomputer HostName 192.168.1.201 User stewie PubkeyAuthentication yes IdentityFile ~/.ssh/id_rsa_mycomputer ProxyCommand ssh firewall nc %h %p 2>/dev/null Host pi HostName 192.168.1.203 User raspberry PubkeyAuthentication yes IdentityFile ~/.ssh/id_rsa_pi ProxyCommand ssh firewall nc %h %p 2>/dev/null
The first entry, 'Host firewall', defines how to access my LAN, it configures the connection to the machine 'firewall'; this connection will be used by the rest of the entries. 'Host mycomputer' specifies how to connect to my computer and 'Host pi' how connect to the Raspberry Pi. The key here for the ssh multi-hop is the 'ProxyCommand' where we use 'nc' (netcat) to transparently send and receive data through a ssh connection. ProxyCommand is briefly explained in the manual page of ssh_config and this article gives a very good explanation about the use of netcat and the ProxyCommand.
Installing now the public keys is a piece of cake:
$ ssh-copy-id -i ~/.ssh/id_rsa_firewall.pub firewall
$ ssh-copy-id -i ~/.ssh/id_rsa_mycomputer.pub mycomputer
$ ssh-copy-id -i ~/.ssh/id_rsa_pi.pub pi
And to connect simply:
$ ssh pi
Compare that with the cumbersome command line equivalent:
$ ssh -i ~/.ssh/id_rsa_pi -X firstname.lastname@example.org -o 'ProxyCommand ssh -i ~/.ssh/id_rsa_firewall -X email@example.com nc %h %p 2>/dev/null'
Likewise, copying files is straight forward:
$ scp localFile pi:~/remoteDirectory
$ scp pi:~/remoteDirectory/remoteFile ~/localPath/
It is clear the advantage of spending a bit of time setting your ~/.ssh/config. Consult the manual (man ssh_config) for a complete list of available options. Of course, you should memorize all the full commands to be prepared for a zombie break out or simply to show off in front of lamers picking into your screen (in this latter case do not forget to lean back and loudly exhale saying ‘FBI here I am’).
Finally, as a sweet end of post, an easy way to remove stale entries from ~/.ssh/known_hosts file. First of all, what is that file? For security reasons, the first time you connect to a server ssh will retrieve its digital fingerprint and store it in that file, afterwards every time you connect again to the same server, ssh will dynamically retrieve the server’s fingerprint and compare it against the stored value. If the values don’t match then ssh will refuse to proceed and it will abort. For a given system the digital fingerprint will always be the same, the problem arises when the same IP address refers to a different system, this is typically the case when you install a new operating system into the same machine and give the same IP address as it had before. In such an example the following will occur:
$ ssh pi @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ 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 ECDSA key sent by the remote host is aa:df:df:57:53:4f:53:c2:a4:38:bf:b7:11:8d:4b:3e. Please contact your system administrator. Add correct host key in /home/bender/.ssh/known_hosts to get rid of this message. Offending ECDSA key in /home/bender/.ssh/known_hosts:2 ECDSA host key for 192.168.1.203 has changed and you have requested strict checking. Host key verification failed.
If you are certain the new fingerprint is valid then you should remove the entry for 192.168.1.203 which corresponds to the line number 2 of known_hosts (or alternatively, if you have no security concerns, become a Rambo and delete the whole file). You can edit the file with your favorite editor and remove such a line or you can use 'sed':
$ sed -i '2d' ~/.ssh/known_hosts
I prefer to use the OpenSSH utility ssh-keygen:
$ ssh-keygen -R 192.168.1.203
Here is where we should all hail ssh-keygen obscurity! Who would think that a program named '-keygen' had the ability to remove anything? Anyway, that’s it.