SSH Filtering with IPTables

From SME Server


After a recent rise in the amount of SSH attacks I decided to have a look at other methods of blocking SSH attacks.


AutoBlock is enabled by default on SME9 and later. By design only IP outside your local network will be blocked if too many attempts are done.

Default values

AutoBlockTime=900           # 900 seconds  (15 minutes). 
AutoBlockTries=4            # meaning that 3 Tries are allowed, the fourth try is blocked. 
AutoBlock=disabled          # default for SME Server 8 
AutoBlock=enabled           # default for SME Server 9

However there is no whitelist, you can easily lock you out.


DenyHosts works well:

However, it was sending me a lot of mails. Yes, I could disable them.

However, it has to check the logs and find failed logins and then create a list for ssh to check against. So it will allow at least one failed connection. It is, quite lightweight as it will update a simple plain text file called by /etc/hosts.deny on every ssh connection.

I wanted something a bit quicker that would bulk block a lot of IPs immediately.


Fail2ban works as well:

However, it needs 3 attempts and required quite a bit of processing so can be a bit cumbersome.

What I really wanted was to block some IPs outright using GeoIP blocking.

Fail2ban can do this as per this:

However, I wanted a something a bit lighter and faster and an instant block. The above link show you how to create a script that you can use with hosts/allow to block with GeoIP


There are some xtables RPMs floating about that work with GeoIP v1 DBs but not sure about v2 DBs. Needs investigation

07/02/20109 - These are in the process of being imported. They will work with GeoIP2.

smeserver-xt_geoip xtables-addons

They should be in smetest shortly.


This approach is very brute force and ignorance. You are highly likely to lock yourself out, so be prepared. Preferably keep an extra terminal open and logged in as a backup.

Make sure other SSH blocking features like denyhosts etc are disabled

mkdir -p /etc/e-smith/templates-custom/etc/hosts.allow
cp /etc/e-smith/templates/etc/hosts.allow/sshd /etc/e-smith/templates-custom/etc/hosts.allow

Open the custom template with your favourite editor.

Remove any other lines and then add this line where a.b.c.d is the IP

sshd: a.b.c.d: allow

You can add more than one address, and subnets too - there is plenty of information online about this.

sshd: a.b.c.d w.x.y.: allow

The only down side is it leaves a lot of mess in your messages log and so far I can't find out how to shift the messages elsewhere.

It is very effective though.

SSH Filter with GeoIP blocking

Another approach is one I found here originally:

However, CentOS does not use aclexec.

I looked for a replacement and found this site, and a relevant comment below

"For all CentOS users, spawn or aclexec does not work, the hint is already given by using iptables to block the user. The iptables command given appends (-A) so the connection might still go through, to really block the IP you have to insert (-I) the block rule at rule #1. You can use my altered script for a working CentOS/RHEL version:"

So I grabbed a copy of the script but found I had to do a little work for it to run with SME.


Here is how to install the geoip blocking script.


OK, running GeoIP2 databases is a prerequisite. Please see smeserver-geoip2 here

Make sure you disable denyhosts so it doesn't interfere with this script in hosts.allow


Make sure you can get results with the geoiplookup tool

Get the main script:

wget -O /usr/local/bin/
chmod 0755 /usr/local/bin/

Edit the file with your favourite editor.

Add the countries you want to ALLOW in:


They are currently set to GB ES FR but you can use any country code/s.

Create a masq iptables fragment to handle the blocks

mkdir -p /etc/e-smith/templates-custom/etc/rc.d/init.d/masq/
touch /etc/e-smith/templates-custom/etc/rc.d/init.d/masq/40sshFilter

Add this:

   # A blacklist chain for sshFilter
   /sbin/iptables --new-chain BLOCKDYN
   /sbin/iptables -A INPUT -j BLOCKDYN

Create a hosts.allow custom fragment as above with the following contents:

sshd: ALL : spawn /usr/local/bin/ %a %d

Now we can expand the templates and restart the masq service:

expand-template /etc/rc.d/init.d/masq
expand-template /etc/hosts.allow
service masq restart

Now you can look at iptables to see your handiwork

iptables -L BLOCKDYN


Testing - please see the comments in the script for how to test.

/usr/local/bin/ ssh DE BLOCKDYN
echo "" | /usr/local/bin/ ssh DE BLOCKDYN



All the logging goes to /var/log/secure. Errors should really go elsewhere. Needs some thought. See my comments:

# This will log to /var/log/secure
# This should go to /var/log/messages but doesn't. Need to figure that out


The table can get big quickly.

It may be worth having running an iptables flush from cron periodically

You can do it manually

iptables -F BLOCKDYN

It may be worth looking at adding a specific AllowHosts section in the chain, or somewhere in masq to Allow Specific hosts, but block the rest of a country.