How to unban IP locked out by aaPanel Fail2ban via SSH terminal commands

Root Cause Diagnosis

Fail2ban tails the SSH auth log (/var/log/auth.log on Debian/Ubuntu, /var/log/secure on RHEL-family). When one IP exceeds maxretry failures within findtime, it inserts a firewall rule (iptables, nftables, ipset, or firewalld) that drops that IP for bantime. Bans are also written to the Fail2ban SQLite database and reinstated on service restart, so restarting the service does not clear them. If you are locked out of SSH, you need a session from a non-banned IP or the provider’s out-of-band console (VNC, KVM, serial) to run the unban commands.

Quick Fix

From the provider console or a different IP:

fail2ban-client status                          # list jails
fail2ban-client unban 203.0.113.50              # remove from all jails (Fail2ban 0.10+)
fail2ban-client set sshd unbanip 203.0.113.50   # per-jail form (also works on 0.9)

To prevent an immediate re-ban, whitelist the IP in the same session (Step 4).

If fail2ban-client is not in PATH, the aaPanel plugin installs it under a custom prefix. Locate it first:

command -v fail2ban-client || find / -name fail2ban-client -type f 2>/dev/null

Step-by-Step Resolution

1. Regain a shell

  • Use the provider’s web console (VNC/KVM/serial). It bypasses the firewall entirely.
  • Or connect from another IP: a mobile hotspot, VPN, or a jump host that is not banned.
  • If the aaPanel web panel is still reachable, use its built-in terminal, since the ban usually covers only the SSH port.

2. Identify the jail and confirm the ban

fail2ban-client status
fail2ban-client status sshd

Sample output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  `- Total failed:     14
`- Actions
   |- Currently banned: 1
   `- Banned IP list:   203.0.113.50

The jail name may differ (ssh, sshd, ssh-iptables), so use the name from the first command. To search all jails for the IP:

for j in $(fail2ban-client status | sed -n 's/.*Jail list:\s*//p' | tr -d ','); do
  fail2ban-client status "$j" | grep -q 203.0.113.50 && echo "banned in: $j"
done

Confirm from the log (find the path first, since the aaPanel plugin may not use the default):

fail2ban-client get logtarget
grep "203.0.113.50" /var/log/fail2ban.log | tail

3. Unban the IP

# All jails at once (0.10+)
fail2ban-client unban 203.0.113.50

# One jail (any version)
fail2ban-client set sshd unbanip 203.0.113.50

# Everything
fail2ban-client unban --all

If the plugin runs a non-default socket, pass it explicitly:

fail2ban-client -s /path/to/fail2ban.sock unban 203.0.113.50

Also check the recidive jail. It applies a much longer ban to repeat offenders, independent of sshd:

fail2ban-client status recidive
fail2ban-client set recidive unbanip 203.0.113.50

4. Whitelist the IP so it is not re-banned

Edit the jail file (/etc/fail2ban/jail.local on package installs, or the aaPanel plugin’s config directory, often under /www/server/fail2ban/). Do not edit jail.conf, since package updates overwrite it.

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.50 198.51.100.0/24

Apply without restarting:

fail2ban-client reload
fail2ban-client get sshd ignoreip

You can also set it at runtime, though it is lost on restart:

fail2ban-client set sshd addignoreip 203.0.113.50

5. Verify the firewall rule is gone

# iptables backend
iptables -S | grep -E 'f2b|fail2ban'
iptables -L f2b-sshd -n --line-numbers

# nftables backend
nft list ruleset | grep -iE 'f2b|203.0.113.50'

# ipset backend
ipset list | grep -A20 f2b

The IP should not appear. Then test from the affected machine:

ssh -v user@SERVER_IP -p SSH_PORT

6. Manual firewall removal (fallback)

Use this only if fail2ban-client cannot reach the daemon. Prefer the client, because manual edits leave Fail2ban’s internal state out of sync.

# iptables: delete by rule number
iptables -L f2b-sshd -n --line-numbers
iptables -D f2b-sshd 1

# ipset
ipset del f2b-sshd 203.0.113.50

# firewalld rich rule
firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="203.0.113.50" reject'
firewall-cmd --reload

7. Stop a restart from reinstating the ban

Fail2ban restores unexpired bans from its database on startup. If you must restart, stop first, clear the row, then start:

systemctl stop fail2ban
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "DELETE FROM bans WHERE ip='203.0.113.50';"
systemctl start fail2ban

Confirm the database path with fail2ban-client get dbfile. Stopping the service also removes its firewall chains on most action types, which lifts every ban until restart.

See also  Resolving MySQL Error 1040 "Too Many Connections" on a 1GB VPS with aaPanel

Why This Fails for Some Users

  • The block is not Fail2ban:fail2ban-client status shows nothing banned, yet SSH is still refused. Check in order:
    • aaPanel’s own firewall (Security page): remove any drop rule for the IP or SSH port.
    • Cloud security group or provider firewall.
    • /etc/hosts.deny and DenyUsers/AllowUsers/AllowGroups in /etc/ssh/sshd_config.
    • CSF/LFD if installed alongside (csf -g 203.0.113.50, csf -dr 203.0.113.50).
    • MaxStartups throttling in sshd_config, which returns connection resets, not a hard ban.
  • Failed to access socket path: the daemon is down or uses a non-default socket. Run systemctl status fail2ban, or ps aux | grep fail2ban to find the process and its -s argument. With the daemon stopped, the ban rules usually no longer exist, so the block is elsewhere.
  • Fail2ban older than 0.10 (CentOS 7, Ubuntu 16.04): the global unban subcommand does not exist. Use set <jail> unbanip. These versions also run on Python 2, and --all is unavailable.
  • Wrong jail name: Sorry but the jail 'sshd' does not exist means the plugin named it differently. Use the exact name from fail2ban-client status.
  • SSH port changed: the ban covers whichever port the jail was configured with. If you changed the SSH port in /etc/ssh/sshd_config but not the jail’s port setting, the ban still applies to the old port, and connections to the new port may be blocked by a separate firewall rule instead.
  • Shared, NAT, or CGNAT source IP: the ban may target an address shared by other users, so one failed-login burst locks everyone behind it. Whitelist the CIDR range, not a single address, and confirm your current public IP with curl -s ifconfig.me from the affected client.
  • IPv6: if the client connects over IPv6, the ban is on the IPv6 address (or its /64 with some actions), and unbanning the IPv4 address has no effect. List bans with fail2ban-client status sshd and unban the exact string shown.
  • Backend mismatch: on systems using backend = systemd, ban timing and logs come from the journal (journalctl -u ssh or -u sshd), not from auth.log. If neither exists, the jail never triggers.
  • Docker or NAT-forwarded services: container traffic traverses the FORWARD chain, and a INPUT-only ban will not block it. In these setups, check the DOCKER-USER chain instead of f2b-sshd.
  • Ban persists after unban: a bantime.increment = true setting with a large bantime.factor makes re-bans escalate quickly. Add ignoreip, raise maxretry, and use key-based login to stop repeat failures.
See also  Fixing LiteSpeed OpenLiteSpeed Cache Crawler 403 Forbidden Loop

Leave a Comment