BSD DevCenter
oreilly.comSafari Books Online.Conferences.


Greylisting with PF

by Dan Langille

This article will show you how I am using PF (the Packet Filter from the OpenBSD project) and spamd (also from OpenBSD) to implement greylisting and greatly reduce incoming spam. This solution is completely MTA agnostic. You do not have to make any changes to your existing mail server configuration to use spamd/PF. In fact, you can easily use PF and spamd to guard any SMTP mail server; even MS Exchange.

I first read about PF when reading Micheal Lucas's Absolute OpenBSD. If you have never read one of his books, I urge you to do so. His writing style is very clear and easy to follow. The book contains about thirty pages on PF, yet there is so much packed into that short chapter that when I read it I knew one day I'd start using PF. That day came a few weeks ago. My gateway at home has been happily running PF since early October. And more importantly, I've been happy. One of the best features of any software product is simplicity of use. PF is simple to start and easy to extend when you need the more advanced features.

A few weeks after implementing PF on my home gateway, I attended NYCBSDCon 2006 and listened to Bob Beck's talk on spamd. It was so easy that I just had to try it. So I did.

On a related note, I'll also give you a short introduction to OS fingerprinting and how you can use PF to block/pass packets based on the sending OS.

How Greylisting Works

spamd and PF work together. PF will direct any known good clients directly to the mail server. Similarly, it will send known bad clients to the tarpit, a slow-responding mail server that does not add much load to your system. Finally, it asks new clients, not known to be good or bad, to try again later.

This makes three lists of clients:

  • Whitelist: known good clients.
  • Blacklist: known bad clients.
  • Greylist: we don't know if they are good or bad yet, but we will soon decide.

The key to greylisting is knowing that good and kind mail servers do not mind being politely asked to come back later. This is part of the STMP protocol. Spammers, however, are cheap and nasty. They don't like this. They probably won't come back later. Why? Queue management is tricky. If you're sending to millions of email addresses, why worry about a few messages that you cannot deliver? Just skip along to the next one. Spammers work on volume. Greylisting exploits that characteristic.

Implementing PF

My gateway was running FreeBSD 6.2 PRELREASE. PF is also available on DragonflyBSD, NetBSD, and OpenBSD (of course!). The FreeBSD Handbook has a good introduction to PF.

There are several ways to enable PF, including compiling it into the kernel, or loading the module. I added a few options to my /etc/rc.conf file to ensure PF starts up at boot time. It also starts the logging daemon.


Note that I have chosen a nondefault value for my PF rules. The default value, as found in /etc/default/rc.conf, is /etc/pf.conf. To avoid any merge conflicts with mergemaster(8), I chose a different filename. The default install comes with many fine examples in /etc/pf.conf and I urge you to read them.

Designing a PF rule set is beyond the scope of this article, but the OpenBSD project has a good PF rule set example.

Enabling PF

The primary interface between PF and the outside world is pfctl. To load PF on a running system, issue the command:

# kldload pf
# kldstat
Id Refs Address    Size     Name
 1    8 0xc0400000 6721fc   kernel
 2    1 0xc0a73000 58554    acpi.ko
 3    1 0xc4eb5000 16000    linux.ko
 4    1 0xc5e20000 2d000    pf.ko

What does loading PF give you? To see all the filter parameters:

# pfctl -s all
No ALTQ support in kernel
ALTQ related functions disabled

Status: Disabled                                Debug: None

Hostid: 0x595cedd1

State Table                          Total             Rate
  current entries                        0
  searches                               0            0.0/s
  inserts                                0            0.0/s
  removals                               0            0.0/s
  match                                  0            0.0/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              0            0.0/s
  proto-cksum                            0            0.0/s
  state-mismatch                         0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s

tcp.first                   120s
tcp.opening                  30s
tcp.established           86400s
tcp.closing                 900s
tcp.finwait                  45s
tcp.closed                   90s
tcp.tsdiff                   30s
udp.first                    60s
udp.single                   30s
udp.multiple                 60s
icmp.first                   20s
icmp.error                   10s
other.first                  60s
other.single                 30s
other.multiple               60s
frag                         30s
interval                     10s
adaptive.start                0 states
adaptive.end                  0 states
src.track                     0s

states     hard limit  10000
src-nodes  hard limit  10000
frags      hard limit   5000

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow

Sponsored by: