lockout

What it is

As everyone knows, or should know, firewalls are access control locks for your computer. Let in the good, keep out the bad. Unfortunately, static (unchanging) firewalls only block (or more correctly, allow in) specific data. For example, say user A is trying to attack something you have blocked – no problem, right? That's the point of the firewall. Now let's say the same user A is trying to use a service you provide, the web server for example. Chances are he's trying to break into your computer through the web server. Not so good. Hence, lockout.

lockout monitors system logs looking for possible attacks. When found, the offending IP address is blocked. The logs are parsed using extended regular expression style rules defined in the configuration, so the possibilities are endless. Unlike other systems that run as cron jobs (minimum latency – one minute) lockout monitors the logs continuously via a named pipe, so attacks can be thwarted very quickly.

The flow for lockout is simple: the first time a rule matches, the offending address is put on a `pending' list and not blocked. If the same address matches any rule a configured number of times, it is moved to the `blocked' list and a firewall block is placed. It will not be removed until a configured amount of time has elapsed. If the address is on the pending list long enough (not moved to the blocked list) it will be expired.

Blocking the first bad packet is not a good idea – imagine if the rule is checking for failed authentication instead of rogue packets. In this case if you mistype your password once, you're locked out!

The packaged rules and examples catch the three problems I see most, but as I said above lockout is fully configurable, so just about any rule can be defined. The three problems I see:

  • trying to access a forbidden port

  • bad ssh authentication

  • invalid password

Usage



lockout [-v] -c config



-v : verbose logging

-c config : point to the configuration file (see below)



In addition, lockout responds to two signals:

USR1 – dump the contents of the pending and locked lists to the log file.

HUP – close the log file and reopen (useful for logrotate)

Building

lockout is written in ANSI C, using the minimum number of unix'isms. Simply untar the source and type `make'

Configuring

