Spam filtering with Exim

This page contains tips and tricks on how to filter spam using the Exim MTA.

The stuff below is based on Debian 5.0 / Lenny.
Select the following packages and anything that they depend on;

Plus any documentation you might want to install and then do a lot of RTFM.

After this configure the lot;
Replace 'sput.nl' with your own domain, '*.sput.nl' with your own hostname. Replace '80.101.95.251' with your own IPv4 address. Replace '2001:888:1533:' and '2001:888:10:533::2' with your own IPv6 addresses. Replace 'RvdP' with your own initials.
Sometimes dots ('.') in IPv4 addresses are escaped with a backslash ('\.') and the colons (':') in IPv6 addresses with a double colon ('::').

Exim configuration

Main

main/000_localmacros

I created my own config file; '/etc/exim4/conf.d/main/000_localmacros'.

# RvdP, this a local config file

# Enable checks; 1 = True = Enable
# ================================
MAIN_TLS_ENABLE = 1
# Avoid more trouble;
MAIN_TLS_TRY_VERIFY_HOSTS = !*
CHECK_MAIL_HELO_ISSUED = 1
# This is not a callout verification;
# Callout is in /etc/exim4/local_check_rcpt
#CHECK_RCPT_VERIFY_SENDER = 1
# Warn;
CHECK_RCPT_REVERSE_DNS = 1
# SPF check is too early
#CHECK_RCPT_SPF = 1
CHECK_DATA_VERIFY_HEADER_SYNTAX = 1
# This is not a callout verification;
# Callout is in /etc/exim4/local_check_data
#CHECK_DATA_VERIFY_HEADER_SENDER = 1

# Define variables
# ================

# Main
# ----
MAIN_QUALIFY_DOMAIN = sput.nl
MESSAGE_SIZE_LIMIT = 10M
#MAIN_HARDCODE_PRIMARY_HOSTNAME = kill-spammers.sput.nl

# Acl
# ---
# CHECK_RCPT_IP_DNSBLS is not defined
# Blacklists are in /etc/exim4/local_check_rcpt and /etc/exim4/local_check_data
#
# CHECK_RCPT_DOMAIN_DNSBLS is not defined
# Blacklists are in /etc/exim4/local_check_rcpt and /etc/exim4/local_check_data

# My own variables
# ----------------
# I have a mail forward on these
hostlist spf_white_hosts = \
	www.lvp-site.nl : \
	*.xs4all.nl

# My own ACLs
# -----------
CHECK_RCPT_LOCAL_ACL_FILE = /etc/exim4/local_check_rcpt
CHECK_DATA_LOCAL_ACL_FILE = /etc/exim4/local_check_data

# I defined this
LOCAL_COMBI_WHITELIST = /etc/exim4/local_combi_whitelist

'MAIN_TLS_TRY_VERIFY_HOSTS = !*' will enable TLS even if Exim thinks that there is something wrong with the remote TLS implementation.
'hostlist spf_white_hosts' are hosts where I have a '.forward' file. These are not SPF checked.

main/01_exim4-config_listmacrosdefs

I edited this file. The first edit is just above the '# listen on all all interfaces?' remark;

# RvdP, primary_hostname
primary_hostname = kill-spammers.sput.nl
# RvdP, make helo interface dependent
# Incoming connections + Callout
smtp_active_hostname = ${if eq{$interface_address}{80.101.95.251}\
   {ns4.sput.nl}{kill-spammers.sput.nl}}

# listen on all all interfaces?

'80.101.95.251' is my IPv4 address. Replace with your own.
'ns4.sput.nl' is the reverse lookup for '80.101.95.251' and 'kill-spammers.sput.nl' the HELO used by my server. Replace these with your own.

main/02_exim4-config_options

The first edit is just above '# Message size limit. The default (used when MESSAGE_SIZE_LIMIT'

# RvdP, These options specify the Access Control Lists (ACLs) that
# are used to control the ETRN, EXPN, and VRFY commands.
# Where no ACL is defined, the command is locked out.

acl_smtp_vrfy = check_vrfy

# Message size limit. The default (used when MESSAGE_SIZE_LIMIT

The next edit is just above '# Domain used to qualify unqualified recipient addresses';

# RvdP, Enable virus check
# av_scanner = clamd:/tmp/clamd
#av_scanner = clamd:/var/run/clamav/clamd.ctl
av_scanner = clamd:127.0.0.1 3310


# For spam scanning, there is a similar option that defines the interface to
# SpamAssassin. You do not need to set this if you are using the default, which
# is shown in this commented example. As for virus scanning, you must also
# modify the acl_check_data access control list to enable spam scanning.

# RvdP, Enabled
spamd_address = 127.0.0.1 783

# Domain used to qualify unqualified recipient addresses

