SSH is a very powerful tool to have if you host your own server, but if you've ever opened up port 22 to the public internet you've probably been bombarded with login attempts. The following covers common hardening strategies for Debian-based systems, though most principles apply universally across distributions.


1. Disable Root Login

The root user is the most frequently targeted account in brute force attacks. Disabling root login over SSH means an attacker needs to know both a valid username and its password — a significant hurdle.

Edit /etc/ssh/sshd_config and change:

PermitRootLogin yes

to:

PermitRootLogin no

Then restart the SSH daemon:

sudo systemctl restart sshd.service

2. SSH Keys

SSH keys are computationally impractical to brute force, so disabling password authentication in favour of key-based auth essentially eliminates that entire attack surface.

Generate a key pair on your local machine:

ssh-keygen -t rsa

Copy the public key to your server:

ssh-copy-id user@server

Verify you can log in with the key, then disable password authentication in /etc/ssh/sshd_config:

PasswordAuthentication no

Restart the daemon:

sudo systemctl restart sshd.service

3. Fail2Ban

Fail2Ban monitors log files for repeated failed login attempts and automatically bans the offending IP address for a configurable period. It's a useful defence-in-depth layer even after keys-only auth is enabled.

Install it:

sudo apt update
sudo apt install fail2ban
sudo systemctl status fail2ban

Fail2Ban ships with a default SSH jail: after 3 failed login attempts the offending IP is banned for 30 minutes. The jail configuration lives in /etc/fail2ban/jail.conf — copy it to jail.local before making changes so package updates don't overwrite your settings:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

4. OpenVPN

The most thorough option is to take SSH off the public internet entirely. Run OpenVPN on your server, then connect to the VPN before SSHing — port 22 never needs to be exposed publicly.

A minimal Docker Compose setup using the linuxserver/openvpn-as image:

version: '3'
services:
  openvpn:
    container_name: openvpn
    image: linuxserver/openvpn-as
    network_mode: bridge
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1008
      - PGID=1008
      - TZ=America/New_York
    volumes:
      - ./config:/config
    ports:
      - 943:943
      - 9443:9443
      - 1194:1194/udp
    restart: always

After the container starts, access the admin panel at https://DOCKER-HOST-IP:943/admin to configure users and network settings. Port-forward 9443 and 1194 on your router. Users can then download their .ovpn profile from the VPN portal and connect before accessing SSH through the VPN gateway.