Dspam, Dovecot, Postfix

DSPAM is a statistical spam filter. It is intended to be scalable, content-based spam filter for large multi-user systems. Documentation for dspam .

NOTE: This is the only component I’ve not found to NOT speak SSL over SMTP/LMTP. It does connect to PostgreSQL over SSL just fine.

Create/populate the database

First, we will need to create a database and then populate it. Creating is straight forward. Connect to Postgres and type in:

1CREATE ROLE dspam WITH LOGIN ENCRYPTED PASSWORD 'password';
2CREATE DATABASE dspam WITH OWNER = dspam;

Now its time to populate it. You can use the following to populate your dspam database:

1psql -U dspam -W dspam < /usr/local/share/examples/dspam/pgsql/pgsql_objects.sql
2psql -U dspam -W  dspam < /usr/local/share/examples/dspam/pgsql/virtual_users.sql

You’ll get some warnings about permissions for analyzing. These can safely be ignored. We will setup a daily script that will perform database maintenance including analyization.

If you get errors like this (This doesn’t happen right away, but it may happen after things have been running for days/weeks/months):

1Dec 12 03:41:29 pgsql.cmhome postgres[92640]: [126-2] DETAIL:  Key (uid, token)=(28, -7501006800599240569) already exists.
2Dec 12 03:41:29 pgsql.cmhome postgres[92640]: [126-3] STATEMENT:  PREPARE dspam_update_plan (bigint) AS UPDATE dspam_token_data SET last_hit=CURRENT_DATE,innocent_hits=innocent_hits+1 WHERE uid=28 AND token=$1;PREPARE dspam_insert_plan (bigint,int,int) AS INSERT INTO dspam_token_data (uid,token,spam_hits,innocent_hits,last_hit) VALUES (28,$1,$2,$3,CURRENT_DATE);

You can add the following to postgres. It won’t fix dspam, but it will work around the problem by telling postgres to attempt to do an update, and if the update fails, redo the same thing as an insert. It also avoids the round-trip time of sending the error back to the mail server, and having dspam issue another sql statement.

1CREATE FUNCTION update_rec(TEXT,INTEGER,INTEGER) RETURNS VOID AS $$
2BEGIN
3UPDATE dspam.table SET f1=$2, f2=$3 WHERE k=$1;
4IF NOT FOUND THEN
5INSERT INTO dspam.table (k,f1,f2) VALUES ($1,$2,$3);
6END IF;
7END;
8$$ LANGUAGE plpgsql;

Configuring DSPAM