The next edit is just above '# In a minimaldns setup, update-exim4.conf guesses the hostname and';

# RvdP, use /etc/hosts first
host_lookup_order = byaddr:bydns

# In a minimaldns setup, update-exim4.conf guesses the hostname and

The next edit is just above '# Bounce handling';

# RvdP, other main config options

# RvdP, mild verbal abuse
bounce_message_text = "See http://www.sput.nl/spam/spam-policy.html"

# RvdP, complain about HELO after RCPT
helo_allow_chars = _

# RvdP, Check HELO after RCPT
helo_try_verify_hosts = *

# RvdP, No pipelining
pipelining_advertise_hosts = :

# RvdP, filter stuff
message_body_visible = 128K

# RvdP, 8 bit stuff
accept_8bitmime
print_topbitchars

# RvdP, Charset
headers_charset = UTF-8

# Bounce handling

Acl

acl/30_exim4-config_check_rcpt

Filter postmaster (My SpamAssassin setup does not spam filter abuse);

  # Accept mail to postmaster in any local domain, regardless of the source,
  # and without verifying the sender.
  #
  # RvdP, disabled
  #accept
  #  .ifndef CHECK_RCPT_POSTMASTER
  #  local_parts = postmaster
  #  .else
  #  local_parts = CHECK_RCPT_POSTMASTER
  #  .endif
  #  domains = +local_domains : +relay_to_domains


  # Deny unless the sender address can be verified.

Use a callout verification;

  # Accept if the address is in a domain for which we are an incoming relay,
  # but again, only if the recipient can be verified.

  accept
    domains = +relay_to_domains
    endpass
    # RvdP, use a callout
    #verify = recipient
    verify = recipient/callout=100s


  # At this point, the address has passed all the checks that have been

acl/50_exim4-config_check_vrfy


# 50_exim4-config_check_vrfy

# RvdP, ACL that is used after the VRFY command
check_vrfy:
  accept

router

router/200_exim4-config_primary

I use 192.168.0.0/16 on my LAN, so I enabled these. I don't want to use link local or site local addresses for mail;

  # ignore private rfc1918 and APIPA addresses
  # RvdP, I have my own blacklist DNS,
  # Ignore local IPv6 too ( fe80::/10 + fec0::/10 = fe80::/9 )
  #ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 : 192.168.0.0/16 :\
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 :\
                        172.16.0.0/12 : 10.0.0.0/8 : 169.254.0.0/16 :\
                        255.255.255.255 : fe80::::/9 : fc00::::/7
  no_more

.endif


.ifdef DCconfig_local
# configtype=local

The colon (':') is escaped by using a double colon. So '::' instead of ':' and '::::' instead of '::'.

Other files

These are not in '/etc/exim4/conf.d/' but in '/etc/exim4/'. Below are the complete files. Edit these to suit your needs.

local_check_rcpt


# RvdP, this is the local RCPT ACL
# A lot of defenitions are in /etc/exim4/conf.d/main/000_localmacros 
# Options are in /etc/exim4/conf.d/main/02_exim4-config_options

# Checks are done in order: IP, Host, Helo, From, To.
# DNS based checks are in order of increasing DNS load.
# SMTP based checks in order of increasing SMTP load.
# Some of these rules defer rather then deny. These can then be whitelisted
# Defers are before denies.
# Reject (deny) is only in case of obvious malice.

# First whitelist temp recipients;
# Tell the DATA ACL that we did, and then accept
  warn
    set acl_m_epoch = $tod_epoch
    message         = X-Sput-WL: $acl_m_epoch
    recipients      = ${if exists{CONFDIR/local_rcpt_whitelist}\
      {CONFDIR/local_rcpt_whitelist}\
    {}}

accept
  recipients = ${if exists{CONFDIR/local_rcpt_whitelist}\
    {CONFDIR/local_rcpt_whitelist}\
  {}}

# Check hostname
  defer
    message   = Broken Reverse DNS; no host name found for IP address $sender_host_address. Please contact your ISP.
    condition = ${if and \
      {\
        {def:sender_host_address}\
        {!def:sender_host_name}\
      }\ 
    {yes}{no}}

# Include whitelist
# Combines host name and email address
  .ifdef LOCAL_COMBI_WHITELIST
    .include LOCAL_COMBI_WHITELIST
  .endif

# Check HELO after RCPT
  defer
    message   = Underscores are not allowed in hostnames. Please contact your ISP.
    condition = ${if match\
      {$sender_helo_name}\
      {\N.*_.*\N}\
    {yes}{no}}

# Helo can't be localhost, *.local, *.localdomain or *.lan
  defer
    message   = HELO can't be $sender_helo_name. Please contact your ISP.
    condition = ${if match\
      {$sender_helo_name}\
      {\N(localhost|\.local(domain)?|\.lan)$\N}\
    {yes}{no}}

