SSH is a popular tool for remotely managing Servers and networking equipment, it’s installed and enabled by default on every single Linux/BSD-based server. Since SSH is so widely used in many environments, it has become an obvious and easy target for many threat actors. If something goes wrong with SSH, things can go wrong quickly, making it an especially valuable target.
Required Knowledge
- Linux fundamentals
- Networking fundamentals TCP/IP
Testing and backups
Before making any configuration changes to your Server, you should first test these changes in a testing environment. If all goes well, backup your production server and then apply your changes.
Table of Contents
Patch, Patch, Patch
This really should go without saying, but make sure you always install the latest security updates for any kind of software installed on your server. Patching protects your system against known security vulnerabilities. You should set up automatic patching using tools like unattended-upgrades to help stay on top on all of the latest security updates. I will write a future blog post that will cover unattended-upgrades.
Firewalling SSH
Firewalls work by filtering IP addresses, ports, and protocols, they can be a lightweight, simple and yet effective tool for preventing attacks. Most saine firewalls will block all incoming traffic by default, so you’ll need to create allow rules to allow the traffic you need. So for example if you have a HTTP server then you need to open port 80 and 443 in your firewall, port 25 for an email server, etc. The trick here is to not just open port 22 for SSH, but only allow a handful of IP addresses to access port 22 and block everybody else. Just by doing this alone, your already making it extremely difficult for anybody to even talk to your SSH server. Good luck trying to break in if you can’t even talk to SSH.
Installing UFW
I’ll be using UFW firewall as an example since it’s easy to use, but feel free to use other firewalls.
Ubuntu / Debain
sudo apt update
sudo apt install ufw
RHEL / Cent OS
sudo dnf install ufw
Creating UFW firewall rules
UFW already denies all incoming connections by default, so all you need to do is create your allow rules for SSH.
Note: To allow a range or group of IP addresses, add a CIDR notation at the end of an IP address i.e: 10.0.0.1/24
sudo ufw allow in from your-ip to any port 22 proto tcp
To allow all IP addresses to access port 22, use this command:
Warning: This leave port 22 wide open to the internet and provides no security for SSH
sudo ufw allow in from any to any port 22 proto tcp
To allow access to other services to your server, say for example your running NGINX you’ll need to create the following allow rules so others can access your website:
sudo ufw allow in from any to any port 80 proto tcp
sudo ufw allow in from any to any port 443
Once your done, double check you created all the rules you need with this command:
sudo ufw status
Enabling UFW
Once you have added all of the firewall rules you need, enable UFW with the command below.
WARNING: MAKE SURE YOU CREATED AN ALLOW RULE FOR SSH BEFORE ENABLING THE FIREWALL OR YOU’LL LOCK YOURSELF OUT.
sudo ufw enable
Secure Authentication
Weak passwords and authentication is by far one of the biggest reason any kind of server gets compromised, this isn’t helped by the fact strong and unique passwords are difficult to remember. This can be solved by using SSH keys, they’re easy to use, next to impossible to guess, and you can even login without a password! The only relatively small problem you’ll have to worry about is someone stealing your SSH key, but you can password protect the key to buy you time to change it.
NOTE: Using passwords for SSH is fine as long as you have a secure password, but SSH keys are easier to use.
Generating SSH Keys
To generate an SSH key, run this command (You can password protect the SSH key if you wish):
ssh-keygen -t ed25519
Uploading your public key to the SSH Server
Once you’ve generated your SSH key you’ll need to upload it to the server, this process is slightly different depending on what your computer’s operating system is.
Linux
On Linux this can be done with one command:
ssh-copy-id username@your-server-ip
Windows Clients
Copying SSH keys from a Windows computer is a bit tricker than Linux. First, we’ll have to manually create the SSH folder with the correct permissions on the server and then upload our key.
mkdir -p ~/.ssh
chmod 0700 ~/.ssh
Once you have created the folder and assigned it the correct permissions, use the SCP command to upload the key.
scp $env:USERPROFILE/.ssh/id_ed25519.pub username@your-server-ip:~/.ssh/authorized_keys
Logging into your Server with a SSH key
From now on you should be able to log into SSH without a password (If you didn’t set one) as long as you have your SSH key stored in your home directory’s .ssh folder.
Disabling Password Authentication
Once all user accounts use SSH keys, disable password authentication to take advantage of the security improvements that come with SSH Keys. Create a drop-in file for SSH called 10-harden.conf under /etc/ssh/sshd_config.d/.
sudo mkdir -p /etc/ssh/sshd_config.d/ /etc/ssh/ssh_config.d/
sudo nano /etc/ssh/sshd_config.d/10-harden.conf
sudo ln -s /etc/ssh/sshd_config.d/10-harden.conf /etc/ssh/ssh_config.d/10-harden.conf
Then add the following:
PasswordAuthentication no
PubkeyAuthentication yes
Restart SSH
To apply your changes, restart SSH:
sudo sshd -t && systemctl restart ssh
Brute Force Protection
A brute force attack is essentially an attack where somebody tries to guess your password, you can stop these attacks using a tool like CrowdSec. CrowdSec is a log analysis tool that protects against SSH brute force attacks by banning the offending IP address, it can detect other attacks such as DDoS, XSS, SQLi, web based scanners, and SSH exploitation attempts but for now I’ll be covering SSH.
Installing CrowdSec
CrowdSec is primarily made up of 2 components, the Security Engine (detects attacks) and the Remediation Component (blocks attacks). CrowdSec is currently not included in any official repositories, so we must first install the official CrowdSec repository, then we’ll install the CrowdSec Security Engine and Remediation Component. The Security Engine and Remediation Component is smart enough to auto-configure itself to detect and block attacks so we don’t have to do anything else. In most cases all you need to do is just run two commands.
Debian / Ubuntu
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt install crowdsec crowdsec-firewall-bouncer-iptables
RHEL / Cent OS
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.rpm.sh | sudo bash
sudo yum install crowdsec crowdsec-firewall-bouncer-iptables
Manually configuring SSHD collection (Optional)
In some cases, CrowdSec may be unable to detect your SSH log path. If this happens then you’ll have to install the SSHD Colllection then configure CrowdSec’s log acquisition. Run the below command to install the SSH collection.
sudo cscli collections install crowdsecurity/sshd
Next, you’ll have to tell CrowdSec where the SSH log file is by creating an acquisition file.
sudo mkdir -p /etc/crowdsec/acquis.d/
sudo nano /etc/crowdsec/acquis.d/ssh.yaml
An example is already provided in the CrowdSec Hub, but I’ll show it here again nevertheless. It should look something like this, just replace /path/to/ssh.log (Usually /var/log/auth.log) with your log path.
filenames:
- /path/to/ssh.log
labels:
type: syslog
---
Once your done, save and restart CrowdSec.
sudo crowdsec -t && sudo systemctl restart crowdsec
Impossible Travel and Successful brute force attacks (Optional)
In addition to brute force attacks, CrowdSec can detect “Impossible Travel” and successful brute force attacks on SSH. The installation method is similar to manually configuring SSH for CrowdSec. However, you can shoot yourself in the foot if you don’t know what you’re doing. I won’t cover how to set it up here, but if you’re interested you can see how to do so here.
CrowdSec is a very powerful tool, I recommend checking out CrowdSec’s documentation and their Academy to see what else it can do.
Reduce attack surface
Attack surface is essentially all of the ways you can be hacked. Generally, the more complex a system is, the more features it has, the more likely it is to have a security vulnerability and therefore experience a security incident. It’s impossible to have no attack surface, but you can reduce it to ensure there are no unnecessary risks. SSH already has a pretty small attack surface, but we can further reduce it by disabling insecure and unnecessary features.
Disable unnecessary features
Create an SSH drop-in file (If you haven’t already).
sudo mkdir -p /etc/ssh/sshd_config.d/ /etc/ssh/ssh_config.d/
sudo nano /etc/ssh/sshd_config.d/10-harden.conf
sudo ln -s /etc/ssh/sshd_config.d/10-harden.conf /etc/ssh/ssh_config.d/10-harden.conf
Then add the following settings.
Note: Some of these suggestions may break certain functionality within SSH, take a look at the table below to see what changes you may want to make then test it to make sure nothing is broken!
X11Forwarding no
GatewayPorts no
PermitTunnel no
AllowTcpForwarding no
AllowStreamLocalForwarding no
Protocol 2
AllowAgentForwarding no
Compression no
TCPKeepAlive no
PermitEmptyPasswords no
LoginGraceTime 1m
MaxAuthTries 1
StrictModes Yes
Configuration | Description |
---|---|
X11 Forwarding | Enable/disable GUI over SSH with X11 Server. |
GatewayPorts | Used for port forwarding over SSH |
PermitTunnel | Used for port forwarding over SSH |
AllowTcpForwarding | Used to port forward TCP ports over SSH. If you are using a database GUI program or similar to access an database on the server, then setting this to no may break it. Try setting this to local first before leaving it to a default value (yes). |
AllowStreamLocalForwarding | Used for port forwarding over SSH |
Protocol | There are two versions of SSH, V1, and V2. V2 is considered more secure and V1 is not supported. If your SSH client/server doesn’t support SSHv2 then you really should update it. |
PermitEmptyPasswords | If set to no, this will disable SSH login for user accounts without a password. |
LoginGraceTime | How long to wait for an SSH client to log in before disconnecting them. |
MaxAuthTries | How many failed login attempts are allowed before being disconnected, this can help slow down brute force attacks by acting as an rate-limit. |
StrictModes | If set to yes, an SSH user won’t log in an user if their SSH folder has insecure permissions (i.e rwx 777) |
Compression | Enable/disable compression of SSH messages. |
Limiting SSH Users
You can also limit the users and user groups that can login via SSH. This is useful to prevent SSH logins from an account that shouldn’t be accessed over SSH, typically caused by a misconfiguration.
AllowUsers exampleuser exampleuser1
AllowGroups examplegroup examplegroup1
Disabling Root Login
Sometimes people might suggest disabling root login for SSH and to use a sudo user to login instead, but this actually doesn’t have any security benefit in many cases. If you are the only one that’s maintaining the server then using the root account is fine. If multiple people are logging into the server then you should use sudo account so you can have better control over user accounts and permissions.
PermitRootLogin no
Restart SSH
Restart SSH to apply your changes.
sudo sshd -t && systemctl restart ssh