Now we can edit the dspam.conf (/usr/local/etc). Most of it is default. Of note, I’ve told it to store preferences in the database. I’ve also added a long list of headers to ignore (we don’t want somebody setting a header that causes us to not scan something, or that sends confusing messages to the user. Here is what mine looks like:

  1## $Id: dspam.conf.in,v 1.103 2011/11/10 00:27:34 tomhendr Exp $
  2## dspam.conf -- DSPAM configuration file
  3##
  4
  5#
  6# DSPAM Home: Specifies the base directory to be used for DSPAM storage
  7#
  8Home /var/db/dspam
  9
 10#
 11# StorageDriver: Specifies the storage driver backend (library) to use.
 12# You'll only need to set this if you are using dynamic storage driver plugins
 13# from a binary distribution. The default build statically links the storage
 14# driver (when only one is specified at configure time), overriding this
 15# setting, which only comes into play if multiple storage drivers are specified
 16# at configure time. When using dynamic linking, be sure to include the path
 17# to the library if necessary, and some systems may use an extension other
 18# than .so (e.g. OSX uses .dylib).
 19#
 20StorageDriver /usr/local/lib/dspam/libpgsql_drv.so
 21
 22# Trusted Delivery Agent: Specifies the local delivery agent DSPAM should call
 23# when delivering mail as a trusted user. Use %u to specify the user DSPAM is
 24# processing mail for. It is generally a good idea to allow the MTA to specify
 25# the pass-through arguments at run-time, but they may also be specified here.
 26#
 27TrustedDeliveryAgent "/usr/local/sbin/sendmail" # FreeBSD
 28
 29# Untrusted Delivery Agent: Specifies the local delivery agent and arguments
 30# DSPAM should use when delivering mail and running in untrusted user mode.
 31# Because DSPAM will not allow pass-through arguments to be specified to
 32# untrusted users, all arguments should be specified here. Use %u to specify
 33# the user DSPAM is processing mail for. This configuration parameter is only
 34# necessary if you plan on allowing untrusted processing.
 35#
 36#UntrustedDeliveryAgent "/usr/bin/procmail -d %u"
 37#UntrustedDeliveryAgent "/usr/libexec/mail.local"
 38
 39#
 40# SMTP or LMTP Delivery: Alternatively, you may wish to use SMTP or LMTP
 41# delivery to deliver your message to the mail server instead of using a
 42# delivery agent. You will need to configure with --enable-daemon to use host
 43# delivery, however you do not need to operate in daemon mode. Specify an IP
 44# address or UNIX path to a domain socket below as a host.
 45#
 46# If you would like to set up DeliveryHost's on a per-domain basis, use
 47# the syntax: DeliveryHost.example.org 1.2.3.4
 48#
 49DeliveryHost		127.0.0.1
 50DeliveryPort		24
 51DeliveryIdent		localhost
 52DeliveryProto		LMTP
 53
 54# Quarantine Agent: DSPAM's default behavior is to quarantine all mail it
 55# thinks is spam. If you wish to override this behavior, you may specify
 56# a quarantine agent which will be called with all messages DSPAM thinks is
 57# spam. Use %u to specify the user DSPAM is processing mail for.
 58#
 59#QuarantineAgent	"/usr/bin/procmail -d spam"
 60
 61# DSPAM can optionally process "plused users" (addresses in the user+detail
 62# form) by truncating the username just before the "+", so all internal
 63# processing occurs for "user", but delivery will be performed for
 64# "user+detail". This is only useful if the LDA can handle "plused users"
 65# (for example Cyrus IMAP) and when configured for LMTP delivery above
 66#
 67EnablePlusedDetail	on
 68
 69# Character to use as seperator between user names and address extensions.
 70# If you change this value then please adjust QuarantineMailbox to use the
 71# new specified character. The default is '+'.
 72#
 73PlusedCharacter	+
 74
 75# Turn this feature on if you want to force DSPAM to lowercase the "plused
 76# users" username.
 77#
 78PlusedUserLowercase	on
 79
 80# Quarantine Mailbox: DSPAM's LMTP code can send spam mail using LMTP to a
 81# "plused" mailbox (such as user+quarantine) leaving quarantine processing
 82# for retraining or deletion to be performed by the LDA and the mail client.
 83# "plused" mailboxes are supported by Cyrus IMAP and possibly other LDAs. If
 84# you don't set/change PlusedCharacter then the mailbox name must have the +
 85# since the + is the default used character.
 86#
 87QuarantineMailbox	+Junk
 88
 89# OnFail: What to do if local delivery or quarantine should fail. If set
 90# to "unlearn", DSPAM will unlearn the message prior to exiting with an
 91# un successful return code. The default option, "error" will not unlearn
 92# the message but return the appropriate error code. The unlearn option
 93# is useful on some systems where local delivery failures will cause the
 94# message to be requeued for delivery, and could result in the message
 95# being processed multiple times. During a very large failure, however,
 96# this could cause a significant load increase.
 97#
 98OnFail unlearn
 99
100# Trusted Users: Only the users specified below will be allowed to perform
101# administrative functions in DSPAM such as setting the active user and
102# accessing tools. All other users attempting to run DSPAM will be restricted;
103# their uids will be forced to match the active username and they will not be
104# able to specify delivery agent privileges or use tools.
105#
106Trust root
107Trust dspam
108Trust virtual
109Trust dovenull
110Trust mail
111Trust daemon
112Trust postfix
113
114# Debugging: Enables debugging for some or all users. IMPORTANT: DSPAM must
115# be compiled with debug support in order to use this option. DSPAM should
116# never be running in production with debug active unless you are
117# troubleshooting problems.
118#
119# DebugOpt: One or more of: process, classify, spam, fp, inoculation, corpus
120#   process     standard message processing
121#   classify    message classification using --classify
122#   spam        error correction of missed spam
123#   fp          error correction of false positives
124#   inoculation message inoculations (source=inoculation)
125#   corpus      corpusfed messages (source=corpus)
126#
127#Debug *
128#DebugOpt process
129
130# Training Mode: The default training mode to use for all operations, when
131# one has not been specified on the commandline or in the user's preferences.
132# Acceptable values are:
133#     toe     Train on Error (Only)
134#     teft    Train Everything (Trains on every message)
135#     tum     Train Until Mature (Train only tokens without enough data)
136#     notrain Do not train or store signatures (large ISP systems, post-train)
137#
138TrainingMode teft
139
140# TestConditionalTraining: By default, dspam will retrain certain errors
141# until the condition is no longer met. This usually accelerates learning.
142# Some people argue that this can increase the risk of errors, however.
143#
144TestConditionalTraining on
145
146# Features: Specify features to activate by default; can also be specified
147# on the commandline. See the documentation for a list of available features.
148# If _any_ features are specified on the commandline, these are ignored.
149#
150Feature noise
151Feature whitelist
152
153# Training Buffer: The training buffer waters down statistics during training.
154# It is designed to prevent false positives, but can also dramatically reduce
155# dspam's catch rate during initial training. This can be a number from 0
156# (no buffering) to 10 (maximum buffering). If you are paranoid about false
157# positives, you should probably enable this option.
158#
159#Feature tb=5
160
161# Algorithms: Specify the statistical algorithms to use, overriding any
162# defaults configured in the build. The options are:
163#    naive       Naive-Bayesian (All Tokens)
164#    graham      Graham-Bayesian ("A Plan for Spam")
165#    burton      Burton-Bayesian (SpamProbe)
166#    robinson    Robinson's Geometric Mean Test (Obsolete)
167#    chi-square  Fisher-Robinson's Chi-Square Algorithm
168#
169# You may have multiple algorithms active simultaneously, but it is strongly
170# recommended that you group Bayesian algorithms with other Bayesian
171# algorithms, and any use of Chi-Square remain exclusive.
172#
173# NOTE: For standard "CRM114" Markovian weighting, use 'naive', or consider
174#       using 'burton' for slightly better accuracy
175#
176# Don't mess with this unless you know what you're doing
177#
178Algorithm graham burton
179
180# Tokenizer: Specify the tokenizer to use. The tokenizer is the piece
181# responsible for parsing the message into individual tokens. Depending on
182# how many resources you are willing to trade off vs. accuracy, you may
183# choose to use a less or more detailed tokenizer:
184#   word    uniGram (single word) tokenizer
185#           Tokenizes message into single individual words/tokens
186#           example: "free" and "viagra"
187#   chain   biGram (chained tokens) tokenizer (default)
188#           Single words + chains adjacent tokens together
189#           example: "free" and "viagra" and "free viagra"
190#   sbph    Sparse Binary Polynomial Hashing tokenizer
191#           Creates sparse token patterns across sliding window of 5-tokens
192#           example: "the quick * fox jumped" and "the * * fox jumped"
193#   osb     Orthogonal Sparse biGram tokenizer
194#           Similar to SBPH, but only uses the biGrams
195#           example: "the * * fox" and "the * * * jumped"
196#
197# In general the reccomendation is to use 'osb' for new installations.
198# The default value of 'chain' remains here as not to surprise anyone upgrading
199# that has not changed from the default value.
200#
201Tokenizer osb
202
203# PValue: Specify the technique used for calculating Probability Values,
204# overriding any defaults configured in the build. These options are:
205#    bcr         Bayesian Chain Rule (Graham's Technique - "A Plan for Spam")
206#    robinson    Robinson's Technique (used in Chi-Square)
207#    markov      Markovian Weighted Technique (for Markovian discrimination)
208#
209# Unlike the "Algorithms" property, you may only have one of these defined.
210# Use of the chi-square algorithm automatically changes this to robinson.
211#
212# Don't mess with this unless you know what you're doing.
213#
214PValue bcr
215
216#
217# WebStats: Enable this if you are using the CGI, which writes .stats files
218WebStats on
219
220#
221# ImprobabilityDrive: Calculate odds-ratios for ham/spam, and add to
222# X-DSPAM-Improbability headers
223#
224ImprobabilityDrive on
225
226#
227# Preferences: Specify any preferences to set by default, unless otherwise
228# overridden by the user (see next section) or a default.prefs file.
229# If user or default.prefs are found, the user's preferences will override any
230# defaults.
231#
232Preference "trainingMode= TOE"		# { TOE | TUM | TEFT | NOTRAIN } -> default:teft
233Preference "spamAction=tag"	# { quarantine | tag | deliver } -> default:quarantine
234Preference "spamSubject="		# { string } -> default:[SPAM]
235Preference "statisticalSedation=5"	# { 0 - 10 } -> default:0
236Preference "enableBNR=on"		# { on | off } -> default:off
237Preference "enableWhitelist=on"		# { on | off } -> default:on
238Preference "signatureLocation=headers"	# { message | headers } -> default:message
239Preference "tagSpam=on"			# { on | off }
240Preference "tagNonspam=on"		# { on | off }
241Preference "showFactors=on"		# { on | off } -> default:off
242Preference "optIn=off"			# { on | off }
243Preference "optOut=off"			# { on | off }
244Preference "whitelistThreshold=10"	# { Integer } -> default:10
245Preference "makeCorpus=off"		# { on | off } -> default:off
246Preference "storeFragments=off"		# { on | off } -> default:off
247Preference "localStore="		# { on | off } -> default:username
248Preference "processorBias=on"		# { on | off } -> default:on
249Preference "fallbackDomain=off"		# { on | off } -> default:off
250Preference "trainPristine=off"		# { on | off } -> default:off
251Preference "optOutClamAV=off"		# { on | off } -> default:off
252Preference "ignoreRBLLookups=off"	# { on | off } -> default:off
253Preference "RBLInoculate=off"		# { on | off } -> default:off
254Preference "notifications=off"		# { on | off } -> default:off
255
256#
257# Overrides: Specifies the user preferences which may override configuration
258# and commandline defaults. Any other preferences supplied by an untrusted user
259# will be ignored.
260#
261AllowOverride enableBNR
262AllowOverride enableWhitelist
263AllowOverride fallbackDomain
264AllowOverride ignoreGroups
265AllowOverride ignoreRBLLookups
266AllowOverride localStore
267AllowOverride makeCorpus
268AllowOverride optIn
269AllowOverride optOut
270AllowOverride optOutClamAV
271AllowOverride processorBias
272AllowOverride RBLInoculate
273AllowOverride showFactors
274AllowOverride signatureLocation
275AllowOverride spamAction
276AllowOverride spamSubject
277AllowOverride statisticalSedation
278AllowOverride storeFragments
279AllowOverride tagNonspam
280AllowOverride tagSpam
281AllowOverride trainPristine
282AllowOverride trainingMode
283AllowOverride whitelistThreshold
284AllowOverride dailyQuarantineSummary
285AllowOverride notifications
286
287# --- PostgreSQL ---
288
289# For PgSQLServer you can Use a TCP/IP address or a socket. If your socket is
290# in /var/run/postgresql/.s.PGSQL.5432 specify just the path where the socket
291# resits (without .s.PGSQL.5432).
292
293PgSQLServer		pgsql.cmhome
294PgSQLPort		5432
295PgSQLUser		dspam
296PgSQLPass		password
297PgSQLDb			dspam
298
299# If you're running DSPAM in client/server (daemon) mode, uncomment the
300# setting below to override the default connection cache size (the number
301# of connections the server pools between all clients).
302#
303PgSQLConnectionCache	2
304
305# UIDInSignature: PgSQL supports the insertion of the user id into the DSPAM
306# signature. This allows you to create one single spam or fp alias
307# (pointing to some arbitrary user), and the uid in the signature will
308# switch to the correct user. Result: you need only one spam alias
309
310#PgSQLUIDInSignature	on
311
312# If you're using vpopmail or some other type of virtual setup and wish to
313# change the table dspam uses to perform username/uid lookups, you can over-
314# ride it below
315
316PgSQLVirtualTable		dspam_virtual_uids
317PgSQLVirtualUIDField		uid
318PgSQLVirtualUsernameField	username
319
320Notifications	off
321
322# TxtDirectory: the directory that holds the templates for notification
323# messages (see Notifications) and tagging (see tagSpam/tagNonspam).
324#
325TxtDirectory /var/db/dspam/txt
326
327# Purge configuration: Set dspam_clean purge default options, if not otherwise
328# specified on the commandline
329#
330# Purge configuration for SQL-based installations using purge.sql
331#
332PurgeSignature	off	# Specified in purge.sql
333PurgeNeutral	90
334PurgeUnused	off	# Specified in purge.sql
335PurgeHapaxes	off	# Specified in purge.sql
336PurgeHits1S	off	# Specified in purge.sql
337PurgeHits1I	off	# Specified in purge.sql
338
339#
340# Local Mail Exchangers: Used for source address tracking, tells DSPAM which
341# mail exchangers are local and therefore should be ignored in the Received:
342# header when tracking the source of an email. Note: you should use the address
343# of the host as appears between brackets [ ] in the Received header.
344# By default DSPAM is considering the following IPs always as LocalMX:
345#	10.0.0.0/8	- Private IP addresses (RFC 1918)
346#	127.0.0.0/8	- Localhost Loopback Address (RFC 1700)
347#	169.254.0.0/16	- Zeroconf / APIPA (RFC 3330)
348#	172.16.0.0/12	- Private IP addresses (RFC 1918)
349#	192.168.0.0/16	- Private IP addresses (RFC 1918)
350#
351LocalMX 127.0.0.1 198.18.0.0/20
352
353#
354# Logging: Disabling logging for users will make usage graphs unavailable to
355# them. Disabling system logging will make admin graphs unavailable.
356#
357SystemLog	on
358UserLog		on
359
360#
361# TrainPristine: for systems where the original message remains server side
362# and can therefore be presented in pristine format for retraining. This option
363# will cause DSPAM to cease all writing of signatures and DSPAM headers to the
364# message, and deliver the message in as pristine format as possible. This mode
365# REQUIRES that the original message in its pristine format (as of delivery)
366# be presented for retraining, as in the case of webmail, imap, or other
367# applications where the message is actually kept server-side during reading,
368# and is preserved. DO NOT use this switch unless the original message can be
369# presented for retraining with the ORIGINAL HEADERS and NO MODIFICATIONS.
370#
371# NOTE: You can't use this setting with dspam_trian; if you're going to use it,
372#       wait until after you train any corpora.
373#
374TrainPristine off
375
376#
377# Opt: in or out; determines DSPAM's default filtering behavior. If this value
378# is set to in, users must opt-in to filtering by dropping a .dspam file in
379# /var/dspam/opt-in/user.dspam (or if you have homedirs configured, a .dspam
380# folder in their home directory).  The default is opt-out, which means all
381# users will be filtered unless a .nodspam file is dropped in
382# /var/dspam/opt-out/user.nodspam
383#
384Opt out
385
386#
387# TrackSources: specify which (if any) source addresses to track and report
388# them to syslog (mail.info). This is useful if you're running a firewall or
389# blacklist and would like to use this information. Spam reporting also drops
390# RABL blacklist files (see http://www.nuclearelephant.com/projects/rabl/).
391#
392TrackSources spam nonspam virus
393
394#
395# ParseToHeaders: In lieu of setting up individual aliases for each user,
396# DSPAM can be configured to automatically parse the To: address for spam and
397# false positive forwards. From there, it can be configured to either set the
398# DSPAM user based on the username specified in the header and/or change the
399# training class and source accordingly. The options below can be used to
400# customize most common types of header parsing behavior to avoid the need for
401# multiple aliases, or if using LMTP, aliases entirely..
402#
403# ParseToHeader: Parse the To: headers of an incoming message. This must be
404#                set to 'on' to use either of the following features.
405#
406# ChangeModeOnParse: Automatically change the class (to spam or innocent)
407#   depending on whether spam- or notspam- was specified, and change the source
408#   to 'error'. This is convenient if you're not using aliases at all, but
409#   are delivering via LMTP.
410#
411# ChangeUserOnParse: Automatically change the username to match that specified
412#   in the To: header. For example, spam-bob@example.org will set the username
413#   to bob, ignoring any --user passed in. This may not always be desirable if
414#   you are using virtual email addresses as usernames. Options:
415#     on or user	take the portion before the @ sign only
416#     full		take everything after the initial {spam,notspam}-.
417#
418ParseToHeaders on
419ChangeModeOnParse off
420ChangeUserOnParse full
421
422#
423# Broken MTA Options: Some MTAs don't support the proper functionality
424# necessary. In these cases you can activate certain features in DSPAM to
425# compensate. 'returnCodes' causes DSPAM to return an exit code of 99 if
426# the message is spam, 0 if not, or a negative code if an error has occured.
427# Specifying 'case' causes DSPAM to force the input usernames to lowercase.
428# Specifying 'lineStripping' causes DSPAM to strip ^M's from messages passed
429# in.
430#
431#Broken returnCodes
432Broken case
433Broken lineStripping
434
435#
436# MaxMessageSize: You may specify a maximum message size for DSPAM to process.
437# If the message is larger than the maximum size, it will be delivered
438# without processing. Value is in bytes.
439#
440MaxMessageSize 5000000
441
442# --- ClamAV ---
443
444#
445# Virus Checking: If you are running clamd, DSPAM can perform stream-based
446# virus checking using TCP. Uncomment the values below to enable virus
447# checking.
448#
449# ClamAVResponse: reject (reject or drop the message with a permanent failure)
450#                 accept (accept the message and quietly drop the message)
451#                 spam   (treat as spam and quarantine/tag/whatever)
452#
453ClamAVPort		3310
454ClamAVHost		127.0.0.1
455ClamAVResponse		reject
456
457# --- CLIENT / SERVER ---
458
459#
460# Daemonized Server: If you are running DSPAM as a daemonized server using
461# --daemon, the following parameters will override the default. Use the
462# ServerPass option to set up accounts for each client machine. The DSPAM
463# server will process and deliver the message based on the parameters
464# specified. If you want the client machine to perform delivery, use
465# the --stdout option in conjunction with a local setup.
466#
467# ServerHost: Not enabling ServerHost will bind DSPAM server to all available
468# interfaces.
469#
470ServerHost		127.0.0.1
471ServerPort		2424
472ServerQueueSize	32
473ServerPID		/var/run/dspam/dspam.pid
474
475#
476# ServerMode specifies the type of LMTP server to start. This can be one of:
477#     dspam: DSPAM-proprietary DLMTP server, for communicating with dspamc
478#  standard: Standard LMTP server, for communicating with Postfix or other MTA
479#      auto: Speak both DLMTP and LMTP; auto-detect by ServerPass.IDENT
480#
481ServerMode auto
482
483# If supporting DLMTP (dspam) mode, dspam clients will require authentication
484# as they will be passing in parameters. The idents below will be used to
485# determine which clients will be speaking DLMTP, so if you will be using
486# both LMTP and DLMTP from the same host, be sure to use something other
487# than the server's hostname below (which will be sent by the MTA during a
488# standard LMTP LHLO).
489#
490#ServerPass.Relay1	"secret"
491ServerPass.client	"password"
492
493# If supporting standard LMTP mode, server parameters will need to be specified
494# here, as they will not be passed in by the mail server. The ServerIdent
495# specifies the 250 response code ident sent back to connecting clients and
496# should be set to the hostname of your server, or an alias.
497#
498# NOTE: If you specify --user in ServerParameters, the RCPT TO will be
499#       used only for delivery, and not set as the active user for processing.
500#
501ServerParameters	"--deliver=innocent, spam -d %u"
502ServerIdent		"mx.cryptomonkeys.com"
503
504# If you wish to use a local domain socket instead of a TCP socket, uncomment
505# the following. It is strongly recommended you use local domain sockets if
506# you are running the client and server on the same machine, as it eliminates
507# much of the bandwidth overhead.
508#
509#ServerDomainSocketPath	"/var/run/dspam/dspam.sock"
510
511#
512# Client Mode: If you are running DSPAM in client/server mode, uncomment and
513# set these variables. A ClientHost beginning with a / will be treated as
514# a domain socket.
515#
516#ClientHost	/var/run/dspam/dspam.sock
517#ClientIdent	"secret@Relay1"
518#
519ClientHost	127.0.0.1
520ClientPort	2424
521ClientIdent	"password@client"
522
523# ProcessorURLContext: By default, a URL context is generated for URLs, which
524# records their tokens as separate from words found in documents. To use
525# URL tokens in the same context as words, turn this feature off.
526#
527ProcessorURLContext on
528
529# ProcessorBias: Bias causes the filter to lean more toward 'innocent', and
530# usually greatly reduces false positives. It is the default behavior of
531# most Bayesian filters (including dspam).
532#
533# NOTE: You probably DONT want this if you're using Markovian Weighting, unless
534# you are paranoid about false positives.
535#
536ProcessorBias on
537
538# StripRcptDomain: Cut the domain (including the at sign) from recipients.
539# This is particularly useful if the recipient name is equal to real user
540# accounts as recipients with domains tend to cause permission issues with
541# dspam-web.
542#
543StripRcptDomain off
544
545# GroupConfig: The configuration file for groups. See the README file
546# for details on how to enable users to combine their training data to
547# get better results.
548GroupConfig /var/db/dspam/group
549
550
551IgnoreHeader Accept-Language
552IgnoreHeader Approved
553IgnoreHeader Archive
554IgnoreHeader Authentication-Results
555IgnoreHeader Cache-Post-Path
556IgnoreHeader Cancel-Key
557IgnoreHeader Cancel-Lock
558IgnoreHeader Complaints-To
559IgnoreHeader Content-Description
560IgnoreHeader Content-Disposition
561IgnoreHeader Content-ID
562IgnoreHeader Content-Language
563IgnoreHeader Content-Return
564IgnoreHeader Content-Transfer-Encoding
565IgnoreHeader Content-Type
566IgnoreHeader DKIM-Signature
567IgnoreHeader Date
568IgnoreHeader Disposition-Notification-To
569IgnoreHeader DomainKey-Signature
570IgnoreHeader Importance
571IgnoreHeader In-Reply-To
572IgnoreHeader Injection-Info
573IgnoreHeader Lines
574IgnoreHeader List-Archive
575IgnoreHeader List-Help
576IgnoreHeader List-Id
577IgnoreHeader List-Post
578IgnoreHeader List-Subscribe
579IgnoreHeader List-Unsubscribe
580IgnoreHeader Message-ID
581IgnoreHeader Message-Id
582IgnoreHeader NNTP-Posting-Date
583IgnoreHeader NNTP-Posting-Host
584IgnoreHeader Newsgroups
585IgnoreHeader OpenPGP
586IgnoreHeader Organization
587IgnoreHeader Originator
588IgnoreHeader PGP-ID
589IgnoreHeader Path
590IgnoreHeader Received
591IgnoreHeader Received-SPF
592IgnoreHeader References
593IgnoreHeader Reply-To
594IgnoreHeader Resent-Date
595IgnoreHeader Resent-From
596IgnoreHeader Resent-Message-ID
597IgnoreHeader Thread-Index
598IgnoreHeader Thread-Topic
599IgnoreHeader User-Agent
600IgnoreHeader X--MailScanner-SpamCheck
601IgnoreHeader X-AV-Scanned
602IgnoreHeader X-AVAS-Spam-Level
603IgnoreHeader X-AVAS-Spam-Score
604IgnoreHeader X-AVAS-Spam-Status
605IgnoreHeader X-AVAS-Spam-Symbols
606IgnoreHeader X-AVAS-Virus-Status
607IgnoreHeader X-AVK-Virus-Check
608IgnoreHeader X-Abuse
609IgnoreHeader X-Abuse-Contact
610IgnoreHeader X-Abuse-Info
611IgnoreHeader X-Abuse-Management
612IgnoreHeader X-Abuse-To
613IgnoreHeader X-Abuse-and-DMCA-Info
614IgnoreHeader X-Accept-Language
615IgnoreHeader X-Admission-MailScanner-SpamCheck
616IgnoreHeader X-Admission-MailScanner-SpamScore
617IgnoreHeader X-Amavis-Alert
618IgnoreHeader X-Amavis-Hold
619IgnoreHeader X-Amavis-Modified
620IgnoreHeader X-Amavis-OS-Fingerprint
621IgnoreHeader X-Amavis-PenPals
622IgnoreHeader X-Amavis-PolicyBank
623IgnoreHeader X-AntiVirus
624IgnoreHeader X-Antispam
625IgnoreHeader X-Antivirus
626IgnoreHeader X-Antivirus-Scanner
627IgnoreHeader X-Antivirus-Status
628IgnoreHeader X-Archive
629IgnoreHeader X-Assp-Spam-Prob
630IgnoreHeader X-Attention
631IgnoreHeader X-BTI-AntiSpam
632IgnoreHeader X-Barracuda
633IgnoreHeader X-Barracuda-Bayes
634IgnoreHeader X-Barracuda-Spam-Flag
635IgnoreHeader X-Barracuda-Spam-Report
636IgnoreHeader X-Barracuda-Spam-Score
637IgnoreHeader X-Barracuda-Spam-Status
638IgnoreHeader X-Barracuda-Virus-Scanned
639IgnoreHeader X-BeenThere
640IgnoreHeader X-Bogosity
641IgnoreHeader X-Brightmail-Tracker
642IgnoreHeader X-CRM114-CacheID
643IgnoreHeader X-CRM114-Status
644IgnoreHeader X-CRM114-Version
645IgnoreHeader X-CTASD-IP
646IgnoreHeader X-CTASD-RefID
647IgnoreHeader X-CTASD-Sender
648IgnoreHeader X-Cache
649IgnoreHeader X-ClamAntiVirus-Scanner
650IgnoreHeader X-Comment-To
651IgnoreHeader X-Comments
652IgnoreHeader X-Complaints
653IgnoreHeader X-Complaints-Info
654IgnoreHeader X-Complaints-To
655IgnoreHeader X-DKIM
656IgnoreHeader X-DMCA-Complaints-To
657IgnoreHeader X-DMCA-Notifications
658IgnoreHeader X-Despammed-Tracer
659IgnoreHeader X-ELTE-SpamCheck
660IgnoreHeader X-ELTE-SpamCheck-Details
661IgnoreHeader X-ELTE-SpamScore
662IgnoreHeader X-ELTE-SpamVersion
663IgnoreHeader X-ELTE-VirusStatus
664IgnoreHeader X-Enigmail-Supports
665IgnoreHeader X-Enigmail-Version
666IgnoreHeader X-Evolution-Source
667IgnoreHeader X-Extra-Info
668IgnoreHeader X-FSFE-MailScanner
669IgnoreHeader X-FSFE-MailScanner-From
670IgnoreHeader X-Face
671IgnoreHeader X-Fellowship-MailScanner
672IgnoreHeader X-Fellowship-MailScanner-From
673IgnoreHeader X-Forwarded
674IgnoreHeader X-GMX-Antispam
675IgnoreHeader X-GMX-Antivirus
676IgnoreHeader X-GPG-Fingerprint
677IgnoreHeader X-GPG-Key-ID
678IgnoreHeader X-GPS-DegDec
679IgnoreHeader X-GPS-MGRS
680IgnoreHeader X-GWSPAM
681IgnoreHeader X-Gateway
682IgnoreHeader X-Greylist
683IgnoreHeader X-HTMLM
684IgnoreHeader X-HTMLM-Info
685IgnoreHeader X-HTMLM-Score
686IgnoreHeader X-HTTP-Posting-Host
687IgnoreHeader X-HTTP-UserAgent
688IgnoreHeader X-HTTP-Via
689IgnoreHeader X-Headers-End
690IgnoreHeader X-ID
691IgnoreHeader X-IMAIL-SPAM-STATISTICS
692IgnoreHeader X-IMAIL-SPAM-URL-DBL
693IgnoreHeader X-IMAIL-SPAM-VALFROM
694IgnoreHeader X-IMAIL-SPAM-VALHELO
695IgnoreHeader X-IMAIL-SPAM-VALREVDNS
696IgnoreHeader X-Info
697IgnoreHeader X-IronPort-Anti-Spam-Filtered
698IgnoreHeader X-IronPort-Anti-Spam-Result
699IgnoreHeader X-KSV-Antispam
700IgnoreHeader X-Kaspersky-Antivirus
701IgnoreHeader X-MDAV-Processed
702IgnoreHeader X-MDRemoteIP
703IgnoreHeader X-MDaemon-Deliver-To
704IgnoreHeader X-MIE-MailScanner-SpamCheck
705IgnoreHeader X-MIMEOLE
706IgnoreHeader X-MIMETrack
707IgnoreHeader X-MMS-Spam-Filter-ID
708IgnoreHeader X-MS-Exchange-Forest-RulesExecuted
709IgnoreHeader X-MS-Exchange-Organization-Antispam-Report
710IgnoreHeader X-MS-Exchange-Organization-AuthAs
711IgnoreHeader X-MS-Exchange-Organization-AuthDomain
712IgnoreHeader X-MS-Exchange-Organization-AuthMechanism
713IgnoreHeader X-MS-Exchange-Organization-AuthSource
714IgnoreHeader X-MS-Exchange-Organization-Journal-Report
715IgnoreHeader X-MS-Exchange-Organization-Original-Scl
716IgnoreHeader X-MS-Exchange-Organization-Original-Sender
717IgnoreHeader X-MS-Exchange-Organization-OriginalArrivalTime
718IgnoreHeader X-MS-Exchange-Organization-OriginalSize
719IgnoreHeader X-MS-Exchange-Organization-PCL
720IgnoreHeader X-MS-Exchange-Organization-Quarantine
721IgnoreHeader X-MS-Exchange-Organization-SCL
722IgnoreHeader X-MS-Exchange-Organization-SenderIdResult
723IgnoreHeader X-MS-Has-Attach
724IgnoreHeader X-MS-TNEF-Correlator
725IgnoreHeader X-MSMail-Priority
726IgnoreHeader X-MailScanner
727IgnoreHeader X-MailScanner-Information
728IgnoreHeader X-MailScanner-SpamCheck
729IgnoreHeader X-Mailer
730IgnoreHeader X-Mailman-Version
731IgnoreHeader X-Mlf-Spam-Status
732IgnoreHeader X-NAI-Spam-Checker-Version
733IgnoreHeader X-NAI-Spam-Flag
734IgnoreHeader X-NAI-Spam-Level
735IgnoreHeader X-NAI-Spam-Report
736IgnoreHeader X-NAI-Spam-Route
737IgnoreHeader X-NAI-Spam-Rules
738IgnoreHeader X-NAI-Spam-Score
739IgnoreHeader X-NAI-Spam-Threshold
740IgnoreHeader X-NEWT-spamscore
741IgnoreHeader X-NNTP-Posting-Date
742IgnoreHeader X-NNTP-Posting-Host
743IgnoreHeader X-NetcoreISpam1-ECMScanner
744IgnoreHeader X-NetcoreISpam1-ECMScanner-From
745IgnoreHeader X-NetcoreISpam1-ECMScanner-Information
746IgnoreHeader X-NetcoreISpam1-ECMScanner-SpamCheck
747IgnoreHeader X-NetcoreISpam1-ECMScanner-SpamScore
748IgnoreHeader X-Newsreader
749IgnoreHeader X-Newsserver
750IgnoreHeader X-No-Archive
751IgnoreHeader X-No-Spam
752IgnoreHeader X-OSBF-Lua-Score
753IgnoreHeader X-OWM-SpamCheck
754IgnoreHeader X-OWM-VirusCheck
755IgnoreHeader X-Olypen-Virus
756IgnoreHeader X-Orig-Path
757IgnoreHeader X-OriginalArrivalTime
758IgnoreHeader X-Originating-IP
759IgnoreHeader X-PAA-AntiVirus
760IgnoreHeader X-PAA-AntiVirus-Message
761IgnoreHeader X-PGP-Fingerprint
762IgnoreHeader X-PGP-Hash
763IgnoreHeader X-PGP-ID
764IgnoreHeader X-PGP-Key
765IgnoreHeader X-PGP-Key-Fingerprint
766IgnoreHeader X-PGP-KeyID
767IgnoreHeader X-PGP-Sig
768IgnoreHeader X-PIRONET-NDH-MailScanner-SpamCheck
769IgnoreHeader X-PIRONET-NDH-MailScanner-SpamScore
770IgnoreHeader X-PMX
771IgnoreHeader X-PMX-Version
772IgnoreHeader X-PN-SPAMFiltered
773IgnoreHeader X-Posting-Agent
774IgnoreHeader X-Posting-ID
775IgnoreHeader X-Posting-IP
776IgnoreHeader X-Priority
777IgnoreHeader X-Proofpoint-Spam-Details
778IgnoreHeader X-Qmail-Scanner-1.25st
779IgnoreHeader X-Quarantine-ID
780IgnoreHeader X-RAV-AntiVirus
781IgnoreHeader X-RITmySpam
782IgnoreHeader X-RITmySpam-IP
783IgnoreHeader X-RITmySpam-Spam
784IgnoreHeader X-Rc-Spam
785IgnoreHeader X-Rc-Virus
786IgnoreHeader X-Received-Date
787IgnoreHeader X-RedHat-Spam-Score
788IgnoreHeader X-RedHat-Spam-Warning
789IgnoreHeader X-RegEx
790IgnoreHeader X-RegEx-Score
791IgnoreHeader X-Rocket-Spam
792IgnoreHeader X-SA-GROUP
793IgnoreHeader X-SA-RECEIPTSTATUS
794IgnoreHeader X-STA-NotSpam
795IgnoreHeader X-STA-Spam
796IgnoreHeader X-Scam-grey
797IgnoreHeader X-Scanned-By
798IgnoreHeader X-Sender
799IgnoreHeader X-SenderID
800IgnoreHeader X-Sohu-Antivirus
801IgnoreHeader X-Spam
802IgnoreHeader X-Spam-ASN
803IgnoreHeader X-Spam-Check
804IgnoreHeader X-Spam-Checked-By
805IgnoreHeader X-Spam-Checker
806IgnoreHeader X-Spam-Checker-Version
807IgnoreHeader X-Spam-Clean
808IgnoreHeader X-Spam-DCC
809IgnoreHeader X-Spam-Details
810IgnoreHeader X-Spam-Filter
811IgnoreHeader X-Spam-Filtered
812IgnoreHeader X-Spam-Flag
813IgnoreHeader X-Spam-Level
814IgnoreHeader X-Spam-OrigSender
815IgnoreHeader X-Spam-Pct
816IgnoreHeader X-Spam-Prev-Subject
817IgnoreHeader X-Spam-Processed
818IgnoreHeader X-Spam-Pyzor
819IgnoreHeader X-Spam-Rating
820IgnoreHeader X-Spam-Report
821IgnoreHeader X-Spam-Scanned
822IgnoreHeader X-Spam-Score
823IgnoreHeader X-Spam-Status
824IgnoreHeader X-Spam-Tagged
825IgnoreHeader X-Spam-Tests
826IgnoreHeader X-Spam-Tests-Failed
827IgnoreHeader X-Spam-Virus
828IgnoreHeader X-Spam-Warning
829IgnoreHeader X-Spam-detection-level
830IgnoreHeader X-SpamAssassin-Clean
831IgnoreHeader X-SpamAssassin-Warning
832IgnoreHeader X-SpamBouncer
833IgnoreHeader X-SpamCatcher-Score
834IgnoreHeader X-SpamCop-Checked
835IgnoreHeader X-SpamCop-Disposition
836IgnoreHeader X-SpamCop-Whitelisted
837IgnoreHeader X-SpamDetected
838IgnoreHeader X-SpamInfo
839IgnoreHeader X-SpamPal
840IgnoreHeader X-SpamPal-Timeout
841IgnoreHeader X-SpamReason
842IgnoreHeader X-SpamScore
843IgnoreHeader X-SpamTest-Categories
844IgnoreHeader X-SpamTest-Info
845IgnoreHeader X-SpamTest-Method
846IgnoreHeader X-SpamTest-Status
847IgnoreHeader X-SpamTest-Version
848IgnoreHeader X-Spamadvice
849IgnoreHeader X-Spamarrest-noauth
850IgnoreHeader X-Spamarrest-speedcode
851IgnoreHeader X-Spambayes-Classification
852IgnoreHeader X-Spamcount
853IgnoreHeader X-Spamsensitivity
854IgnoreHeader X-TERRACE-SPAMMARK
855IgnoreHeader X-TERRACE-SPAMRATE
856IgnoreHeader X-TM-AS-Category-Info
857IgnoreHeader X-TM-AS-MatchedID
858IgnoreHeader X-TM-AS-Product-Ver
859IgnoreHeader X-TM-AS-Result
860IgnoreHeader X-TMWD-Spam-Summary
861IgnoreHeader X-TNEFEvaluated
862IgnoreHeader X-Text-Classification
863IgnoreHeader X-Text-Classification-Data
864IgnoreHeader X-Trace
865IgnoreHeader X-UCD-Spam-Score
866IgnoreHeader X-User-Agent
867IgnoreHeader X-User-ID
868IgnoreHeader X-User-System
869IgnoreHeader X-Virus-Check
870IgnoreHeader X-Virus-Checked
871IgnoreHeader X-Virus-Checker-Version
872IgnoreHeader X-Virus-Scan
873IgnoreHeader X-Virus-Scanned
874IgnoreHeader X-Virus-Scanner
875IgnoreHeader X-Virus-Scanner-Result
876IgnoreHeader X-Virus-Status
877IgnoreHeader X-VirusChecked
878IgnoreHeader X-Virusscan
879IgnoreHeader X-WSS-ID
880IgnoreHeader X-WinProxy-AntiVirus
881IgnoreHeader X-WinProxy-AntiVirus-Message
882IgnoreHeader X-Yandex-Forward
883IgnoreHeader X-Yandex-Front
884IgnoreHeader X-Yandex-Spam
885IgnoreHeader X-Yandex-TimeMark
886IgnoreHeader X-cid
887IgnoreHeader X-iHateSpam-Checked
888IgnoreHeader X-iHateSpam-Quarantined
889IgnoreHeader X-policyd-weight
890IgnoreHeader X-purgate
891IgnoreHeader X-purgate-Ad
892IgnoreHeader X-purgate-ID
893IgnoreHeader X-sgxh1
894IgnoreHeader X-to-viruscore
895IgnoreHeader Xref
896IgnoreHeader acceptlanguage
897IgnoreHeader thread-index
898IgnoreHeader x-uscspam
899
900## EOF

Starting the service

Now we can start dspam(1) with:

1sudo service dspam start

You can check that its running with ps(1) (but don’t show all the PostgreSQL connections):

1[louisk@mx louisk 101 ]$ ps ax | grep dspam | grep -v postgres
2 742 v0- S    0:00.03 /usr/local/bin/dspam --daemon
3[louisk@mx louisk 102 ]$

You can also use sockstat(1) to show which sockets dspam is listening on:

1[louisk@mx louisk 102 ]$ sockstat -4l | grep dspam
2root     dspam      742   6  tcp4   127.0.0.1:10024       *:*
3[louisk@mx louisk 103 ]$

Training

Periodically, you will want to clean up the dspam database. Helpfully, there is a purge.sql script in ‘/usr/local/share/examples/dspam/pgsql’. I put a wrapper around it and drove it from cron:

1@daily                                  root    /usr/local/bin/dspam-cleanup.sh
1cat /usr/local/bin/dspam-cleanup.sh
2#!/bin/sh
3#
4# run sql queries to clean old/unused tokens out of PostgreSQL
5/usr/local/bin/psql -U pgsql dspam < /usr/local/etc/dspam-purge.sql
6# clean old logs
7/usr/local/bin/dspam_logrotate -a 30 -d /var/db/dspam/data

Testing

If you need to download some spam (do you want to do this very often?), you can get some here .

1cat /path/to/mail_corpus_file | dspamc --client --user user@dom.tld --class=spam --source=corpus --deliver=summary
2X-DSPAM-Result: user@dom.tld; result="Spam"; class="Spam"; probability=1.0000; confidence=1.00; signature=N/A

If you ask dspam to classify this message again, it may still consider it “innocent”, but now it won’t be as confident. Compare the confidence number to the output from before training the message:

1cat /path/to/mail_corpus_file | dspam --user user@dom.tld --classify
2X-DSPAM-Result: user@dom.tld; result="Innocent"; class="Innocent"; probability=0.7127; confidence=0.75; signature=N/A

If you need to enable debugging (dspam must be built with debug option), you can do

1sudo sysrc dspam_debug=YES

Logs are in /var/log/dspam. Debug knobs are in dspam.conf.

Configuring Dovecot

Dovecot is a secure and scalable POP/IMAP server. It supports the relevant internet standards, offers storage via maildir, and is quite responsive under load. Dovecot’s job is to accept mail from Postfix, and cause it to be available when users connect via POP or IMAP. Documentation for Dovecot .

I’m only providing the output from ‘doveconf -n’. You can either put these directives into a dovecot.conf file, or you can put them into the more modern conf.d/*.conf files. The outcome is the same, and ‘doveconf -n’ output should also be the same. Should you wish to compare, you can put the bits into the conf.d/*.conf files, copy them all into a dovecot.conf file, start dovecot, and do a:

1diff -u `doveconf -n` dovecot.conf

If things are correct, you should get a prompt back and no differences listed.

Here is the output from ‘doveconf -n’:

  1# 2.2.27 (c0f36b0): /usr/local/etc/dovecot/dovecot.conf
  2# Pigeonhole version 0.4.16 (fed8554)
  3# OS: FreeBSD 11.0-RELEASE-p2 amd64
  4auth_socket_path = /var/run/dovecot/auth-userdb
  5debug_log_path = /var/log/dovecot-debug.log
  6first_valid_gid = 125
  7first_valid_uid = 125
  8hostname = mail.cryptomonkeys.org
  9imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
 10last_valid_gid = 125
 11last_valid_uid = 125
 12lmtp_save_to_detail_mailbox = yes
 13mail_gid = 125
 14mail_home = /usr/local/virtual/%d/%n/
 15mail_location = maildir:~/Maildir
 16mail_privileged_group = postfix
 17mail_uid = 125
 18mailbox_list_index = yes
 19managesieve_notify_capability = mailto
 20managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart
 21namespace inbox {
 22  inbox = yes
 23  location =
 24  mailbox All {
 25    auto = subscribe
 26    special_use = \All
 27  }
 28  mailbox Archive {
 29    auto = subscribe
 30    special_use = \Archive
 31  }
 32  mailbox Drafts {
 33    auto = subscribe
 34    special_use = \Drafts
 35  }
 36  mailbox Flagged {
 37    auto = subscribe
 38    special_use = \Flagged
 39  }
 40  mailbox Junk {
 41    auto = create
 42    autoexpunge = 30 days
 43    special_use = \Junk
 44  }
 45  mailbox Notes {
 46    auto = subscribe
 47  }
 48  mailbox Sent {
 49    auto = subscribe
 50    special_use = \Sent
 51  }
 52  mailbox Trash {
 53    auto = subscribe
 54    special_use = \Trash
 55  }
 56  prefix =
 57}
 58passdb {
 59  args = /usr/local/etc/dovecot/dovecot-sql.conf.ext
 60  driver = sql
 61}
 62plugin {
 63  home = /usr/local/virtual/%d/%n/
 64  mailbox_alias_new = Sent Messages
 65  mailbox_alias_new2 = Sent Items
 66  mailbox_alias_new3 = Deleted Items
 67  mailbox_alias_old = Sent
 68  mailbox_alias_old2 = Sent
 69  mailbox_alias_old3 = Trash
 70  mailbox_alias_old4 = Deleted Messages
 71  sieve = /usr/local/virtual/%d/%n/dovecot.sieve
 72  sieve_dir = /usr/local/virtual/%d/%n/
 73  sieve_global_dir = /usr/local/virtual/sieve
 74  sieve_global_path = /usr/local/virtual/sieve/globalfilter.sieve
 75  stats_refresh = 30 secs
 76  stats_track_cmds = yes
 77}
 78pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
 79pop3_enable_last = yes
 80postmaster_address = postmaster@cryptomonkeys.org
 81protocols = imap pop3 lmtp sieve
 82quota_full_tempfail = yes
 83sendmail_path = /usr/local/sbin/sendmail
 84service auth {
 85  unix_listener /var/spool/postfix/private/auth {
 86    group = postfix
 87    mode = 0666
 88    user = postfix
 89  }
 90  unix_listener auth-userdb {
 91    group = postfix
 92    mode = 0777
 93    user = postfix
 94  }
 95}
 96service imap-login {
 97  client_limit = 10
 98  inet_listener imap {
 99    port = 0
100  }
101  inet_listener imaps {
102    port = 993
103    ssl = yes
104  }
105  process_min_avail = 4
106  service_count = 1
107  vsz_limit = 256 M
108}
109service lmtp {
110  inet_listener lmtp {
111    address = 127.0.0.1 ::1
112    port = 24
113  }
114  user = postfix
115}
116service managesieve-login {
117  inet_listener sieve {
118    port = 4190
119  }
120  process_min_avail = 0
121  service_count = 1
122  vsz_limit = 64 M
123}
124service managesieve {
125  process_limit = 1024
126}
127service pop3-login {
128  client_limit = 10
129  inet_listener pop3 {
130    port = 0
131  }
132  inet_listener pop3s {
133    port = 995
134    ssl = yes
135  }
136  process_limit = 128
137  process_min_avail = 2
138  service_count = 1
139  vsz_limit = 256 M
140}
141service stats {
142  fifo_listener stats-mail {
143    mode = 0600
144    user = postfix
145  }
146}
147ssl_ca = </usr/local/etc/ssl/cacert.pem
148ssl_cert = </usr/local/etc/ssl/server.crt
149ssl_cipher_list = EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4
150ssl_dh_parameters_length = 2048
151ssl_key =  # hidden, use -P to show it
152ssl_parameters_regenerate = 1 weeks
153ssl_prefer_server_ciphers = yes
154ssl_protocols = !SSLv2 !SSLv3
155userdb {
156  args = /usr/local/etc/dovecot/dovecot-sql.conf.ext
157  driver = sql
158}
159protocol lmtp {
160  mail_plugins = " sieve"
161}
162protocol lda {
163  mail_plugins = " sieve"
164}
165protocol imap {
166  mail_max_userip_connections = 20
167  mail_plugins = " stats expire mail_log notify quota trash imap_quota"
168}
169protocol pop3 {
170  mail_max_userip_connections = 10
171}

Tightening things up a little

I’ve tightened security a little from the defaults. I’ve removed the SSLv2, and SSLv3 capabilities completely. I’ve Limited the ciphers to only those on the HIGH list. If you want to know more, check out the openssl docs on ciphers . Additionally, I added a dhparam with a length of 2048 bits. Dovecot generates this key the first time you start it after adding this directive, so you don’t have to worry about having a dhparam that is shared with anybody else. Probability is very low.

 1# This file is commonly accessed via passdb {} or userdb {} section in
 2# conf.d/auth-sql.conf.ext
 3
 4driver = pgsql
 5connect = host=localhost dbname=postfix user=postfix password=password
 6default_pass_scheme = MD5
 7user_query = \
 8    SELECT '/usr/local/virtual/'||maildir AS home, '*:bytes='||quota AS quota_rule \
 9    FROM mailbox WHERE username = '%u' AND active = TRUE
10password_query = \
11    SELECT '/usr/local/virtual/'||maildir AS userdb_home, \
12        username AS user, password, '*:bytes='||quota AS userdb_quota_rule \
13    FROM mailbox WHERE username = '%u' AND active = TRUE
14iterate_query = SELECT username AS user FROM mailbox

Start up dovecot

With all this in place, we should be able to add the

1sudo sysrc dovecot_enable=YES

entry to /etc/rc.conf, and then do:

1sudo service dovecot start

We can check our success with ps(1):

 1[root@mx etc 122 ]$ ps ax | grep dove
 2 3641  -  Is     0:00.03 /usr/local/sbin/dovecot -c /usr/local/etc/dovecot/dovecot.conf
 3 3642  -  I      0:00.02 dovecot/pop3-login
 4 3643  -  I      0:00.01 dovecot/imap-login
 5 3644  -  I      0:00.00 dovecot/anvil
 6 3645  -  I      0:00.01 dovecot/log
 7 3647  -  I      0:00.03 dovecot/pop3-login
 8 3648  -  I      0:00.01 dovecot/imap-login
 9 3649  -  I      0:00.09 dovecot/imap-login
10 3650  -  I      0:00.00 dovecot/imap-login
11 3651  -  I      0:00.08 dovecot/config
12 3653  -  I      0:00.02 dovecot/auth
13 3657  -  I      0:00.00 dovecot/ssl-params
14[root@mx etc 123 ]$

and with sockstat(1) (both IPv4 and IPv6, if you don’t want to check IPv6, omit the ‘6’):

 1[root@mx dovecot 120 ]$ sockstat -46l | grep dove
 2dovenull imap-login 2733  7  tcp4   *:993                 *:*
 3dovenull imap-login 2733  8  tcp6   *:993                 *:*
 4dovenull imap-login 2732  7  tcp4   *:993                 *:*
 5dovenull imap-login 2732  8  tcp6   *:993                 *:*
 6dovenull imap-login 2731  7  tcp4   *:993                 *:*
 7dovenull imap-login 2731  8  tcp6   *:993                 *:*
 8dovenull pop3-login 2729  7  tcp4   *:995                 *:*
 9dovenull pop3-login 2729  8  tcp6   *:995                 *:*
10dovenull imap-login 2725  7  tcp4   *:993                 *:*
11dovenull imap-login 2725  8  tcp6   *:993                 *:*
12dovenull pop3-login 2724  7  tcp4   *:995                 *:*
13dovenull pop3-login 2724  8  tcp6   *:995                 *:*
14root     dovecot    2723  15 tcp4   *:4190                *:*
15root     dovecot    2723  16 tcp6   *:4190                *:*
16root     dovecot    2723  26 tcp4   *:995                 *:*
17root     dovecot    2723  27 tcp6   *:995                 *:*
18root     dovecot    2723  40 tcp4   *:993                 *:*
19root     dovecot    2723  41 tcp6   *:993                 *:*
20[root@mx dovecot 121 ]$

Configuring Postfix

Postfix is a popular and secure SMTP server. Its extensible, flexible, and light on resources. Many people like it because its config file isn’t written in M4. Documentation for Postfix here .

Originally I’d used v2 because it appeared that some of the patches necessary weren’t available for v3, but it turns out those patches are not necessary with this architecture, so yey!

Enable postfix (and disable sendmail) with sysrc.

1sudo sysrc postfix_enable=YES
2sudo sysrc sendmail_enable=NONE

Disable the sendmail periodic scripts. If /etc/periodic.conf doesn’t exist, create it and add the following contents:

1daily_clean_hoststat_enable="NO"
2daily_status_mail_rejects_enable="NO"
3daily_status_include_submit_mailq="NO"
4daily_submit_queuerun="NO"

Most of the config directives will go in the main.cf (/usr/local/etc/postfix). When you dump the config with postconf, those are the settings it looks at.

Here is the output from ‘postconf -n’ (it can be dumped straight into main.cf if you wish):

  1alias_maps = hash:${config_directory}/aliases
  2allow_mail_to_commands = alias
  3body_checks = pcre:${config_directory}/body_checks
  4broken_sasl_auth_clients = yes
  5command_directory = /usr/local/sbin
  6compatibility_level = 2
  7config_directory = /usr/local/etc/postfix
  8daemon_directory = /usr/local/libexec/postfix
  9data_directory = /var/db/postfix
 10delay_warning_time = 5d
 11disable_vrfy_command = yes
 12header_checks = pcre:${config_directory}/header_checks
 13html_directory = /usr/local/share/doc/postfix
 14inet_protocols = ipv4, ipv6
 15lmtp_header_checks = pcre:${config_directory}/lmtp_header_checks
 16mail_owner = postfix
 17mailbox_size_limit = 536870912
 18mailq_path = /usr/local/bin/mailq
 19manpage_directory = /usr/local/man
 20maximal_queue_lifetime = 8d
 21message_size_limit = 33554432
 22mydestination = $myorigin, localhost.$mydomain, localhost
 23mydomain = cryptomonkeys.com
 24myhostname = mail.cryptomonkeys.com
 25mynetworks = 192.168.0.0/20, 127.0.0.0/8, [::1]/128, [fe80::]/10
 26myorigin = $myhostname
 27nested_header_checks =
 28newaliases_path = /usr/local/bin/newaliases
 29postscreen_access_list = permit_mynetworks, cidr:$config_directory/postscreen_access.cidr
 30postscreen_bare_newline_action = ignore
 31postscreen_bare_newline_enable = yes
 32postscreen_bare_newline_ttl = 30d
 33postscreen_blacklist_action = drop
 34postscreen_cache_cleanup_interval = 12h
 35postscreen_cache_map = btree:$data_directory/postscreen_cache
 36postscreen_cache_retention_time = 7d
 37postscreen_disable_vrfy_command = $disable_vrfy_command
 38postscreen_dnsbl_action = enforce
 39postscreen_dnsbl_reply_map = pcre:$config_directory/postscreen_dnsbl_reply_map.pcre
 40postscreen_dnsbl_sites = zen.spamhaus.org*3 b.barracudacentral.org*2 bl.spameatingmonkey.net*2 bl.spamcop.net dnsbl.sorbs.net psbl.surriel.com bl.mailspike.net swl.spamhaus.org*-4 list.dnswl.org=127.[0..255].[0..255].0*-2 list.dnswl.org=127.[0..255].[0..255].1*-3 list.dnswl.org=127.[0..255].[0..255].[2..255]*-4
 41postscreen_dnsbl_threshold = 3
 42postscreen_dnsbl_ttl = 1h
 43postscreen_dnsbl_whitelist_threshold = -1
 44postscreen_greet_action = enforce
 45postscreen_greet_banner = $smtpd_banner
 46postscreen_greet_ttl = 1d
 47postscreen_greet_wait = ${stress?2}${stress:6}s
 48postscreen_helo_required = $smtpd_helo_required
 49postscreen_non_smtp_command_action = drop
 50postscreen_non_smtp_command_enable = yes
 51postscreen_non_smtp_command_ttl = 30d
 52postscreen_pipelining_enable = yes
 53postscreen_whitelist_interfaces = static:all
 54proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $virtual_mailbox_limit_maps
 55proxy_write_maps = $smtp_sasl_auth_cache_name $lmtp_sasl_auth_cache_name $address_verify_map $postscreen_cache_map
 56queue_directory = /var/spool/postfix
 57readme_directory = /usr/local/share/doc/postfix
 58recipient_delimiter = +
 59relay_domains = proxy:pgsql:${config_directory}/pgsql_relay_domains_maps.cf
 60sample_directory = /usr/local/etc/postfix
 61sendmail_path = /usr/local/sbin/sendmail
 62setgid_group = maildrop
 63smtp_destination_concurrency_limit = 20
 64smtp_header_checks = pcre:${config_directory}/smtp_header_checks
 65smtp_helo_timeout = 30s
 66smtp_sasl_auth_enable = no
 67smtp_tls_loglevel = 1
 68smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
 69smtp_tls_note_starttls_offer = yes
 70smtp_tls_policy_maps = hash:${config_directory}/tls_policy
 71smtp_tls_protocols = !SSLv2, !SSLv3
 72smtp_tls_security_level = may
 73smtp_use_tls = yes
 74smtpd_banner = $myhostname ESMTP Sendmail 8.10 (Solaris 2.6)
 75smtpd_client_connection_count_limit = 5
 76smtpd_client_connection_rate_limit = 10
 77smtpd_data_restrictions = reject_unauth_pipelining, reject_multi_recipient_bounce, permit
 78smtpd_end_of_data_restrictions = check_policy_service inet:127.0.0.1:10031
 79smtpd_error_sleep_time = 0
 80smtpd_hard_error_limit = 10
 81smtpd_helo_required = yes
 82smtpd_recipient_limit = 50
 83smtpd_recipient_restrictions = permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3], permit_sasl_authenticated, permit_mynetworks, permit_auth_destination, check_policy_service inet:127.0.0.1:10031, check_client_access hash:${config_directory}/access, check_recipient_access hash:${config_directory}/recipient_access, check_recipient_access hash:${config_directory}/sender_access, warn_if_reject reject_unknown_recipient_domain, warn_if_reject reject_non_fqdn_recipient, warn_if_reject reject_unknown_client, warn_if_reject reject_non_fqdn_sender, warn_if_reject reject_non_fqdn_hostname, reject_unverified_recipient, reject_invalid_hostname, reject_unknown_recipient_domain, reject_unlisted_recipient, reject_unverified_recipient, reject_unknown_hostname, reject_unauth_destination, permit
 84smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
 85smtpd_sasl_auth_enable = yes
 86smtpd_sasl_authenticated_header = no
 87smtpd_sasl_local_domain = $myhostname
 88smtpd_sasl_path = private/auth
 89smtpd_sasl_security_options = noanonymous
 90smtpd_sasl_type = dovecot
 91smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, check_client_access hash:${config_directory}/access, pcre:/usr/local/etc/postfix/rejected_domains, warn_if_reject reject_non_fqdn_sender, reject_unknown_client, reject_unknown_sender_domain, permit
 92smtpd_soft_error_limit = 5
 93smtpd_timeout = 30s
 94smtpd_tls_CAfile = /usr/local/etc/ssl/cacert.pem
 95smtpd_tls_auth_only = yes
 96smtpd_tls_cert_file = /usr/local/etc/ssl/server.crt
 97smtpd_tls_dh1024_param_file = /usr/local/etc/ssl/dh2048.pem
 98smtpd_tls_dh512_param_file = /usr/local/etc/ssl/dh512.pem
 99smtpd_tls_eecdh_grade = strong
100smtpd_tls_key_file = /usr/local/etc/ssl/server.key
101smtpd_tls_loglevel = 1
102smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
103smtpd_tls_protocols = !SSLv2, !SSLv3
104smtpd_tls_received_header = yes
105smtpd_tls_security_level = may
106smtpd_tls_session_cache_timeout = 3600s
107smtpd_use_tls = yes
108tls_eecdh_strong_curve = prime256v1
109tls_eecdh_ultra_curve = secp384r1
110tls_random_source = dev:/dev/random
111transport_maps = hash:${config_directory}/transport
112unknown_local_recipient_reject_code = 550
113virtual_alias_maps = proxy:pgsql:${config_directory}/pgsql_virtual_alias_maps.cf
114virtual_gid_maps = static:125
115virtual_mailbox_base = /usr/local/virtual
116virtual_mailbox_domains = proxy:pgsql:${config_directory}/pgsql_virtual_domains_maps.cf
117virtual_mailbox_limit = 536870912
118virtual_mailbox_limit_maps = proxy:pgsql:${config_directory}/pgsql_virtual_mailbox_limit_maps.cf
119virtual_mailbox_maps = proxy:pgsql:${config_directory}/pgsql_virtual_mailbox_maps.cf
120virtual_minimum_uid = 125
121virtual_transport = lmtp:127.0.0.1:2424
122virtual_uid_maps = static:125

I’ve done a number of things, picked up over the years. A few things of note:

  • Again, I’ve disabled SSLv2, and SSLv3 for both SMTP and SMTPD (handled separately).
  • All the ‘proxy:pgsql’ statements are where postfix will do lookups against information in our PostgreSQL database (things like domains, email addresses, aliases, etc.
  • Postscreen is setup to query a number of DNSBL (DNS Black List) and then weigh the answer accordingly. If there are too many hits, the connection is rejected. THis is nice because it keeps a single DNSBL from blocking a connection (sometimes a server is added to a list mistakenly, or because there was a problem, but its been fixed). if you’re not familiar with Postscreen, you can read about it here

The master.cf (also in /usr/local/etc/postfix) controls where and how postfix communicates with other programs. For example, if you want it to listen on a certain port, such as TCP/465, you need to define the ‘smtps’ entry you see below.

 1# master.cf
 2smtpd     pass  -       -       n       -       -       smtpd
 3
 4submission inet n       -       n       -       -       smtpd
 5  -o smtpd_tls_security_level=encrypt
 6  -o smtpd_enforce_tls=yes
 7  -o smtpd_sasl_auth_enable=yes
 8  -o smtpd_reject_unlisted_recipient=no
 9  -o smtpd_helo_restrictions=
10  -o smtpd_sender_restrictions=
11  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
12  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject
13  -o milter_macro_daemon_name=ORIGINATING
14
15smtps     inet  n       -       n       -       -       smtpd
16  -o smtpd_tls_wrappermode=yes
17  -o smtpd_sasl_auth_enable=yes
18  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
19  -o milter_macro_daemon_name=ORIGINATING
20
21127.0.0.1:10026 inet  n -       n       -       -        smtpd
22    -o content_filter=
23    -o receive_override_options=no_address_mappings,no_unknown_recipient_checks,no_header_body_checks
24    -o smtpd_helo_restrictions=
25    -o smtpd_client_restrictions=
26    -o smtpd_sender_restrictions=
27    -o smtpd_recipient_restrictions=permit_mynetworks,reject
28    -o mynetworks=127.0.0.0/8
29    -o smtpd_authorized_xforward_hosts=127.0.0.0/8
30
31smtp-amavis     unix    -       -       -       -       2       smtp
32        -o smtp_data_done_timeout=1200
33        -o smtp_send_xforward_command=yes
34        -o disable_dns_lookups=yes
35        -o max_use=20
36
37127.0.0.1:10025 inet    n       -       -       -       -       smtpd
38        -o content_filter=
39        -o local_recipient_maps=
40        -o relay_recipient_maps=
41        -o smtpd_restriction_classes=
42        -o smtpd_delay_reject=no
43        -o smtpd_client_restrictions=permit_mynetworks,reject
44        -o smtpd_helo_restrictions=
45        -o smtpd_sender_restrictions=
46        -o smtpd_recipient_restrictions=permit_mynetworks,reject
47        -o smtpd_data_restrictions=reject_unauth_pipelining
48        -o smtpd_end_of_data_restrictions=
49        -o mynetworks=127.0.0.0/8
50        -o smtpd_error_sleep_time=0
51        -o smtpd_soft_error_limit=1001
52        -o smtpd_hard_error_limit=1000
53        -o smtpd_client_connection_count_limit=0
54        -o smtpd_client_connection_rate_limit=0
55        -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
56
57pickup    unix  n       -       n       60      1       pickup
58	-o content_filter=
59	-o receive_override_options=no_header_body_checks
60cleanup   unix  n       -       n       -       0       cleanup
61qmgr      unix  n       -       n       300     1       qmgr
62tlsmgr    unix  -       -       n       1000?   1       tlsmgr
63rewrite   unix  -       -       n       -       -       trivial-rewrite
64bounce    unix  -       -       n       -       0       bounce
65defer     unix  -       -       n       -       0       bounce
66trace     unix  -       -       n       -       0       bounce
67verify    unix  -       -       n       -       1       verify
68flush     unix  n       -       n       1000?   0       flush
69proxymap  unix  -       -       n       -       -       proxymap
70proxywrite unix -       -       n       -       1       proxymap
71smtp      unix  -       -       n       -       -       smtp
72relay     unix  -       -       n       -       -       smtp
73showq     unix  n       -       n       -       -       showq
74error     unix  -       -       n       -       -       error
75retry     unix  -       -       n       -       -       error
76discard   unix  -       -       n       -       -       discard
77local     unix  -       n       n       -       -       local
78virtual   unix  -       n       n       -       -       virtual
79lmtp      unix  -       -       n       -       -       lmtp
80anvil     unix  -       -       n       -       1       anvil
81scache    unix  -       -       n       -       1       scache
82smtp      inet  n       -       n       -       1       postscreen
83dnsblog   unix  -       -       n       -       0       dnsblog
84tlsproxy  unix  -       -       n       -       0       tlsproxy
85# dspam retraining
86dspam-retrain         unix    -       n       n       -      -     pipe
87	flags=Rhq
88	user=dspam
89	argv=/usr/local/bin/dspam --client --mode=teft --class=$nexthop --source=error --user ${sender}

Of note, I added the last couple lines so that mail could be forwarded to specific addresses and it would be passed directly to dspam for training.

Also, I’ve ensured that people who authenticate aren’t subjected to the DNSBL checks, because mobile users aren’t in control of the network they’re on and shouldn’t be subjected to its reputation (or lack there of).

I then added these entries to the transport file (hash with ‘postmap transport’). This allows anybody who is allowed to send mail through the system to submit (as an attachment) spam or ham to be reclassified by dspam.

1spam@spam.spam	dspam-retrain:spam
2ham@ham.ham		dspam-retrain:innocent

I want to strip out some headers as things pass through, including some of the authenticated user headers (don’t need to advertise that to the world), as well as the spam/virus scanner headers. It doesn’t matter if somebody else has scanned it or not, it all gets scanned locally.

 1# We use the header_checks file to remove some headers that we find undesirable.
 2# Return receipts and software versions are the most significant in this
 3# situation.
 4
 5/^Received: from 127.0.0.1/                     IGNORE
 6/^X-Authenticated-Sender:/                      IGNORE
 7/^User-Agent:/                                  IGNORE
 8/^X-Mailer:/                                    IGNORE
 9/^X-MimeOLE:/                                   IGNORE
10/^X-MSMail-Priority:/                           IGNORE
11/^X-Originating-IP:/                            IGNORE
12/^X-Sanitizer:/                                 IGNORE
13/^X-Spam-Status:/                               IGNORE
14/^X-Spam-Level:/                                IGNORE
15/^X-Virus-Check:/                               IGNORE
16/^X-Virus-Checked:/                             IGNORE
17/^X-Virus-Scan:/                                IGNORE
18/^X-Virus-Scanned:/                             IGNORE
19/^X-Virus-Status:/                              IGNORE
20/^(X-DSPAM-.*)/                                 IGNORE

These files control how Postfix looks up data in our PostgreSQL database. Each one contains the user, password, database, host, and query to be executed. These queries return a set of information that tells postfix how to behave when its accepting or sending messages (postfix doesn’t deliver messages with this setup, that’s left to dovecot.

1# pgsql_relay_domains_maps.cf
2user = postfix
3password = password
4hosts = localhost
5dbname = postfix
6query = SELECT domain FROM domain WHERE domain='%s' and backupmx = true
1# pgsql_virtual_alias_maps.cf
2user = postfix
3password = password
4hosts = localhost
5dbname = postfix
6query = SELECT goto FROM alias WHERE address='%s' AND active = true
1# pgsql_virtual_domains_maps.cf
2user = postfix
3password = password
4hosts = localhost
5dbname = postfix
6query = SELECT domain FROM domain WHERE domain='%s' and backupmx = false and active = true
1# pgsql_virtual_mailbox_limit_maps.cf
2user = postfix
3password = password
4hosts = localhost
5dbname = postfix
6query = SELECT quota FROM mailbox WHERE username='%s'
1# pgsql_virtual_mailbox_maps.cf
2user = postfix
3password = password
4hosts = localhost
5dbname = postfix
6query = SELECT maildir FROM mailbox WHERE username='%s' AND active = true

Starting up postfix

You should now be able to start postfix with:

1service postfix start

and check that its running with:

 1[louisk@mail postfix 16 ]$ sockstat -46l | grep master
 2root     master     2985  17 tcp4   *:587                 *:*
 3root     master     2985  18 tcp6   *:587                 *:*
 4root     master     2985  21 tcp4   *:465                 *:*
 5root     master     2985  22 tcp6   *:465                 *:*
 6root     master     2985  25 tcp4   127.0.0.1:10026       *:*
 7root     master     2985  31 tcp4   127.0.0.1:10025       *:*
 8root     master     2985  105 tcp4  *:25                  *:*
 9root     master     2985  106 tcp6  *:25                  *:*
10[louisk@mail postfix 17 ]$

We see postfix listening on all the ports we want.


Footnotes and References

Copyright

Comments