Blocking suspicious IP addresses

When you have a home in the world, people will try turning your front doorknob to see if it's open while you're away. On the cyberly internets, the equivalent is people trying to log in with guesswork. For example, there may be a few computers out there that allow access through ssh with user pi and a well-known password. This is called a dictionary attack.

As always, security is a trade-off between safety and convenience. The air gap is the most thorough safety measure, but it also means I can't access my own computer myself, so it's only appropriate in very specific scenarios.

Simple approach: hide

I found it's not a bad idea to move your incoming ssh port to something other than the standard port. This will of course not prevent people from finding it (e.g. using a port scanner), but it seems to greatly reduce the number of obviously bogus attempts my sshd has to suffer. My logs aren't flooded, I can concentrate better on the actual attempts. This is probably going to change in the future.

Never trust security by obscurity alone, but sometimes hiding does have merit.

Obvious measures

Use good passwords.

For ssh, forbid password-only authentication in sshd, require key authentication instead. Never allow root to log in directly through ssh. Consider DenyHosts or Fail2Ban.

For web access, always use SSL - since Let's Encrypt, there's no excuse anymore. Do not ignore browser warnings, the threat is real. For sensitive web sites consider client certificate authentication.

Find documentation about good security practices (e.g. here), make an effort to understand the implications, and implement measures.


After such an attack once got through, I'm a bit more paranoid these days. I thought about what happens when someone really wants to get in. Would they only knock on the front door? Or go around the back, if they found the front door locked?

What if I set up tripwires on both doors, and when one of them is triggered, I block access to all doors and windows?

On my server, I have incoming connections for apache, ssh, smtp. On apache, I also have my webmail. For the webmail, I'd like to require client certificate authentication, but that blocks my own access from a friend's home or client's site unless I carry my laptop with me.


On my Debian GNU/Linux server, I am using syslog-ng 3.5 for analyzing logfiles, and iptables 1.4.21 for blocking IPs. I'll just describe one tripwire; it's your choice to find as many indicators for possible attacks as you can find on your system. Horde is a web-based groupware framework.

IP tables rules

# block bad guys once they are in the block list, extend their ban period
$IPTABLES -A BLOCK -m recent --set --name block
$IPTABLES -A INPUT -m recent --update --name block --seconds 3600 -j DROP

# look for block criteria
# - 3 bad horde logins within 5 minutes
$IPTABLES -A INPUT -m recent --rcheck --name hordefail --seconds 300 --hitcount 3 -j BLOCK
# - 2 bad ssh logins within 1 hour
$IPTABLES -A INPUT -m recent --rcheck --name authfail --seconds 3600 --hitcount 2 -j BLOCK

Syslog-ng configuration

The configuration needs the script /usr/local/bin/ban and the pattern definition horde.xml, see below. The configuration logs all webmail login attempts to one log file, and additionally passes all failed login attempts to the iptables block list.

# /etc/syslog-ng/conf.d/horde.conf
filter f_fw_horde { program("HORDE"); };

destination d_fw_horde { file("/var/log/horde/horde.log"); };
destination d_fw_horde_fail {
    program("/usr/local/bin/ban" template("$ip hordefail\n") suppress(3));

parser p_fw_horde { db_parser(file("/etc/syslog-ng/patterndb.d/horde.xml")); };

filter f_fw_horde_fail { filter(f_fw_horde); tags("horde_auth_fail"); };

log { source(s_src); filter(f_fw_horde); destination(d_fw_horde); };
log { source(s_src); parser(p_fw_horde); filter(f_fw_horde_fail); destination(d_fw_horde_fail); };

The script:

# /usr/local/bin/ban IP TARGET
while true ; do
    read ip target
    [ -z "$ip" ] && continue
    echo +$ip > /proc/net/xt_recent/$target
    date +"%F %T $ip $target"
  } 2>&1 >> /var/log/ban.log

The pattern:

# /etc/syslog-ng/patterndb.d/horde.xml
<patterndb version='3' pub_date='2014-05-05'>
    <ruleset name='horde' id='123456678'>
            <rule provider='miracle' id='182437592347542' class='system'>
                    <pattern>[horde] FAILED LOGIN for @STRING@ to horde (@IPvANY:ip@) @ANYSTRING@</pattern>
                        <test_message program="HORDE">[horde] FAILED LOGIN for joe to horde ( [pid 26134 on line 199 of "/usr/share/horde/login.php"]</test_message>

                            <test_value name="ip"></test_value>

Here's how you use the example to test:

$ pdbtool test --validate /etc/syslog-ng/patterndb.d/horde.xml  -v
Module loaded and initialized successfully; module='syslogformat'
Module loaded and initialized successfully; module='basicfuncs'
patterndb.d/horde.xml validates
Testing message: program='HORDE' message='[horde] FAILED LOGIN for joe to horde ( [pid 26134 on line 199 of "/usr/share/horde/login.php"]'
 Match name='.classifier.rule_id', value='182437592347542', expected='182437592347542'
 Match name='ip', value='', expected=''

Documentation: syslog-ng

jan 2018-03-12