SSH fundamentals

//

steve

SSH stands for Secure Shell. As you might already know, a shell is a place where you can execute commands to interact with your system. SSH allows you to access the shell of another system remotely. Before SSH there were only protocols like Telnet which weren’t very secure or even encrypted. This meant that all the traffic could be “sniffed” meaning that all the commands sent the machine could be read. Because they weren’t encrypted it also made them vulnerable to MITM (Man In The Middle) attacks too. Telnet had many other vulnerabilties though.

There’s two things required for SSH: an SSH server which is a server running an SSH daemon and then a client which can connect to that SSH server. OpenSSH is the standard for this, it’s a suite of tools which offers both an SSH daemon called “sshd” and a client called “ssh”.

I’d like to note that SSH can do A LOT more than just provide a shell. It can do tunnellingX-11 forwarding (which allows you access graphical applications running on your server) and so so much more.

Installing an ssh server

If you’ve setup a VPS then you already have everything you need installed. Otherwise run sudo apt install openssh-server on your machine to install it. You can check if it’s running with sudo systemctl status sshd. Note that some strange VPS providers use other SSH daemons or configurations so sometimes you need to run sudo systemctl status ssh.

Don’t ask me why this is, it’s ridiculous because ssh is the client tool. I once had an issue where I had a VPS provider install another SSH server on top of the default openssh-server meaning two different servers were running. This meant that when I ran sudo systemctl restart sshd to reload my ssh server configuration file that we’ll be changing soon, nothing happened and my changes didn’t take effect. This is because although openssh-server (sshd – which I just restarted) was running they’d configured it to use this other ssh server which goes by the name of “ssh”. So for my changes to take effect I needed to run sudo systemctl restart ssh. Absolute rubbish! Don’t worry if none of that made any sense, I’m yapping!

Editing the SSHD config file

The configuration for your SSH server is held at /etc/ssh/sshd_config. I suggest you make a backup of this file before editing it so that you can restore it if necessary. To make a backup run sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak – you now have a copy of the original file with a “.bak” extension. You can put backup anywhere you like and call it anything you like but I find this rather conventional.

Changing the default port

The first thing I recommend changing is the SSH port you use. This isn’t necessarily something that should be relied on to protect you as it’s security through obscurity however if there are vulnerabilties in OpenSSH (which there have been in the past) then there’s a smaller chance automated bots will find your SSH server. This is because they traditionally look for port 22 which is the defualt SSH port. Of course they can scan for other ports on your machine and try to establish an SSH connection but this is less likely.

Another benefit is that it will keep your logs (found with journalctl -u ssh) down as they will get rather big if they’re logging automated attacks which try to authenticate with your SSH server. Fortunately they likely won’t reach your actual SSH port and so the only logs generated will generally be from your own activity which often is smaller than a hundred or so requests a day from bots. To do this change #Port 22 to Port 7845 or whatever port you choose.

Disable root login

There’s a line which says: #PermitRootLogin prohibit-password. Adjust this to PermitRootLogin no. This prevents logging into root (essentially the admin account) via SSH. Much safer! Note that it is possible to only allow specific users to be signed in via SSH but that’s out of the scope of this guide and not really necesssary.

Disable password authentication

Another crucial line to change if not the most important is: #PasswordAuthentication yes. This needs to be set to: PasswordAuthentication no. Wonderful! This is because we don’t want someone being able to just guess your password. Authenticating with keys will be much much safer. Don’t worry if you don’t understand this yet, it’ll all make sense soon.

Generate some SSH keys

At this point it would probably be a good idea to generate some SSH keys. On your client machine (the one you want to use to connect to your SSH server) run ssh-keygen set the filename to something memorable such as “myserver” and press enter. Then add a password – this encrypts your private key with symmetric encryption, ensuring that even if somebody got hold of it, they’d be unable to access your SSH server (providing the password was strong enough). It’s an extra step but isn’t necessary as long as you feel the files on your computer are secure!!! Are they!?

Type ls into the terminal and you should see two files myserver and myserver.pub (or whatever you named them). Move these to the .ssh folder in the home directory using the following command mkdir -p ~/.ssh/keys && mv myserver myserver.pub ~/.ssh/keys – this command makes the .ssh directory in the home folder if it doesn’t already exist and then moves the ssh keys to a folder called “keys” inside of the .ssh directory. Your directory structure should look like this:

.ssh
└── keys
    ├── myserver
    └── myserver.pub

2 directories, 2 files

~ means the users home directory, it’s shortcut. So cd ~/Documents would be equivalent tocd /home/bob/Documents.