# Helo should be FQDN
  defer
    hosts     = !+relay_from_hosts
    message   = HELO should be Fully Qualified Domain Name. Please contact your ISP.
    condition = ${if !match\
      {$sender_helo_name}\
      {\N.*[A-Za-z].*\..*[A-Za-z].*\N}\
    {yes}{no}}

# A remote helo can't be mine
  deny
    message   = Using my HELO is identity theft.
    hosts     = ! : !+relay_from_hosts
    condition = ${if match\
      {$sender_helo_name}\
      {\N^(80\.101\.95\.251|2001:888:1533:|(.*\.)?sput\.nl)$\N}\
    {yes}{no}}

# Domain can't be localhost or localdomain
  defer
    message   = $sender_address_domain is an incorrect domain. Please contact your ISP.
    hosts     = ! : !+relay_from_hosts
    condition = ${if match\
      {$sender_address_domain}\
      {\N(localhost|\.local(domain)?|\.lan)$\N}\
    {yes}{no}}

# A remote host using my Domain is wrong
  deny
    hosts     = ! : !+relay_from_hosts : !+spf_white_hosts
    message   = Using my domain is identity theft.
    condition = ${if match\
      {$sender_address_domain}\
      {\N^(.*\.)?sput\.nl$\N}\
    {yes}{no}}

HELO check. If the HELO verify fails there must be some other link between the HELO and the hostname. If not, it's a zomby box.

# Try to match helo
# Conditions are AND-ed.
  defer
    message   = Lookup of $sender_helo_name failed or did not match. Please contact your ISP.
    hosts     = ! : !+relay_from_hosts
   !verify    = helo
    condition = ${if !eq\
      # Domains without TLD
      {${extract{-2}{.}{${lc:$sender_host_name}}}}\
      {${extract{-2}{.}{${lc:$sender_helo_name}}}}\
    {yes}{no}}
    condition = ${if \
      # Same network
      or {\
        {\
          # IPv4; Same /24, EG 192.168.1.
          and {\
            {isip4{$sender_host_address}}\
            {!match \
              {${lookup dnsdb{a=$sender_helo_name}{$value}fail}}\
              {\
                ${extract{1}{.}{$sender_host_address}}\.\
                ${extract{2}{.}{$sender_host_address}}\.\
                ${extract{3}{.}{$sender_host_address}}\.\
              }\
            }\
          }\
        }\
        {\
          # IPv6; Same /48, EG 2001:0db8:1234:
          and {\
            {isip6{$sender_host_address}}\
            {!match \
              {${lookup dnsdb{a=$sender_helo_name}{$value}fail}}\
              {\
                ${extract{1}{:}{$sender_host_address}}\:\
                ${extract{2}{:}{$sender_host_address}}\:\
                ${extract{3}{:}{$sender_host_address}}\:\
              }\
            }\
          }\
        }\
      }\
    {yes}{no}}
    condition = ${if !match\
      # Host is mx of helo
      {${lc:${lookup dnsdb{>: mxh=$sender_helo_name}{$value}fail}}}\
      {${lc:$sender_host_name}}\
    {yes}{no}}
    condition = ${if !match \
      # Host address is mx of helo address
      {${lookup dnsdb{a=${lookup dnsdb{>: mxh=$sender_helo_name}{$value}fail}}{$value}fail}}\
      {$sender_host_address}\
    {yes}{no}}

# Enforce a message-size limit here and after DATA command
  defer
    message   = Message size $message_size is larger than limit of MESSAGE_SIZE_LIMIT. See http://www.sput.nl/spam/
    condition = ${if >\
      {$message_size}{MESSAGE_SIZE_LIMIT}\
    {yes}{no}}

# Local host based blacklist
  defer
    message = Sender IP address $sender_host_address is locally blacklisted here. See http://www.sput.nl/spam/
    hosts   = !+relay_from_hosts
    hosts   = ${if exists{CONFDIR/local_host_blackl}\
                {CONFDIR/local_host_blackl}\
              {}}

Some addresses are rejected instead of deferred, or need regexes;

# Google groups
  deny
    message = Fuck off morons
    hosts   = *.google.com
    senders = *@groups.bounces.google.com
# *@messagent.*
  defer
    message = Domain $sender_address_domain is locally blacklisted here. See http://www.sput.nl/spam/
    condition = ${if match\
      {$sender_address_domain}\
      {\N^messagent\..*$\N}\
    {yes}{no}}
# myfanbox.com
  deny
    message = Fuck off morons
    hosts   = *.sms.ac
    senders = fbNOREPLY@myfanbox.com
# myspace
  deny
    message = Fuck off morons
    hosts   = *.myspace.com
    senders = noreply@message.myspace.com
