Intrusion Prevention & Fail2banLinux Security GuideLinux System AdministrationSSH & Remote Access

Top 5 Effective Best Practices to Prevent SSH Brute-Force Login Attacks on Linux Servers

SSH (Secure Shell) is the backbone for remote server management on Linux, but it also remains a prime attack vector for brute-force login attempts. In my 15+ years managing production Linux servers—ranging from Debian and Ubuntu to RHEL and CentOS—I’ve seen firsthand how unchecked brute-force attacks can cripple server availability and lead to serious security breaches. Recognizing the evolving threats, it’s crucial to implement proactive measures to secure SSH access effectively. This article will walk you through five tried-and-tested best practices to prevent SSH brute-force attacks. These strategies not only harden your server but also provide practical guardrails that have worked reliably in real-world environments. Whether you are running a single server or managing a fleet, applying these practices will significantly reduce your attack surface and protect your infrastructure from persistent malicious login attempts.

1. Disable Password Authentication and Use SSH Key-Based Authentication

A critical mistake I often see is leaving password authentication enabled on SSH servers. Passwords, especially weak or reused ones, are low-hanging fruit for brute-force bots hammering port 22 every minute. Key-based authentication leverages cryptographic key pairs, which are effectively immune to brute-force password guessing. In practice, this is the first and most impactful step you can take.

To enforce key-based login and disable password authentication, edit the SSH daemon configuration:

sudo vim /etc/ssh/sshd_config

# Typical output after restarting sshd won’t be visible but SSH will reload silently

Inside the file, update or add the following directives:

  • PasswordAuthentication no – disables password login entirely
  • PubkeyAuthentication yes – ensures public key authentication is enabled

Once saved, reload the SSH service to apply changes:

sudo systemctl reload sshd

# No output expected on success

Enabling key-based authentication improves security drastically. In my experience, this step alone stops most automated brute-force attempts cold because attackers can no longer simply guess passwords—they must compromise private keys, which is orders of magnitude harder.

2. Set a Limit on Maximum Authentication Attempts

By default, SSH permits up to 6 authentication attempts per connection, which might seem low, but can be abused by persistent attackers reconnecting repeatedly. Limiting this to 3 or fewer attempts ramps up the blocker threshold, forcing bots to be throttled or kicked off quickly. This lowers server load and reduces the window attackers have.

Modify or add the MaxAuthTries directive as follows:

sudo vim /etc/ssh/sshd_config

# After saving changes, reload sshd
sudo systemctl reload sshd

Set the value:

MaxAuthTries 3

# Confirmation: no error messages implies success

In production, I noticed that reducing this value to 3 cuts down brute-force log noise dramatically and improves overall server responsiveness. However, be mindful not to set it too low for environment where legitimate users might mistype passwords occasionally.

3. Deploy Fail2ban to Automatically Block Repeated Failed Attempts

Fail2ban is a lifesaver on busy servers exposed to the internet. It monitors authentication logs and, based on configurable thresholds, automatically bans IP addresses exhibiting suspicious behavior using firewall rules. In production clusters I manage, Fail2ban acts as a dynamic firewall pump, drastically reducing brute-force patterns with minimal manual intervention.

To install Fail2ban on Debian/Ubuntu:

sudo apt-get update && sudo apt-get install fail2ban

# Example output:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  python3-pyinotify python3-systemd
Suggested packages:
  firewalld
