Well, why can't I just configure fail2ban to block hosts that are hitting me with portscans? Why not, indeed!
fail2ban has a fairly simple format for filters. You need a [Definition] block, which consists of a chunk of line separated regexes for the error you are looking for, and potentially a chunk of line separated regexes for errors you'd like to ignore. This all goes into a filter file, which usually lives in /etc/fail2ban/filters.d.
My anti-portscan filter, creatively called "portscan.conf," is as follows:
# Option: failregex # Notes: Looks for attempts on ports not open in your firewall. Expects the # iptables logging utility to be used. Add the following to your iptables # config, as the last item before you DROP or REJECT: # -A <chain_name> -j LOG --log-prefix "PORT DENIED: " --log-level 5 --log-ip-options --log-tcp-options --log-tcp-sequence # This will place a notice in /var/log/messages about any attempt on a port that isn't open. failregex = PORT DENIED: .* SRC=<HOST>
This is pretty simple, the regex is just this: PORT DENIED: .* SRC=<HOST>
The <HOST> item indicates where the IP address of the attacker is expected.
In order to get these error messages, you need to take advantage of the iptables logging utility. iptables has the ability to log any connections, successful or not. Which connections it logs depends on where in the rules chain the LOG item is placed. If you were a lunatic, I'm sure you could place it first and log each and every connection that your system makes. Don't do this. It's dumb. For our purposes, we want a LOG item placed immediately before we finally DROP or REJECT a connection. All rules regarding opening a hole for a service should go PRIOR to the LOG entry. An example of a LOG item is:
-A <chain_name> -j LOG --log-prefix "PORT DENIED: " --log-level 5 --log-ip-options --log-tcp-options --log-tcp-sequence
where <chain_name> is the name of whatever chain you currently are in. The log-level and other options are mostly insignificant for the purposes of our fail2ban filter, but the log-prefix is meaningful. If you change the log prefix in the iptables config, make sure you update the regex in your filter to match.
The logging utility will then log any DROPped or REJECTed connection to /var/log messages.
Once we have our filter created, and our failed connections being logged, we can update the fail2ban config to make use of them.
Edit /etc/fail2ban/jail.conf (or, jail.local if you're on Ubuntu or an OS that prefers you to not use jail.conf) with the following:
[portscan] enabled = true filter = portscan action = iptables[name=portscan] logpath = /var/log/messages maxretry = 3
You can just append it to the end of the file. This names the jail, enables it, tells it which filter to use and how to block it, and which log to parse. I have it set to fail after 3 attempts, you can customize that to suit your preferences.
Once iptables and fail2ban have been restarted to take advantage of the new configs, a server that gets hit with portscans regularly should start blocking connections. Note that many residential ISPs sniff out and shut down portscans, themselves, so you may not see this sort of traffic at home.
Apparently this doesn't work out of the box on Ubuntu. I don't have an install handy, so I'm not sure what the differences are. The definition should still work, you just might have to do some other finagling.