# NRC
  deny
    message = Fuck off morons
    condition = ${if match\
      {$sender_address_domain}\
      {\N^(.*\.)?nrc\.nl$\N}\
    {yes}{no}}

# Local address based blacklist
  defer
    message = Sender envelope address $sender_address is locally blacklisted here. See http://www.sput.nl/spam/
    hosts   = !+relay_from_hosts
    senders = ${if exists{CONFDIR/local_sender_blackl}\
                {CONFDIR/local_sender_blackl}\
              {}}

Not all blacklists support IPv6 yet. See IPv6 enabled RBLs


# Check IP address in DNS based blacklists
  deny
    hosts    = ! : !+relay_from_hosts
    message  = Host is listed in $dnslist_domain. Please contact your ISP.
    dnslists = \
      cbl.abuseat.org : \
      virbl.dnsbl.bit.nl : \
      bl.spamcop.net : \
      sbl.spamhaus.org : \
      xbl.spamhaus.org : \
      psbl.surriel.com

# Check host name in domain DNS based blacklists; Deny
  deny
    message  = Host name is listed in $dnslist_domain. Please contact your ISP.
    hosts    = ! : !+relay_from_hosts
    dnslists = \
      dob.sibl.support-intelligence.net/$sender_host_name

# Check email address domain in DNS based blacklists; Deny
  deny
    message  = Domain is listed in $dnslist_domain. Please contact your ISP.
    hosts    = ! : !+relay_from_hosts
    senders  = ! :
    dnslists = \
      dob.sibl.support-intelligence.net/$sender_address_domain

# A simple SPF check
# In case of a DNS error, it doesn't do the check.
  deny
    message     = [SPF] $sender_host_address is not allowed to send mail \
                  from $sender_address_domain. Please contact your ISP.
    senders     = ! :
    hosts       = ! : !+relay_from_hosts : !+spf_white_hosts
    #log_message = SPF check failed.
    set acl_m9  = -ipv4=$sender_host_address \
                  -sender=$sender_address \
                  -helo=$sender_helo_name
    set acl_m9  = ${run{/usr/bin/spfquery $acl_m9}}
    condition   = ${if eq \
      {$runrc}{1}\
    {true}{false}}

# Callout
  defer
    message = No verifiable envelope sender address. Please contact your ISP.
    senders = ! :
   !verify  = sender/callout=100s

# Return to default Exim rcpt ACL

local_check_data


# RvdP, this is the local DATA ACL
# A lot of defenitions are in /etc/exim4/conf.d/main/000_localmacros
# Options are in /etc/exim4/conf.d/main/02_exim4-config_options

# First whitelist temp recipients
# Check if the whitelist exists and then check if the X-Sput-WL header line
# matches the epoch.
  accept
    condition = ${if exists\
      {CONFDIR/local_rcpt_whitelist}\
    {yes}{no}}
    condition = ${if eq\
      {$h_x-sput-wl:}\
      {$acl_m_epoch}\
    {yes}{no}}

# Include whitelist
# Combines host name and email address
  .ifdef LOCAL_COMBI_WHITELIST
    .include LOCAL_COMBI_WHITELIST
  .endif

# Checks are done in order: From, To, other header stuff, message content;
# DNS based checks are in order of increasing DNS load,
# SMTP based checks in order of increasing SMTP load.
# Some of these rules defer rather then deny. These can then be whitelisted.
# Defers are before denies.
# Reject (deny) is only in case of obvious malice.

# Insist on Date
  defer
    message   = RFC compliant date required.
    condition = ${if !match\
      {$h_date:}\
      {\N[0-9]\N}\
    {yes}{no}}

# Insist on To
  defer
    message   = RFC compliant to required.
    condition = ${if !match\
      {$h_to:}\
      {\N.+\N}\
    {yes}{no}}

# More syntax: Non ASCII, except UTF-8
  defer
    message   = Message headers contain non ASCII chars.
    condition = ${if !match\
      {${lc:$h_content-type:}}\
      {\Nutf-?8\N}\
    {yes}{no}}
    condition = \
      ${if \
        or {\
          {match{$rh_bcc:}{\N[\x80-\xff]\N}}\
          {match{$rh_cc:}{\N[\x80-\xff]\N}}\
          {match{$rh_date:}{\N[\x80-\xff]\N}}\
          {match{$rh_from:}{\N[\x80-\xff]\N}}\
          {match{$rh_reply-to:}{\N[\x80-\xff]\N}}\
          {match{$rh_sender:}{\N[\x80-\xff]\N}}\
          {match{$rh_subject:}{\N[\x80-\xff]\N}}\
          {match{$rh_to:}{\N[\x80-\xff]\N}}\
        }\
      {yes}{no}}

