Postfix -> DSPAM -> Amavis/Spamassassin -> Postfix

Spamassassin can add scores depending on the DSPAM output by reading the headers that DSPAM adds.

These rules can be added to local.cf. They read confidence and result headers. Edit the scores to suit your setup.

# DSPAM RULES ###############
# Confidence #
header DSPAM_CONFIDENCE_8X X-DSPAM-Confidence =~ /^0\.8/
describe DSPAM_SPAM_8X DSPAM is moderately confident
score DSPAM_CONFIDENCE_8X 0.001

header DSPAM_CONFIDENCE_9X X-DSPAM-Confidence =~ /^0\.9[0-8]/
describe DSPAM_SPAM_9X DSPAM is very confident
score DSPAM_CONFIDENCE_9X 0.001

header DSPAM_CONFIDENCE_99 X-DSPAM-Confidence =~ /^0\.99/
describe DSPAM_SPAM_99 DSPAM is extremely confident
score DSPAM_CONFIDENCE_99 0.001

# Spam #
header DSPAM_SPAM X-DSPAM-Result =~ /^Spam$/
describe DSPAM_SPAM DSPAM claims it is spam
score DSPAM_SPAM 5

meta DSPAM_SPAM_8X (DSPAM_SPAM && DSPAM_CONFIDENCE_8X)
score DSPAM_SPAM_8X 1.5

meta DSPAM_SPAM_9X (DSPAM_SPAM && DSPAM_CONFIDENCE_9X)
score DSPAM_SPAM_9X 3.0

meta DSPAM_SPAM_99 (DSPAM_SPAM && DSPAM_CONFIDENCE_99)
score DSPAM_SPAM_99 5.0

# Ham #
header DSPAM_HAM X-DSPAM-Result =~ /^Innocent$/
describe DSPAM_HAM DSPAM claims it is ham
score DSPAM_HAM -0.1

meta DSPAM_HAM_9X (DSPAM_HAM && DSPAM_CONFIDENCE_9X)
score DSPAM_HAM_9X -1.5

meta DSPAM_HAM_99 (DSPAM_HAM && DSPAM_CONFIDENCE_99)
score DSPAM_HAM_99 -5.0

# Whitelist #

header DSPAM_WHITELIST X-DSPAM-Result =~ /^Whitelisted$/
describe DSPAM_WHITELIST DSPAM has whitelisted this mail
score DSPAM_WHITELIST -2.0

meta DSPAM_WHITELIST_9X (DSPAM_WHITELIST && DSPAM_CONFIDENCE_9X)
score DSPAM_WHITELIST_9X -1.5

meta DSPAM_WHITELIST_99 (DSPAM_WHITELIST && DSPAM_CONFIDENCE_99)
score DSPAM_WHITELIST_99 -5.0

This is something I’ve been tweaking for a few months now. I’ve got it filtering 99% of spam before it hits content filtering.

Postfix v2.5.5 using PostFWD v1.18 as a policy daemon with PostGrey v1.31 for greylisting.

## ----------------------------------
# Trusted networks (internal usually)
&&TRUSTED_NETS { \
client_address=192.168.0.0/16 ; \
};

# Trusted hostnames
&&TRUSTED_HOSTS { \
client_name~=\.nooblet\.org$ ; \
};

# Trusted sasl usernames
&&TRUSTED_USERS { \
sasl_username==stalks ; \
};

# Free mailers we don't need to greylist
&&FREEMAIL { \
client_name~=\.gmx\.net$ ; \
client_name~=\.web\.de$ ; \
client_name~=\.(aol|yahoo|h(ush|ot)mail)\.co(\.uk|m)$ ; \
};

