ULOG on Debian Woody

the problem

I like to use iptables. I also like to know about break-in attempts. Using the LOG target produces so much of exhibit A and its ilk in /var/log/syslog, that I can't read anything else in said file. I could use the ULOG target, but I don't know how to interface to netlink. Thankfully, there's a ulogd package. Sadly, the Woody package doesn't work (at least not with my 2.4.19 kernel, even though it was sourced from Woody). Packets were matched by my rule in iptables and sent to the ULOG target but nothing would be written to /var/log/ulogd.syslogemu. Florian suggested building ulogd from Sarge.

a solution

This is how to build your own ulogd package for Woody from the Sarge source. dpkg-buildpackage is the elegant way of building a package from source but differences between Sarge and Woody gave rise to problems that I couldn't be arsed with. This is the dirty method of building the package.

  1. Get both parts of the source from Sarge.
  2. At a shell:
    $ tar xzf ulogd_1.02.orig.tar.gz
    $ zcat ulogd_1.02-2.diff.gz | patch -p0
    $ cd ulogd-1.02
    $ ./configure --prefix=/
    $ make
    $ export DESTDIR=/tmp/ulogd_1.02-2_i386
    $ make -e install
    $ mkdir --mode 755 /tmp/ulogd_1.02-2_i386/DEBIAN
    $ cp debian/control /tmp/ulogd_1.02-2_i386/DEBIAN/
    
  3. Edit /tmp/ulogd_1.02-2_i386/DEBIAN/control so it looks like this:

    Package: ulogd
    Version: 1.02-2
    Architecture: i386
    Maintainer: Your Name <yourname@yourdomain>
    Description: The Netfilter Userspace Logging Daemon
     ulogd is a daemon that listens for Netlink packets generated by iptables's
     ULOG target. Basically, it's a replacement for syslog for logging packets,
     and does a much better job - it logs to files, mySQL, PostgreSQL and soon
     will be able to log remotely.
     .
     mySQL and PostgreSQL support is in separate packages, called ulogd-mysql
     and ulogd-pgsql respectively.
     .
     ulogd homepage: http://www.gnumonks.org/projects/ulogd
    
    (bold shows the parts that need editing)
  4. Next, say unto your shell:

    $ cd /tmp
    $ dpkg -b ulogd_1.02-2_i386
    $ sudo dpkg -i ulogd_1.02-2_i386.deb
    

This dirty package has many faults (including not stopping and starting the daemon on installation or removal) but none that cause me problems.

using ulogd

Part of the reason I want to use ULOG is to avoid writing packets to disc every minute or however often attackers choose to attack. It keeps my discs awake. The perl script reads packet logging (the output of ulogd) on stdin and summarises the day's attacks in two CSV files. A named pipe at /var/log/ulogd.syslogemu connects ulogd and the script.

#!/usr/bin/perl
#
# put netfilter packet logging on stdin, stats written daily to files
#
# maintains two maps:
#  1. (dpt,proto) -> packet_count  (for in=red,spt!=80)
#  2. src -> packet_count  (for in=red,spt-80 - reverse DDOS)
#
# $Revision: 1.2 $
#

use strict;


my $LOG_DIR = '/tmp';
my $INET_IFACE = 'red';


my $when_last;  # when the last packet was received in the form yyyy-MM-dd
my %rddos_popularity_by_host;
my %attack_popularity_by_vector;


sub sync
{

    my $today = shift or die;

    my $path_to_file = "$LOG_DIR/$today.RDDoS.csv";
    open F, ">$path_to_file" or die "$path_to_file: $!";
    for ( sort {$a <=> $b} keys %rddos_popularity_by_host )
    {
        print F "$_,$rddos_popularity_by_host{$_}\n";
    }
    close F;

    $path_to_file = "$LOG_DIR/$today.attacks.csv";
    open F, ">$path_to_file" or die "$path_to_file: $!";
    for ( sort {$a <=> $b} keys %attack_popularity_by_vector )
    {
        print F "$_,$attack_popularity_by_vector{$_}\n";
    }
    close F;
}


sub today
{

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    $year += 1900;
    $mon ++;
    $mon = "0$mon" if $mon <= 9;
    $mday = "0$mday" if $mday <= 9;
    return "$year-$mon-$mday";
}


sub categorise
{

    my $src = shift or die;
    my $proto = shift or die;
    my $spt = shift or die;
    my $dpt = shift or die;

    # determine the date today
    
my $today = today();

    # if a packet was last received yesterday..
    
if ( $when_last ne $today )
    {
        # write yesterdays data to file
        
sync( $when_last ) if $when_last;

        $when_last = $today;
    }

    # categorise the packet
    
if ( 80 == $spt )  # this attack is probably a RDDoS
    
{
        $rddos_popularity_by_host{ $src } ++;
    }
    else {
        $attack_popularity_by_vector{ "$dpt,$proto" } ++;
    }
}


while ( <> )
{
    if ( m/ .+? IN=(.*?) OUT=.* MAC=.* SRC=(.*?) DST=.*? .* PROTO=(.*?) SPT=(.*?) DPT=(.*?) / )
    {
        my ($in, $src, $proto, $spt, $dpt) = ($1, $2, $3, $4, $5);
        categorise( $src, $proto, $spt, $dpt ) if $INET_IFACE eq $in;
    }
    elsif ( m/ .+? IN=(.*?) OUT=.* MAC=.* SRC=(.*?) DST=.*? .* PROTO=(.*?) TYPE=(.*?) / )
    {
        my ($in, $src, $proto, $type) = ($1, $2, $3, $4);
        categorise( $src, $proto, 'n/a', $type ) if $INET_IFACE eq $in;
    }
    elsif ( "sync\n" eq $_ )
    {
        sync( today() );
    }
    else {
        print stderr "bad: $_";
    }
}

sync( today() );

exhibit A

Oct 26 10:27:57 earth kernel: IN=red OUT= MAC= SRC=217.136.231.238 DST= LEN=48 T
OS=0x00 PREC=0x00 TTL=118 ID=1403 DF PROTO=TCP SPT=56461 DPT=2100 WINDOW=16384 R
ES=0x00 SYN URGP=0

$Revision: 1.2 $, $Date: 2006/03/07 08:37:16 $

Valid HTML 4.01 Transitional