lockout uses helper programs, defined in the configuration file, to do the actual work of setting up the firewall. By default these scripts use the `iptables'' command, so that configuration will be described here. If you are using a different firewall technique, you'll need to read the documentation for that system.

There are three parts to the configuration: setting up the firewall, setting up syslog, and configuring lockout.

Setting up iptables

The assumption is you've a firewall setup, and any packet not explicitly allowed by the firewall is considered rogue, a possible attack, and should be stopped. This isn't meant to be an in depth tutorial about firewall configuration! Find that elsewhere. You'll first need to create a new chain, ``offenders'':

iptables -n offenders

Once done, add it to your INPUT chain. You want it to be the first rule.

iptables -I INPUT 1 offenders

You also need to add a LOG entry. This is what catches rogue packets. It should be inserted directly above the last entry, which should be DROP.

When complete, your INPUT chain should look something like:

Chain INPUT (policy ACCEPT)

target prot opt source destination

offenders 0 -- anywhere anywhere

fwallin 0 -- anywhere anywhere

LOG 0 -- anywhere anywhere LOG level warning

DROP 0 -- anywhere anywhere

A few things to note:

offenders is the rule that lockout populates. It should be first to filter out known offenders quickly. Another alternative is to put it between LOG and DROP. If you put it first you'll not notice if an attack is continuing because it has been blocked. That means at some time in the future the address will be unblocked even though it is the source of an attack. If you put it after LOG it will remain blocked until such time that it stops the attack. This uses more CPU and I've not ever noticed a problem.

fwallin should be whatever fire wall rules you have defined. At the end of this document there is an example.

LOG is built-in. Any packets that make it to LOG are suspect because they got past fwallin. This will make the kernel send all of the packets to the system log.

DROP is also built-in. If you've a working firewall you likely already have this as your last input rule.

Setting up syslog

lockout works by reading from a named pipe and parsing the results. syslogd must be setup to send information to this pipe. Start by making a directory for lockout. I put it in /var/lib/lockout. Once there, create the named pipe (the default is lockout.pipe, but it can be anything you want). The command is mkfifo.

Next you'll need to add the following line to /etc/syslog.conf:

#

# used by lockout

#

kern.*;auth.* |/var/lib/lockout/lockout.pipe

This simply means send anything from the kernel or authentication logs to /var/lib/lockout/lockout.pipe.

Remember, after doing this you'll need to restart syslogd and, as I've discovered, klogd. Under debian this done with:

/etc/init.d/sysklogd restart

/set/init.d/klogd restart

Configuring lockout

The included lockout configuration file is completely documented, and more likely to be up to date than this, but below is a description of its contents. As is typical for configuration files, blank lines are ignored, and a '#' denotes the beginning of a comment which lasts to the end of the line. The maximum length of a line is 1023 bytes. String values must be enclosed in double quotes.

Value

Default

Explanation

FLUSH

``./iptables_flush.sh''

Command to execute when lockout starts which will flush all lockout addresses from the firewall.

BLOCK

``./iptables_block.sh''

Command to execute to block an address. A list of IP addresses is passed as parameters.

UNBLOCK

``./iptables_unblock.sh''

Command to execute to unblock an address. A list of IP addresses is passed as parameters.

ADDR_MAX

512

Maximum number of addresses to pass to BLOCK and UNBLOCK

STATE

``/var/lib/lockout/state''

Holds the current list of blocked addresses in the event lockout must be restarted.

LOG

``/var/log/lockout.log''

A simple log so you can see what's happening.

PIPE

``/var/lib/lockout/lockout.pipe''

The location of the named pipe created above.

IGNORE

None

Some addresses should never be blocked. For example, your default gateway. Any address listed here will never be blocked. Only one address per IGNORE line.

lockout_MIN

259200

The minimum number of seconds an address will be blocked. This default is three days.

lockout_JITTER

172800

An address will be blocked between lockout_MIN and lockout_MIN + lockout_JITTER seconds. This prevents someone from attacking you & knowing he'll be free to do so again in a given amount of time.

PENDING_MAX

300

The maximum number of seconds a possible offender stays on the pending list.

EXPIRE_FREQUENCY

60

How often to run the expire routine.

RULE

None

See below.



The rules are defined, one per line, as follows:

RULE “title:count:regular expression”

title is used to define which rule a particular address hit.
count tells lockout how many times a rule must be hit before the address moves from the pending list to the blocked list.

regular expression is just that. It is assumed that when the offending IP address or host name will be the first thing not matched by the regular expression. Here's an example:

RULE “firewall:3:^\w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: IN=eth[[:digit:]]+ OUT= MAC=[0-9:a-fA-F]{41} SRC=”

The title is, `firewall.' It must hit at least three times before the offender is blocked.

Sample lockout Configuration File

# Commands
FLUSH    “./iptables_flush.sh”
BLOCK    “./iptables_block.sh”
UNBLOCK  “./iptables_unblock.sh”
ADDR_MAX 512

# Various file locations.
STATE “/var/lib/lockout/state”
LOG   “/var/log/lockout.log”
PIPE  “/var/lib/lockout/lockout.pipe”

# One or more IGNORE clauses can be used to ignore certain
# addresses. You don't ever want to block your default gateway.
# this can be either an address or a host name
IGNORE 10.10.10.1  # default gateway
IGNORE 10.10.10.2  # me
#
# a lockout will last between lockout_MIN and lockout_MIN + lockout_JITTER
# seconds. The default is 3 - 5 days.
#
lockout_MIN    259200
lockout_JITTER 172800
#
# When an address is first seen, it goes onto the pending list. If it is
# not seen again within PENDING_MAX seconds it is removed.
#
PENDING_MAX 300
#
# determine how often to run the address expire routine
# (# of seconds between iterations)
#
EXPIRE_FREQUENCY 60
#
# rules are simply extended regular expressions. The format is:
# RULE=name:ct:expr
#   name -- rule name for logging which rule was matched
#   ct   -- number of times an offender must be seen before
#           moving from the pending list to the blocked list
#   expr -- goes to the end of the line
# nb: line length is limited to 1023 characters
#
# match iptables -- catches any packet not allowed by the firewall
#
RULE “firewall:3:^\w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: IN=eth[[:digit:]]+ OU
T= MAC=[0-9:a-fA-F]{41} SRC=”
#
# match authentication failures
#
RULE “auth:3:^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sshd\[[0-9]+\]: \(pam_unix\) auth
entication failure; .* rhost=”
#
# match bad password (probably redundant as this should be caught above)
#
RULE “badpass:3:^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sshd\[[0-9]+\]: Failed passwor
d for .* from[ ]*”