# Static IPs, no need to greylist
# contains freemailers
&&STATIC { \
&&FREEMAIL ; \
client_name~=[\.\-]static[[\.\-] ; \
client_name~=^(mail|smtp|mout|mx)[\-]*[0-9]*\. ; \
};

# Client reverse != smtp helo
&&BADHELO { \
client_name==!!($$(helo_name)) ; \
};

&&NORDNS { \
client_name==unknown ; \
};

&&DYNAMIC { \
&&NORDNS ; \
client_name~=(\-.+){4} ; \
client_name~=\d{5} ; \
client_name~=[_\.\-]([axt]{0,1}dsl|br(e|oa)dband|ppp|pppoe|dynamic|dynip|adsl|dial(up|in)|pool|dhcp|leased)[_\.\-] ; \
};
&&DYNL { \
rbl=zen.spamhaus.org/^127\.0\.0\.1[0-1]$/3600 ; \
rbl=dul.dnsbl.sorbs.net ; \
};

&&RWL { \
rbl=list.dnswl.org ; \
rbl=hostkarma.junkemailfilter.com/^127\.0\.0\.1$/3600 ; \
rhsbl_client=hostkarma.junkemailfilter.com/^127\.0\.0\.1$/3600 ; \
};

&&RBL { \
rbl=zen.spamhaus.org/^127\.0\.0\.[2-8]$/3600 ; \
rbl=hostkarma.junkemailfilter.com/^127\.0\.0\.(2|4)$/3600 ; \
rbl=bl.spamcop.net ; \
rbl=problems.dnsbl.sorbs.net ; \
rhsbl_client=hostkarma.junkemailfilter.com/^127\.0\.0\.(2|4)$/3600 ; \
rhsbl=rhsbl.ahbl.org ; \
rhsbl=rhsbl.sorbs.net ; \
};

##
## Ruleset
##

# stress-friendly behaviour (will not match on postfix version pre 2.5)
id=STRESS ; stress==yes ; action=dunno

# Whitelists (fixed)
id=WL_001 ; &&TRUSTED_NETS ; action=dunno
id=WL_002 ; &&TRUSTED_HOSTS ; action=dunno
id=WL_003 ; &&TRUSTED_USERS ; action=dunno

# Dynamic Counter
id=DYNL_001 ; &&DYNL ; rblcount=all ; action=set(HIT_dynls=$$rblcount, DYNL_text=$$dnsbltext)

# DNS Block Lists
id=RBL_001 ; &&RBL ; \
rhsblcount=all ; rblcount=all ; \
action=set( \
HIT_rbls=$$rblcount, \
HIT_rbls+=$$rhsblcount, \
RBL_text=$$dnsbltext)
id=RBL_002 ; HIT_rbls>=2 ; action=REJECT You are listed on $$HIT_rbls RBLs. [$$RBL_text]
id=RBL_003 ; HIT_rbls>=1 ; HIT_dynls>=1 ; action=REJECT Host listed as dynamic and listed on RBL. [$$RBL_text]
id=RBL_004 ; HIT_rbls>=1 ; &&NORDNS ; action=REJECT No reverse DNS and listed on RBL. [$$RBL_text]
id=RBL_005 ; HIT_rbls>=1 ; &&DYNAMIC ; action=REJECT Host looks dynamic and listed on RBL. [$$RBL_text]
id=RBL_006 ; HIT_rbls>=1 ; &&BADHELO ; action=REJECT (helo $$helo_name) != ($$client_name) and listed on RBL. [$$RBL_text]

# Whitelists (rwl)
id=RWL_001 ; &&RWL ; \
rhsblcount=all ; rblcount=all ; \
action=set( \
HIT_rwls=$$rblcount, \
HIT_rwls+=$$rhsblcount, \
RWL_text=$$dnsbltext)
id=RWL_002 ; HIT_rwls>=1 ; action=PREPEND X-POSTFWD: Listed on $$HIT_rwls whitelists. [$$RWL_text]

# Rate limits
id=RATE_001 ; HIT_rbls>=1 ; action=rate($$client_address/1/300/450 4.7.1 Throttled. Listed on RBL. Limited to 1 message every 5 minutes. [$$RBL_text])
id=RATE_002 ; HIT_dynls>=1 ; action=rate($$client_address/1/300/450 4.7.1 Throttled. Listed as dynamic. Limited to 1 message every 5 minutes.)
id=RATE_003 ; &&NORDNS ; action=rate($$client_address/1/300/450 4.7.1 Throttled. No reverse DNS. Limited to 1 message every 5 minutes.)
id=RATE_004 ; &&DYNAMIC ; action=rate($$client_address/1/300/450 4.7.1 Throttled. Host is probably dynamic. Limited to 1 message every 5 minutes.)

# Selective greylist
id=GREY_001 ; action=greylist ; HIT_rbls>=1
id=GREY_002 ; action=dunno ; &&STATIC
id=GREY_003 ; action=dunno ; $$client_name~=$$(sender_domain)$
id=GREY_004 ; action=dunno ; HIT_rwls>=1
id=GREY_005 ; action=greylist ; HIT_dynls>=1
id=GREY_006 ; action=greylist ; &&DYNAMIC
## greylist should be safe during out-of-office-hours
# id=GREY_007 ; action=greylist ; days=Sat-Sun
# id=GREY_008 ; action=greylist ; days=Mon-Fri ; time=!!06:00:00-20:00:0