The following NEW packages will be installed:
  fail2ban python3-pyinotify python3-systemd
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,234 kB of archives.
After this operation, 5,678 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Fetched 1,234 kB in 1s (1,234 kB/s)
Selecting previously unselected package python3-pyinotify.
(Reading database ... 123456 files and directories currently installed.)
Preparing to unpack .../python3-pyinotify_0.9.6-1_all.deb ...
Unpacking python3-pyinotify (0.9.6-1) ...
Selecting previously unselected package python3-systemd.
Preparing to unpack .../python3-systemd_234-1_amd64.deb ...
Unpacking python3-systemd (234-1) ...
Selecting previously unselected package fail2ban.
Preparing to unpack .../fail2ban_0.11.2-1_all.deb ...
Unpacking fail2ban (0.11.2-1) ...
Setting up python3-pyinotify (0.9.6-1) ...
Setting up python3-systemd (234-1) ...
Setting up fail2ban (0.11.2-1) ...
Processing triggers for systemd (245.4-4ubuntu3.6) ...
Processing triggers for man-db (2.9.1-1) ...

Next, enable and start the service:

sudo systemctl enable --now fail2ban

# Checking status
systemctl status fail2ban

● fail2ban.service - Fail2ban Service
   Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2024-06-15 10:00:00 UTC; 15s ago
 Main PID: 1234 (fail2ban-server)
    Tasks: 3 (limit: 4915)
   Memory: 20.0M
   CGroup: /system.slice/fail2ban.service
           └─1234 /usr/bin/python3 /usr/bin/fail2ban-server -xf

You can customize the jail configuration in /etc/fail2ban/jail.local to tailor ban times and thresholds. Fail2ban’s auto-blocking gives you peace of mind, especially on servers where you cannot manually monitor attempts all day.

4. Restrict SSH Access with TCP Wrappers or Firewall Rules

Limiting which IPs can even connect to the SSH port is an effective layer to complement the above measures. TCP Wrappers uses /etc/hosts.allow and /etc/hosts.deny files to whitelist or blacklist hosts. Alternatively, firewalls like iptables/nftables or cloud security groups achieve similar network-level restriction.

Here’s a practical way using TCP Wrappers:

sudo vim /etc/hosts.deny

# Add:
ALL: ALL

This denies all connections by default. Then whitelist trusted IPs in /etc/hosts.allow:

sudo vim /etc/hosts.allow

# Allow SSH from specific IPs:
sshd: 192.168.1.100 203.0.113.25

# Deny others:
sshd: ALL: DENY

Blocking at the network access layer throws away malicious connections before the SSH daemon even receives them. However, this method requires maintaining the whitelist IPs carefully. In one environment I managed, remote teams with dynamic IPs struggled with TCP Wrappers, leading us to implement VPN or SSH bastion hosts instead for controlled access.

5. Implement Two-Factor Authentication (2FA) for SSH

Even with keys and restrictions, adding a second layer of authentication drastically reduces risk from stolen keys or insider threats. Two-Factor Authentication (2FA) with apps like Google Authenticator or hardware tokens integrates seamlessly with SSH using PAM modules.

On a RHEL or Debian-based system, installing the Google Authenticator PAM module is straightforward:

sudo apt-get install libpam-google-authenticator

# No output expected on success

After installation, each user configures their TOTP token by running:

google-authenticator

Do you want authentication tokens to be time-based (y/n) y
... Configuration output including emergency codes ...

Edit /etc/pam.d/sshd to add:

auth required pam_google_authenticator.so nullok

And in /etc/ssh/sshd_config, set:

ChallengeResponseAuthentication yes

Reload SSH after updates.

In production, I’ve seen 2FA foil brute-force and credential stuffing attacks because even if attackers get a key, they cannot bypass the time-based token prompt. Although 2FA introduces slight complexity for users, the security gains outweigh the minor inconvenience, especially on critical servers.

Conclusion

Securely managing SSH access is a foundational step in protecting Linux servers from brute-force intrusions. From disabling password authentication to implementing dynamic banning with Fail2ban, these five best practices form a layered defense that’s dynamic and robust. In my experience, neglecting any one of these opens up vulnerabilities that attackers readily exploit. Always test configuration changes during maintenance windows and communicate with your users to avoid lockouts. Your SSH infrastructure is the frontline of your Linux system security—guard it wisely with these practical measures proven over years of protecting production environments.

Leave a Reply

Your email address will not be published. Required fields are marked *