Hardening SSH Access Against Brute-Force Attacks with Fail2ban, Iptables, and Port Obfuscation

A common threat for any publicly reachable Linux server is the relentless scanning and brute‑force attempts against the default SSH port (22). Attackers use automated tools to try thousands of username/password combinations, often exhausting system resources and potentially gaining access through weak credentials. This guide outlines a practical, layered defence combining port changes, log auditing, and automated blocking with Fail2ban and iptables.

Changing the Default SSH Port

Although not a security measure by itself, moving SSH to a non‑standard port drastically reduces the volume of automated scans and low‑effort attacks. Edit the SSH daemon configuration file:

vim /etc/ssh/sshd_config

Locate #Port 22, remove the comment character and set a custom port, for example Port 5454. Save the file and restart the service:

CentOS / RHEL:

systemctl restart sshd

Debian / Ubuntu:

service ssh restart

Verify the change with ss -tulpn | grep sshd or netstat -ntlp. From this point forward, all SSH connections must target the new port.

Auditing SSH Logs for Attack Patterns

The authentication log (/var/log/auth.log on Debian‑based systems, /var/log/secure on Red Hat) contaisn detailed records of every login attempt. Analysing these logs helps identify repeat offenders before deploying automated countermeasures.

Extracting IPs of Failed Login Attempts

Failed password entries are often written as Failed password for ... from X.X.X.X. Use grep and awk to isolate the source addresses:

grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort -u

To count how many failures each host generated, sort, count and sort numerically:

grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr

Identifying Successful Logins

A similar query reveals successful authentications, useful for verifying legitimate access or spotting compromised accounts:

grep "Accepted password" /var/log/auth.log | awk '{print $(NF-3)}' | sort -u

For a frequency list:

grep "Accepted password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr

Alternative Extraction Using sed

For environments where log line format differs, a more robust approach uses grep with Perl‑compatible regex:

grep -oP 'rhost=\K[^ ]+' /var/log/auth.log | sort | uniq -c | sort -nr

This matches the rhost= field directly and works regardless of the exact message structure.

Automated Blocking with Fail2ban and Iptables

Fail2ban monitors log files for patterns that indicate abuse and temporarily bans the offending IP using firewall rules. It relies on filters (regular expressions) and actions (iptables, firewalld, etc.). The default installation already ships with a comprehensive SSH filter.

Installation

CentOS / RHEL:

yum install -y epel-release
yum install -y fail2ban
systemctl enable --now fail2ban

Debian / Ubuntu:

apt-get update
apt-get install -y fail2ban
systemctl enable --now fail2ban

Configuring a Dedicated Jail for SSH

Do not modify the main /etc/fail2ban/jail.conf directly; instead, create a /etc/fail2ban/jail.local file that overrides the defaults. Below is a practical setup that uses iptables‑multiport to block traffic on all SSH ports after a few failed attempts.

[DEFAULT]
bantime = 600
findtime = 300
maxretry = 5
banaction = iptables-multiport
action = %(action_mwl)s

[sshd]
enabled = true
filter   = sshd
port     = 5454,22
logpath  = /var/log/auth.log
maxretry = 3
findtime = 300
bantime  = 3600
ignoreip = 127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16

Key parameters:

  • bantime: duration of the ban in seconds.
  • findtime: time window in seconds during which maxretry failures trigger a ban.
  • maxretry: number of failures before banning.
  • ignoreip: a list of CIDR networks or IPs that should never be blocked.

After editing, reload the service:

systemctl restart fail2ban

Monitoring Fail2ban Activity

Check the current bans and jail status:

fail2ban-client status sshd

Output shows the filter currently applied, the number of banned IPs and a list of those addresses. The logs are stored in /var/log/fail2ban.log and can be tailed in real time:

tail -f /var/log/fail2ban.log

Common administrative commands:

fail2ban-client set sshd unbanip 203.0.113.42    # lift ban for one IP
fail2ban-client unban --all                      # remove all bans
fail2ban-client reload                           # apply configuration changes

Adding a Low‑Interaction Honeypot on Port 22

To deceive casual attackers and gather threat intelligence, a honeypot can be placed on the now‑unused port 22. Tools like HFish (https://hfish.net) emulate SSH services, logging all interaction attempts and credentials used. This helps detect attack patterns without risking a real service. Deploying a honeypot should be done on a dedicated host or isolated container to prevent lateral movement.

By combining a non‑default SSH port, regular log analysis, and automatic iptibles bans through Fail2ban, servers can withstand the vast majority of brute‑force campaigns and remain manageable for administrators.

Tags: ssh Fail2Ban iptables brute-force Security

Posted on Sat, 04 Jul 2026 16:51:28 +0000 by remlabm