Block hosts that are brute-forcing sshd by grepping and counting IP addresses for authentication failures in /var/log/auth.log. This was written for Debian, and is intended to co-exist with UFW rules.
A better solution is fail2ban, there may be others. However this is simple and light, and good enough to reduce logfile noise and to hinder the bots.
Add a new chain right at the start of the INPUT chain. This will hold our temporary block rules.
iptables -N blocked-ips
iptables -I INPUT 1 -j blocked-ips
Extract IP addresses of intrusion attempts by grepping /var/log/auth.log
for authentication failures and filtering on the number of failed attempts.
grep 'authentication failure' /var/log/auth.log|egrep -o 'rhost=[[:digit:]]+.[[:digit:]]+.[[:digit:]]+.[[:digit:]]+'|gawk -f count.awk >blocked_ips.txt
The awk script used above, count.awk
, is shown below.
BEGIN { FS="=" }
/rhost/ {
ips[$2] = ips[$2] + 1;
}
END {
for (key in ips)
if (ips[key] > 5)
print key;
}
Now block the hosts in blocked_ips.txt
using iptables
. There are more efficient ways to do this (iptables-restore), but simply iterating is fast enough (~2ms per IP on average).
for ip in $(cat blocked_ips.txt); do iptables -A blocked-ips -s $ip -j DROP; done
Remember to flush the chain prior to re-running.
iptables -F blocked-ips
A bash script to put it all together.
The following script can be used in a cron to block everything in the current auth.log file. I called it block_ips.sh. Remember to make it executable.
#!/bin/bash # Define a file to hold IPs, a regex to match IP addresses, and # the chain to hold the IP block rules. TEMP_FILE='/run/blocked_ips.txt' IP_REGEX='[[:digit:]]+.[[:digit:]]+.[[:digit:]]+.[[:digit:]]+' CHAIN='blocked-ips' # Ensure we have a chain for blocked IPs, and that it's checked first iptables -L $CHAIN -n >/dev/null 2>&1 || \ ( iptables -N $CHAIN && iptables -I INPUT 1 -j $CHAIN ) # Extract the auth failure IPs and pipe them through an awk # script to count the occurrences of each. grep 'authentication failure' /var/log/auth.log | \ egrep -o "rhost=$IP_REGEX" | \ gawk ' BEGIN { FS="=" } /rhost/ { ips[$2] = ips[$2] + 1; } END { for (key in ips) if (ips[key] >= 5) print key }' >$TEMP_FILE # Flush the chain and add rules for current IPs to block. iptables -F $CHAIN for ip in $(cat $TEMP_FILE); do iptables -A $CHAIN -s $ip -j DROP; done
There are a lot of ways to improve on this, for example including expiry times as comments, and auto-expiring.