Notes and Bugs

A downside to this technique is if many computers are sharing a single address (common for home and small businesses), if one machine is sending bad packets none of the other machines on that address will be able to get through to your services. I have not found this to be an issue in practice. I also believe protecting my computer is paramount.

As of this writing there are no known bugs.

The following will be seen in the log always:

MMM dd hh:mm:ss Added xxx.yyy.zzz.www to pending (matched rulename)
MMM dd hh:mm:ss Expired xxx.yyy.zzz.www from list after DD hh:mm:ss
MMM dd hh:mm:ss Moved xxx.yyy.zzz.www to blocked (matched rulename)



These will only be seen if “-v” is specified:

MMM dd hh:mm:ss Pending xxx.yyy.zzz.www (matched rulename)
MMM dd hh:mm:ss Blocked xxx.yyy.zzz.www (matched rulename)

Finally, these will be seen when the USR1 signal is caught (ct is the number of times an address has been seen, the time is the time since last seen):

MMM dd hh:mm Pending (nn entries)
MMM dd hh:mm       xxx.yyy.zzz.www   ct days hh:mm:ss
...
MMMM dd hh:mm Blocked (nn entries)
MMM dd hh:mm       xxx.yyy.zzz.www   ct days hh:mm:ss
...

Sample firewall rules

This is the shell script I use to setup my iptables

#!/bin/sh
#
# set the default input to DENY (nothing gets in)
# flush all chains
# delete the fwallin chain
#
iptables -P INPUT ACCEPT
iptables -F
iptables -X fwallin
#
# the firewall chain will be called `fwallin'
#
iptables -N fwallin
iptables -N offenders
#
# allow anything from/to the loopback
#
iptables -A fwallin -i lo -j ACCEPT
#
#
# allow TCP connections & transactions to ports 22, 25, 53, 80
# (ssh, email, dns, www)
#
iptables -A fwallin -p TCP -d 0/0 --destination-port 22 -j ACCEPT
iptables -A fwallin -p TCP -d 0/0 --destination-port 25 -j ACCEPT
iptables -A fwallin -p TCP -d 0/0 --destination-port 53 -j ACCEPT
iptables -A fwallin -p TCP -d 0/0 --destination-port 80 -j ACCEPT

#
# allow any established TCP connections, but don't allow
# packets with only the SYN bit set
#
iptables -A fwallin -p TCP -d 0/0 --destination-port 0:65535 ! --syn -j ACCEPT
#
# allow UDP to port 53 (dns)
#
iptables -A fwallin -p udp -d 0/0 --destination-port 53 -j ACCEPT
iptables -A fwallin -p udp -s 0/0 --source-port 53 -j ACCEPT
#
# allow UDP to/from port 123 (ntp)
#
iptables -A fwallin -p udp -d 0/0 --destination-port 123 -j ACCEPT
iptables -A fwallin -p udp -s 0/0 --source-port 123 -j ACCEPT
#
# allow all ICMP messages
#
iptables -A fwallin -p icmp -j ACCEPT
#
# deny everything else
#
iptables -A INPUT -j offenders
iptables -A INPUT -j fwallin
iptables -A INPUT -j LOG
iptables -A INPUT -j DROP