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:
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
|