Postfix header_checks using tcp_table and checkdbl.pl script

Postfix implements the header_checks as built-in content inspection classes while receiving mail. Usually the best performance is obtained with pcre (Perl Compatible Regular Expression) tables or slower regexp (POSIX regular expressions). Googling on the net, i’ve found tiny perl script that can queries to dbl.spamhaus.org, multi.surbl.org, black.uribl.com. ( Sahil Tandon wrote it, based on  João Gouveia perl script, i think..)

first download the script

# cd /etc/postfix
# wget http://people.freebsd.org/~sahil/scripts/checkdbl.pl.txt

Rename and make it executable

# mv checkdbl.pl.txt checkdbl.pl
# chmod 755 checkdbl.pl

Edit master.cf add this two lines

127.0.0.1:2526 inet  n       n       n       -       0      spawn
user=nobody argv=/etc/postfix/checkdbl.pl

Make preliminary test, to ensure checkdb.pl sih really spawned and answering our queries

# postfix reload
# telnet 127.0.0.1 2526


Let’s try with Message-ID header (it is real spam in my inbox hehehe)

Trying 127.0.0.1...
Connected to mx.example.com (127.0.0.1).
Escape character is '^]'.
get Message-ID: <243589555829862551ca6a14e9bf5c6b@vldu204.eusensyv.info>
200 REJECT

Reply-To header

Trying 127.0.0.1...
Connected to mx.example.com (127.0.0.1).
Escape character is '^]'.
get reply-to: <software_innovations5@buyinhe.com>
200 REJECT

From header

Trying 127.0.0.1...
Connected to mx.example.com (127.0.0.1).
Escape character is '^]'.
get from: <software_innovations5@buyinhe.com>
200 REJECT

Or we can query by using postmap tool

# postmap -q "from: <software_innovations5@buyinhe.com>" tcp:127.0.0.1:2526
REJECT buyinhe.com, which appears in the 'from' header, is listed on black.uribl.com

We’ve seen that checkdbl.pl realy work as expected, now it’s time to make it realy working in real life.
put this two lines in main.cf

127.0.0.1:2526_time_limit = 3600s
header_checks = tcp:[127.0.0.1]:2526

Reload postfix

# postfix reload

And these are real rejected spam logs made by postfix and checkdbl.pl

Month date 15:15:35 mx.example.com postfix/smtpd[24907]: 152CB30012A: client=unknown[69.162.108.69]
Month date 15:15:35 mx.example.com postfix/cleanup[28392]: 152CB30012A: reject: header Message-ID: <4507031@creditreports.tampocopica.com> from unknown[69.162.108.69]; from=<Nancy@tampocopica.com> to=<example-user@example.com> proto=ESMTP helo=<creditreports.tampocopica.com>: 5.7.1 creditreports.tampocopica.com, which appears in the 'Message-ID' header, is listed on dbl.spamhaus.org
Month date 15:15:35 mx.example.com postfix/cleanup[28392]: 152CB30012A: message-id=<4507031@creditreports.tampocopica.com>
Month date 15:50:04 mx.example.com postfix/smtpd[29412]: 7837130012F: client=unknown[66.90.109.40]
Month date 15:50:05 mx.example.com postfix/cleanup[31734]: 7837130012F: reject: header From: "Personalized-Christmas-Ornaments" <Alicia@diaseven.info> from unknown[66.90.109.40]; from=<Alicia@diaseven.info> to=<example-user@example.com> proto=ESMTP helo=<iyio40.diaseven.info>: 5.7.1 diaseven.info, which appears in the 'From' header, is listed on dbl.spamhaus.org
Month date 14:15:28 mx.example.com checkdbl[22069]: Hit: vldu204.eusensyv.info on dbl.spamhaus.org
Month date 14:17:53 mx.example.com checkdbl[22360]: Hit: buyinhe.com on black.uribl.com

Yes, they are real spammer and rejected. sweet…

5 Comments

  1. Pfixler

    main.cf entry:
    header_checks = tcp:[127.0.0.1]:2526

    spits:

    Dec 5 16:30:53 inet postfix/cleanup[9849]: fatal: unsupported dictionary type: tcp
    any idea?

  2. Pfixler

    switched on brain and:

    postconf -m
    btree
    cidr
    environ
    hash
    ldap
    mysql
    nis
    pcre
    proxy
    regexp
    static
    unix

    so I don’t have tcp table support enabled/compiled.
    Looking for more info, it seems this is “experimental”

  3. Roberto

    I have recently run into a situation where I have an address/domain that I need to white list so the email is not rejected. Suggestions on how I can do this ?

    Many thanks !
    -Roberto

    • add and exception on main loop, say, you have one domain to whitelist, set a variable $mydomain and slightly modify the loop.
      this is not tested, but i hope you get the picture.

      while (<>) {
              chomp;
              my $mydomain = "example.com";
      
              if (/^get\s+(?:resent-)?([\w-]+)\s*:\s*(.+)$/i) {
                      my ($hdr, $data) = ($1, $2);
                      unless(grep(/^$hdr/i, @headers)) {
                              print "200 DUNNO\n";
                              next;
                      }
                      my @res = querybl($data);
                      if (@res && $res[0] != $mydomain) {
                              print "200 REJECT $res[0], which appears in the '$hdr' header, is listed on $res[1]\n";
                              next;
                      }
              }
              print "200 DUNNO\n";
      }
      

Leave a Reply

Your email address will not be published. Required fields are marked *