What would we do without SSH? I must use it hundreds of times a day for all sorts of tasks. Connect and control any server anywhere in the world? Yes please! In this article I will outline a few tips and tricks I've accrued over the years to make SSH even better.
Keys are an absolute fundamental for using SSH. They mean you don't need to use passwords to login to your remote systems and are much more secure. The gold standard for many years has been the
rsa key type but more recently I've started using the
ed25519 format instead. Here are two public keys:
# id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJkjxs7qlCUL8TSA5DrZ6+RWIWPTc2N3kpFZeWMVo3WwIiyv/8q4O40/HgmvOwwPOfJpvZe2KKphNbaMik5QzOLneZW4cZlwDuXmyFKoCkwHPfANBOOY6v8NCiN/obRc2Kb22hVs0NB2qeosew7VunkLemwrdNJd8/UCFH0ZVLE3ygflQeM/Spnlbm1JRWQaRg2FgiZ3Cs4WvEwKsSdg4mDbA5U45abksR3P0QlTkpOXQnhMf9sKHvT7pSlyUub14wYPjBxhJ6qz0wrq7xvtBgce31tzRtcC2MziVeTLSWcsP7A+bM5mQGX3VnDTliAOTxdM5/qjX1Yl3H4Zm6CPN6pxLdl+zRFNwjAbgxOjoNWr8IsOnOZ0Nj7R3/wxEW16T7UF4FTPwbzShVrmSH1lhEGh4ntpqq2GGNSiJwnQI3vQKBH55I4ppND6cqJ9rDvR2CqwSEUcHiHCtUfFrMgovy7MsPLurHN9n9Q4hET8HzHZupEwtZCeYvQ4IyL8wgrVs= alex@dev # id_ed25519.pub ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK4oWAP5LyXJsqOdu0X0m1BOYqQrBIDzj/63yAdC5vP8 alex@dev
ed25519 public key makes the
rsa keys look a bit gross don't they? The caveat here is that older versions of SSH don't support
ed25519 but you probably shouldn't be exposing those old versions to the internet anyways. There's a great stackoverflow thread discussing the various merits of key types, enjoy that rabbit hole.
Generating a key is simple enough:
ssh-keygen -t ed25519
Add a passphrase if you like. Tips on passphrases can be found here.
You can view the generate keys in
id_ed25519 for the private key and
id_ed25519.pub for the public key. Guard the private key as you would a password. Do not share it, do not copy it anywhere else as it is your key to the kingdom. The public key is safe to share however, if you're interested as to why then check this video out:
It's also worth understanding
AgentForwarding too. Github have some nice documentation on this topic. It allows you to use your local SSH keys instead of leaving keys (without passphrases!) sitting on your server. In their documentation pay attention to the
ssh-agent section, it's important for all this to work.
Copying keys to the remote host can be done in a few ways. The first requires that you already have SSH connectivity to the host (perhaps with temporary password authentication).
ssh-copy-id user@host:port will copy the keys over and subsequent SSH sessions will automatically try the key first. If you temporarily enabled password authentication for SSH, disable it now.
The second method requires console access to the host itself and hand editing the file
~/.ssh/authorized_keys. Simply copy the public key from your client to a new line in this file. Watch that you don't add extra spaces or clip characters. A successful file should look something like this (this example has two public keys in it of differening types):
[alex@dev .ssh]$ cat authorized_keys ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJI+pcKMSI48VPa/Hmfn6CWIIHas7KjNI+nRU3hnBYxt4s81Ay1zYlHV7AmkcA1Vuucn7VzjrBXiDmv6QJDJu3oc27OcnCe1UUP/qyC+I9tATx4uf7/j90q4GnFiTNeVI7W3V+qIad07Fj2+XmzWIYFaUbKxEte3X9P40pecEjVsWq5P0/jTYGF+FSD/WjsRYu2384Is+7JMrPMWHgmjRH6ZYEI4O4mp9TfnxL5uQrE7qGU+zreFaT1DxwdCxxQnjGDPGNAOfOW0i+HgYxJqR2LIaEshOhJ4WgiCPWDsFbNtO1Ew/BOna/p/FYi8UHZ3idnB85GdhUzpj9MQ3qFm8ZckY7mq7WeNTN5hRrYBdbhfPjqqtZJxcuXQTnAy+i62gBoFhCt+hkdTgQuWVCrzrVabG4QGtkQgVPttlHTkReVLLov3A3qsElbOQ9wXagqCuDjwN27aWJOXhhMI4LqlHu6b9XUWoNzy0yXyqEcY+qKdwmN+oY8BaJMkQQTl8sWuU= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBGdkkT4XX52jLLcRPaOvuU719iFRfk43V0TnNJhW0W2
The third way is to use
ssh-import-id gh:ironicbadger. Unfortunately this is limited to Ubuntu / Deb systems only. It makes use of the SSH keys you store on Github. Every user exposes their keys publicly at github.com/ironicbadger.keys. Remember public keys are safe to expose.
Finally, you could use Ansible but again that requires SSH in the first place so it can be somewhat of a chicken and egg problem. This method is useful if you have rotated keys elsewhere in your infrastructure and your Ansible host remains able to access the remote host. Here's an example task:
- name: Set authorized keys taken from url authorized_key: user: alex state: present key: https://github.com/ironicbadger.keys
The Bastion Host
Given that SSH is so powerful, many bad actors target port 22 with scripts and other nastiness. For this reason it is good practice to only expose as little to the internet as possible rather than forwarding dozens of ports in your firewall to dozens of individual hosts.
This is common in enterprise environments but makes sense in most small networks where you have multiple hosts. The bastion server is your single exposed entrypoint so focus on making it as secure as possible.
Common things to look for when securing SSH are running on a non-standard port (i.e. not 22), disallow Root login, disallow password-authentication and so on. I use the Geerlingguy security Ansible role in my infra to change dozens of parameters.
Once on the bastion, you can probably reach most hosts in the rest of your LAN. This is sometimes known therefore as a jump host or island hopping. Whatever you call it, it means that now to get to a server that used to involve a single hop now requires two or more hops. We'll come onto
ProxyJump in a moment but first we need to understand the SSH config file.
We haven't talked much about the
~/.ssh/config file yet but it is extremely useful. Define multiple hosts by pet names, specify custom ports, hostnames, users, etc. Take the following command:
ssh firstname.lastname@example.org:50482 -i ~/.ssh/id_ed25519
By using SSH config we can reduce this to:
Here's the SSH config snippet needed for that:
# ~/.ssh/config Host bastion Hostname public.domain.com User alex Port 50482 IdentityFile ~/.ssh/id_ed25519
We can also glob hosts together so that rules and paramaters defined apply to multiple hosts at once:
Host * ForwardAgent yes Host 192.168.1.* User alex
And finally, it's time for the coolest thing. ProxyJump makes it super simple to jump from one host to another totally transparently. For example:
# ~/.ssh/config Host * ForwardAgent yes Host bastion Hostname public.domain.com User alex Port 50482 IdentityFile ~/.ssh/id_ed25519 Host lanserver Hostname 192.168.1.1 User alex ProxyJump bastion
In the above example when we execute
ssh lanserver we first connect to
bastion before connecting to our final destination of
In the old days we had to use
ProxyCommand with a bunch of crap to make this happen but now it's very elegant using