# Domain can't be localhost or localdomain
  defer
    message   = ${domain:$h_from:} is a wrong domain. 
    hosts     = ! : !+relay_from_hosts
    condition = ${if match\
      {${domain:$h_from:}}\
      {\N^(localhost|localhost\.local(domain)?|local(domain)?)$\N}\
    {yes}{no}}

# Blacklist unreachable sender before h_from: verification
  deny
    message   = Sender content address $h_from: is locally blacklisted here. See http://www.sput.nl/spam/
    hosts     = ! : !+relay_from_hosts
    #${lookup{<key>} <search type> {<file>} {<string1>} {<string2>}} 
    condition = \
      ${lookup{${address:$h_from:}}\
      nwildlsearch {CONFDIR/local_sender_blackl}\
    {yes}{no}}

# Blacklist based on domain; Deny
  deny
    message  = Sender content domain is listed in $dnslist_domain. Please contact your ISP.
    hosts    = ! : !+relay_from_hosts
    dnslists = \
      dob.sibl.support-intelligence.net/${domain:$h_from:}

# require that there is a verifiable sender address in at least
# one of the "Sender:", "Reply-To:", or "From:" header lines.
  defer
    message = No verifiable sender address in message headers.
   !verify  = header_sender/callout=100s

# Deny fake Message-Id
  deny
    message   = Using my domain is identity theft.
    hosts     = ! : !+relay_from_hosts : !+spf_white_hosts
    condition = ${if match\
      {$h_message-id:}\
      {\N^<.+@(.+\.)?sput\.nl>$\N}\
    {yes}{no}}

# Block fake bounces on ip address
  deny
    message   = This is a fake (joe job) or sub standard (lacking original headers) DSN.
    hosts     = ! : !+relay_from_hosts : !+spf_white_hosts
    senders   = :
    condition = ${if !match\
      {${lc:$message_body}}\
      {\N( |^)received: from .+(80\.101\.95\.251|2001:888:1533:|2001:888:10:533::2)\N}\
    {yes}{no}}

# Block fake bounces on mesg-id
  deny
    message   = This is a fake (joe job) or sub standard (lacking original headers) DSN.
    hosts     = ! : !+relay_from_hosts : !+spf_white_hosts
    senders   = :
    condition = ${if !match\
      {${lc:$message_body}}\
      {\N( |^)message-id: <.+@(.+\.)?sput\.nl>( |$)\N}\
    {yes}{no}}

If you know exactly which lists your users are subscribed to, you can block unwanted mailing lists.
Mailing list subscription is only allowed by means of a confirmed opt-in. For a lot of mailing list software this is in fact the default. Subscriptions without confirmation are rogue.

# Block fake mailing lists
  defer
    message = I did not subscribe to this list
    condition = ${if match\
      {$h_list-unsubscribe:}\
      {\N.+\N}\
    {yes}{no}}
    condition = ${if !match\
      {${lc:$h_list-unsubscribe:}}\
      {\N(list1|list2|list3)\N}\
    {yes}{no}}

Replace list1, list2, list3, etc with regexes for valid lists.

# Check non ASCII in body when ASCII is specified
  defer
    message   = Charset not specified
    condition = ${if match\
      {${lc:$h_content-type:}}\
      {\Nus-ascii\N}\
    {yes}{no}}
    condition = ${if match\
      {$message_body}\
      {\N[\x80-\xff]\N}\
    {yes}{no}}

Below various specific spammer related filters. Thay all claim that you subscribed to their mailing lists. Edit to suit your needs.

# Block Paypal spam
# Deze e-mail is verzonden naar rob@sput.nl, omdat uw
# e-mailvoorkeuren zijn ingesteld voor het ontvangen van
# promotieacties van partners/derden. Abonnement opzeggen
  defer
    message   = I did not subscribe to this list.
    hosts     = *.paypal.com
    senders   = paypal@email.paypal.nl
    condition = ${if match\
      {${lc:$message_body}}\
      {\Nmailvoorkeuren zijn ingesteld voor het ontvangen van promotieacties\N}\
    {yes}{no}}

# Block Ebay spam
# Je hebt deze e-mail ontvangen omdat je in je berichtgevingsvoorkeuren hebt
# opgegeven dat je informatie over speciale acties, aanbiedingen en evenementen
# wilt ontvangen. Je bent rob@sput.nl op www.ebay.nl
  defer
    message   = I did not subscribe to this list.
    condition = ${if match\
      {${lc:$h_from:}}\
      {\Nebay\N}\
    {yes}{no}}
    condition = ${if match\
      {${lc:$message_body}}\
      {\Nberichtgevingsvoorkeuren hebt opgegeven dat je informatie over speciale acties\N}\
    {yes}{no}}