Now let’s change the permission of the files to ensure they’re kept extra safe chmod 600 ~/.ssh/keys/*. This ensures only the owner of the file has read and write permission.

The myserver file is the private key (this must be kept secret and NEVER shared with anyone else). The myserver.pub key is the public key and this can be shared anywhere without consequences. We need to take the contents of myserver.pub and append them (add as a new line) to a file on our server found at ~/.ssh/authorized_keys. This file lives on the server you want to connect to and contains a list of public keys from clients who want to connect. Each line in this file is a different public key. This means I can share my public key on say GitHub and anybody can append it to their SSH server with no issues.

Feel free to add this to GitHub or a pastepin service and download it onto the server. I recommend copying the files contents as follows: cat ~/.ssh/keys/myserver.pub. Make sure to copy this exactly and create a paste here. You should have a link like this https://pastebin.com/raw/VAuQ6qta (note the raw part of the URL is rather important for the next step). Now go to your server and execute the following curl https://pastebin.com/raw/VAuQ6qta >> ~/.ssh/authorized_keys. Curl will grab the contents and then add it as a new line to the authorized_keys file (and create the file if it didn’t already exist). Note that >> means append but > means overwrite (which is only harmful if you already had keys in there).

The > greater than character means take the output of the command on the left and send it something on the right. For example echo "Hello World" > hello.txt would write “Hello World” to a new (or existing) file called “hello.txt”. If this file already had contents inside it will overwrite them. This is destructive. The double greater than symbol will write the output to a new line; therefore echo "Hello World" >> hello.txt would create the file if it didn’t exist, but if it did exist with contents then it would ensure that the new content is added as a new line.

Now it’s time to start your SSH server with sudo systemctl enable sshd --now. If it was already running then run sudo systemctl restart sshd to restart the server and allow the new configuration changes to take effect.

A note about firewalls

You’ll also need to change any firewall settings. Allow your SSH port with sudo ufw allow 7845/tcp (or whatever port you chose). Run sudo ufw status to verify it’s taken effect and your firewall is up and running. To delete the rule run sudo ufw status numbered and then sudo ufw delete NUMBER where “NUMBER” is the rule you wish to delete. If you want to establish an SSH connection outside of your home network then you’ll also need to port forward (open up the port on your routers firewall) the appropriate port.

If you already had port 22 exposed then make sure to close it back up with sudo ufw delete allow 22/tcp

If all is well you should be able to log in on your client machine with ssh -p 7845 -i ~/.ssh/keys/myserver myserverusername@myIPAddress. The -p 7845 stands for the port you wish to use (if you didn’t change it from 22 you can leave this out) and -i specifies the identity file (associated private key) required to authorize the connection.

Setting up Fail2Ban

Fail2Ban is a great piece of kit designed to mitigate brute-force attacks – where an attacker will try different combinations until they guess the right password/ key to gain access. Essentially it allows us to ban an IP address of an attacker using various rule-sets. To install it run sudo apt install fail2ban. Now the configuration file can be found at /etc/fail2ban/jail.conf however when the package is updated this will be overwritten and there it’s not recommended to edit this file directly. Instead create a copy in the same directory called jail.local. To do this run the following command: sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local. Now you can edit the file using administrator privileges. Under the “Jails” section you should find sshd. As shown below:

#
# JAILS
#

#
# SSH servers
#

[sshd]

# To use more aggressive sshd modes set filter parameter "mode" in jail.local:
# normal (default), ddos, extra or aggressive (combines all).
# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode   = normal
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

If you changed the SSH port in the previous step then adjust that here e.g. port = 7845. I recommend changing the bantime to at least an hour. To do this edit the following:

# "bantime" is the amount of time that a host is banned, integer in seconds or
# time abbreviation format (m - minutes, h - hours, d - days, w - weeks, mo - months, y - years).
# This is to consider as an initial time if bantime.increment gets enabled.
bantime  = 10m

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime  = 10m

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

You can adjust bantime to your liking e.g. bantime = 1h will set it to one hour. There’s a lot more you can do with fail2ban and formulas you can tinker with which dynamically change the ban time expondentially for repeat offenders but we’ll leave it there for now. Save the file and run sudo systemctl enable fail2ban --now or sudo systemctl restart fail2ban if you were already running it.

To test this works you can actually abuse your SSH server and see if you get banned (although I recommend setting the bantime to 1m if you’re going to do this). To test it, simply try to login to your SSH server with the wrong username and identity key (more on that later) several times.

If you try to login with root it should fail, it you try to login without a key it should fail and if you try to login with the wrong username it should fail. If you fail enough times then your IP will be banned for the specified time period. I suggest using a VPN to try logging in and banning yourself. This means you can change to a new IP which will allow you to login again.

You can see the status of bans by running sudo fail2ban-client status sshd.

Managing SSH keys

Running ssh -p 7845 -i ~/.ssh/keys/myserver myserverusername@myIPAddress can become rather tedious, especially if you need to connect to multiple machines, remember all of their identity files, individual ports etc. To mitiate this create a file in ~/.ssh/config. Inside add the following:

Host myserver
    User myserverusername
    Port 7845
    IdentityFile ~/.ssh/keys/myserver
    HostName myIPAddress

This then allows you to run ssh myserver and you’ll be able to connect to your server just like that. Note that the name “myserver” is whatever you specify as the “Host” – you can literally call it anything and it can be different from the key names or username.

To add more ssh connections edit the file as follows:

Host myserver
    User myserverusername
    Port 7845
    IdentityFile ~/.ssh/keys/myserver
    HostName myIPAddress
Host linode-vps
    User oden
    Port 5034
    IdentityFile ~/.ssh/keys/linode
    HostName 203.0.113.45

We can now connect to our VPS with ssh myserver or ssh linode-vps. Your SSH client will look for this config file so it’s important to make sure it’s in the ~/.ssh directory.

Congratulations 🥳

Well done! You have successfully (I hope) set up SSH securely and learnt to manage SSH keys. If you’d like to create an SSH connection without port forwarding or firewall rules then I suggest looking into Cloudflare tunnels or TailScale.

Additionally it’s useful to note that lots of people use Wireguard to access their servers securely but this still involves exposing a port for the wireguard server. Wireguard is a VPN. By running a VPN on your server and then connecting to it securely from your client machine you’re able to access the servers closed off local network. So then you could just run ssh 127.0.0.1. This is what VPN’s are really great for in security!

127.0.0.1 is a special IP address called a “loopback” address. It basically means “localhost” or “this computer’s IP address”.