# Block Ebay spam
# Je hebt deze e-mail ontvangen omdat je in je berichtgevingsvoorkeuren hebt
# opgegeven dat je informatie over speciale acties, aanbiedingen en evenementen
# wilt ontvangen. Je bent rob@sput.nl op www.ebay.nl
  defer
    message   = I did not subscribe to this list.
    condition = ${if match\
      {${lc:$h_from:}}\
      {\Nebay\N}\
    {yes}{no}}
    condition = ${if match\
      {${lc:$message_body}}\
      {\Nberichtgevingsvoorkeuren hebt opgegeven dat je informatie over speciale acties\N}\
    {yes}{no}}

# Block Marktplaats spam
# OK:
# Dit is een kopie van uw reactie op een advertentie van:
# Not OK:
# Marktplaats B.V. heeft deze e-mail naar u gestuurd. Op dit adres wordt u
# op de hoogte gehouden van nieuws,
# productwijzigingen en promoties van Marktplaats.nl. Indien u hier geen
# prijs op stelt, klik dan
  defer
    message   = I did not subscribe to this list.
    condition = ${if match\
      {${lc:$h_from:}}\
      {\Nmarktplaats\N}\
    {yes}{no}}
    condition = ${if !match\
      {${lc:$message_body}}\
      {\Ndit is een kopie van uw reactie op een advertentie van\N}\
    {yes}{no}}
    condition = ${if match\
      {${lc:$message_body}}\
      {\Nop de hoogte gehouden\N}\
    {yes}{no}}
    condition = ${if match\
      {${lc:$message_body}}\
      {\Nproductwijzigingen en promoties\N}\
    {yes}{no}}

Roque webmail quota.

# Block quota shit.
# Beste gebruiker,
# =20
# Uw e-mail quota heeft overschreden Admin opslag termijn die is 20GB =
# zoals ingesteld door uw beheerder, bent u momenteel op 20.9GB, kunt u =
# niet in staat zijn om nieuwe e-mail verzenden of ontvangen totdat je =
# weer valideren uw mailbox. Voor re-valideren uw mailbox klik Dan Hier: =
# <http://www.opinionpower.com/Surveys/634059182.html>=20
#
# Line may be wrapped after dot;
# http://www.=
#   opinionpower.=
#   com/Surveys/
#
# For tests Exim converts newlines to spaces.
#
  defer
  message   = I don't use webmail
  condition = ${if match\
    {${lc:$message_body:}}\
    {\Nhttp://www\.(= )?opinionpower\.(= )?com/surveys/\N}\
  {yes}{no}}

# Enforce a message-size limit
  defer
    message   = Message size $message_size is larger than limit of MESSAGE_SIZE_LIMIT. See http://www.sput.nl/spam/
    condition = ${if >\
      {$message_size}{MESSAGE_SIZE_LIMIT}\
    {yes}{no}}

# Reject broken attachments
  deny
    message   = This message contains malformed MIME $demime_reason.
    demime    = *
    condition = ${if >\
      {$demime_errorlevel}{2}\
    {1}{0}}

# Dos / Windows junk
  deny
    message = Attachment has unsupported file format .$found_extension. try text, PDF or ODF instead.
    demime  = bat:btm:cmd:com:cpl:dat:dll:exe:lnk:msi:pif:prf:reg:scr:vb:vbs

If you want the check stuff inside attached zip files, have a look at filtering zip files.

# Virus scan
  deny
    message = Message contains a virus or other harmful content ($malware_name)
    demime  = *
    malware = *

# Don't spam check local generated mail
# (Comment out for spam check test purposes)
  accept
    hosts = : +relay_from_hosts

# Don't spam filter abuse
  accept
    condition = ${if \
      and {\
        {match{$recipients}{\N<?abuse@(.+\.)?sput\.nl>?\N}}\
        {match{$h_to:}{\N<?abuse@(.+\.)?sput\.nl>?\N}}\
      }\
    {yes}{no}}

# Spam filter the rest
# Warn tmp at 00 points for debug
  warn
    message   = X-Spam-Score: $spam_score ($spam_bar).
    #condition = ${if <\
    #  {$message_size}{128k}\
    #{1}{0}}
    spam      = spamd:true
    condition = ${if >\
      #{$spam_score_int}{32}\
      {$spam_score_int}{0}\
    {1}{0}}
  warn
    message   = X-Spam-Report: $spam_report
    #condition = ${if <\
    #  {$message_size}{128k}\
    #{1}{0}}
    spam      = spamd:true
    condition = ${if >\
      #{$spam_score_int}{32}\
      {$spam_score_int}{0}\
    {1}{0}}
  deny
    message   = This message scored $spam_score spam points.
    #condition = ${if <\
    #  {$message_size}{128k}\
    #{1}{0}}
    spam      = spamd:true
    condition = ${if >\
      {$spam_score_int}{42}\
    {1}{0}}

# Return to default Exim data ACL

Filtering zip files

Just before the virus scan, insert the following;

# Reject stuff inside zip
  deny
    message   = Attachment has unsupported file format inside zip file
    demime    = zip
    condition = ${run{/bin/sh -c '/usr/local/sbin/check_zip.sh $message_exim_id'}{1}{0}}

/usr/local/sbin/check_zip.sh;

#/bin/bash
cd "/var/spool/exim4/scan/${1}"
for i in $( ls | egrep -i '[.]zip' )
do
if [ $( unzip -l "${i}" | \
  egrep -i '[.](bat|btm|cmd|com|cpl|dat|dll|exe|lnk|msi|pif|prf|reg|scr|vb|vbs)$' | \
  wc -l ) -gt 0 ]
then
	exit 1
fi
done
exit 0

local_combi_whitelist

Don't use this if you use Windows on your LAN. This whitelist bypasses the virus check!

# RvdP, local comibined host - address whitelist
# A lot of defenitions are in /etc/exim4/conf.d/main/000_localmacros

# Example
  accept
    hosts   = *.example.com
    senders = *@example.com

# 2nd example
  accept
    hosts   = *.example.org
    senders = joe@example.org

local_host_blackl

This file simply lists blacklisted hosts. One per line. Wildcard can be used.

local_sender_blackl

This file simply lists blacklisted senders. One per line. Wildcard can be used.

SPF configuration

Clamav configuration

In '/etc/clamav/clamd.conf/' I set TCPAddr to 127.0.0.1;

TCPSocket 3310
# RvdP, localhost
TCPAddr 127.0.0.1
TemporaryDirectory /tmp

SpamAssassin configuration

Is run SpamAssassin as user spamd. A lot of SpamAssassin, Pyzor and Razor files are therefore in '~spamd/'.
I used '/home/spamd/' as a homedir. Maybe '/var/lib/spamd/' is a better location.

/etc/default/spamassassin

Enable spamd and run as user spamd;

# /etc/default/spamassassin
# Duncan Findlay

# WARNING: please read README.spamd before using.
# There may be security risks.

# Change to one to enable spamd
# RvdP, enabled
#ENABLED=0
ENABLED=1

# Options
# See man spamd for possible options. The -d option is automatically added.

# SpamAssassin uses a preforking model, so be careful! You need to
# make sure --max-children is not set to anything higher than 5,
# unless you know what you're doing.

# RvdP, non priv
#OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
OPTIONS="--create-prefs --max-children 5 --helper-home-dir --username spamd"

# Pid file
# Where should spamd write its PID to file? If you use the -u or
# --username option above, this needs to be writable by that user.
# Otherwise, the init script will not be able to shut spamd down.
# RvdP, non priv
#PIDFILE="/var/run/spamd.pid"
PIDFILE="/var/run/spamd/spamd.pid"

# Set nice level of spamd
#NICE="--nicelevel 15"

# Cronjob
# Set to anything but 0 to enable the cron job to automatically update
# spamassassin's rules on a nightly basis
CRON=0

/var/run/spamd/ should be owned spamd:spamd

Files in /etc/spamassassin/

local.cf

I added a lot of stuff;

#   Set headers which may provide inappropriate cues to the Bayesian
#   classifier
#
# bayes_ignore_header X-Bogosity
# bayes_ignore_header X-Spam-Flag
# bayes_ignore_header X-Spam-Status

# RvdP, my own stuff
# ==================

# Modules
# -------

# DCC is enabled in v310.pre
# DKIM is enabled in v312.pre (supersedes DomainKeys)
# Spamcop is disabled in v310.pre

# Config
# ------

# Pyzor home
pyzor_options --homedir /home/spamd/.pyzor

# razor.conf
razor_config		/home/spamd/.razor/razor-agent.conf

# Charset
report_charset		UTF-8

# Sensible contact
report_contact		postmaster@sput.nl
report_hostname		sput.nl

# I Don't understand other languages
ok_languages            en nl
ok_locales              en nl

# Scores
# ------

# Blacklisted in received lines
score RCVD_IN_SBL	10.0
score RCVD_IN_XBL	10.0
score RCVD_IN_BL_SPAMCOP_NET 10.0

# Blacklisted web sites
score URIBL_AB_SURBL	10.0
score URIBL_OB_SURBL	10.0
score URIBL_PH_SURBL	10.0
score URIBL_SC_SURBL	10.0
score URIBL_WS_SURBL	10.0

# Razor
score RAZOR2_CHECK		2.0
score RAZOR2_CF_RANGE_51_100	2.0
score RAZOR2_CF_RANGE_E4_51_100	2.0
score RAZOR2_CF_RANGE_E8_51_100	2.0

# More bayes
#score BAYES_40		 2.0
#score BAYES_50		 2.5
score BAYES_60		 3.0
score BAYES_80		 4.0
score BAYES_95		 5.0
score BAYES_99		10.0

SORBS lists static IP addresses as dynamic. It's best not to use SORBS at all!

# SORBS Sux
score RCVD_IN_SORBS_DUL	 0

# HTML Only sux
score MIME_HTML_ONLY	10.0

v310.pre

Disabled spamcop auto reporting;

# RvdP, disabled
#loadplugin Mail::SpamAssassin::Plugin::SpamCop

# AntiVirus - some simple anti-virus checks, this is not a replacement

Enabled language guesser;
You need this for 'ok_languages'.

# TextCat - language guesser
#
# RvdP, enabled
loadplugin Mail::SpamAssassin::Plugin::TextCat

# AccessDB - lookup from-addresses in access database

v312.pre

Enabled the DKIM module;

# Note that if C<Mail::DKIM> version 0.20 or later is installed, this
# renders the DomainKeys plugin redundant.
#
# RvdP, enabled
loadplugin Mail::SpamAssassin::Plugin::DKIM

Auto update

This is a bit like volatile for SpamAssassin. By setting the cron value in /etc/default/spamassassin to non zero, /etc/cron.daily/spamassassin will be enabled, which updates SpamAssassin automatically;

# Cronjob
# Set to anything but 0 to enable the cron job to automatically update
# spamassassin's rules on a nightly basis
# RvdP, Enabled
#CRON=0
CRON=1

The files will be put in /var/lib/spamassassin/

If you don't compile SpamAssassin you don't need re2c or sa-compile.
If you want to update SpamAssassin immediately after installation, run;


sa-update
chmod -R go-w,go+rX /var/lib/spamassassin/
/etc/init.d/spamassassin reload

This doesn't recompile SpamAssassin.

sa-update over proxy

Append a proxy setting to /etc/default/spamassassin. EG;

# RvdP, proxy
http_proxy='http://proxy.example.org:8080/'

This makes the proxy environment variable available to /etc/cron.daily/spamassassin

Edit /etc/cron.daily/spamassassin (just under the $CRON test) so it exports the proxy environment variable to sa-update;


if [ "$CRON" = "0" ] ; then
    exit 0
fi

# RvdP, Use proxy; export to child
if [ -n "${http_proxy}" ]
then
	export "http_proxy=${http_proxy}"
fi

# If there's a problem with the ruleset or configs, print the output

The http_proxy environment variable is only exported if it exist and is non-zero.

Bayes

I have a spam archive and lots of saved non spam messages.

su - spamd
cd .spamassassin/
sa-learn --mbox --spam Spam_File
sa-learn --mbox --spam Ham_File

Image and PDF spam check

PDFassassin interferes with fuzzyocr on Squeeze. However, fuzzyocr can be made to do pdf checks as well. So you don't need PDFassassin. In FuzzyOcr.cf.real just uncomment the line;

focr_bin_helper pdfinfo, pdftops, pstopnm

And install pdfinfo, pdftops and pstopnm. These are in the Debian packages netpbm and poppler-utils.

Pyzor configuration

From the readme;
Before using pyzor the first time, you should run "pyzor discover" to store public server IPs in the server configuration file, which is usually located at $HOME/.pyzor/servers
You need to do this as user spamd.

Razor configuration

I edited /etc/razor/razor-agent.conf';

# See razor-agent.conf (5)
# Change this to 5 for safer classification of MIME attachments.  This will let more spam through
logic_method = 4
# Change the next line to a file to stop using syslog
logfile = /var/log/razor-agent.log

# RvdP, force ~spamd/
razorhome = /home/spamd/.razor

Misc

Reporting to Pyzor

Create a dir .pyzor in your homedir. Copy the servers file to this dir. Create an empty mailfolder, EG 'todays-spam'. Save the spam to this folder. Copy the spam to stdin of Pyzor and tell it to report the spam;
cat mail/todays-spam | pyzor report
This should produce something like '(200, OK)'.

Delete the contents of 'todays-spam' before you save more spam.

DNS issues

Make sure you have a helo name which points to the ip addresses of all the interfaces which your mailserver might use.
If you have a split DNS, use all interfaces for in the internal, and just the external interface names in the external DNS.
If your mailserver is behind reverse NAT / portforwarding, the helo name should point to the external IP address too.

/etc/services

It's a good idea to put the ports used by various daemons in /etc/services (if not already there);

# Local services
clamd		3310/tcp			# Clam AntiVirus
spfd		5970/tcp			# Sender Policy Framework

IPv6 Ready