This is a very first implementation of Postfix content filtering. A Postfix content filter receives unfiltered mail from Postfix and either bounces the mail or re-injects filtered mail back into Postfix. This document describes two approaches to content filtering. Simple content filtering example ================================ The first example is simple to set up. It uses a shell script that receives unfiltered mail from the Postfix pipe delivery agent, and that feeds filtered mail back into the Postfix sendmail command. Only mail arriving via SMTP will be content filtered. .................................. : Postfix : Unfiltered mail----->smtpd \ /local---->Filtered mail : -cleanup->queue- : ---->pickup / \smtp----->Filtered mail ^ : | : | : \pipe-----+ | .................................. | | | | | +-Postfix sendmail<----filter script<--+ The /some/where/filter program can be a simple shell script like this: #!/bin/sh # Localize these. INSPECT_DIR=/var/spool/filter SENDMAIL="/usr/sbin/sendmail -i" # Exit codes from EX_TEMPFAIL=75 EX_UNAVAILABLE=69 # Clean up when done or when aborting. trap "rm -f in.$$" 0 1 2 3 15 # Start processing. cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; } cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; } # filter smtpd \ /local----> : -cleanup->queue- : ---->pickup / ^ | \smtp-----> : | v : : smtpd smtp : : 10026 | : ......................|........... ^ | | v ....|............ : | 10025 : : filter : : : ................. To enable content filtering in this manner, specify in main.cf a new parameter: /etc/postfix/main.cf: content_filter = smtp:localhost:10025 This causes Postfix to add one extra content filtering record to each incoming mail message, with content smtp:localhost:10025. You can use the same syntax as in the right-hand side of a Postfix transport table. The content filtering records are added by the smtpd and pickup servers. When a queue file has content filtering information, the queue manager will deliver the mail to the specified content filter regardless of its final destination. The content filter can be set up with the Postfix spawn service, which is the Postfix equivalent of inetd. For example, to instantiate up to 10 content filtering processes on demand: /etc/postfix/master.cf: localhost:10025 inet n n n - 10 spawn user=filter argv=/some/where/filter localhost 10026 "filter" is a dedicated local user account. The user will never log in, and can be given a "*" password and non-existent shell and home directory. This user handles all potentially dangerous mail content - that is why it should be a separate account. In the above example, Postfix listens on port localhost:10025. If you want to have your filter listening on port localhost:10025 instead of Postfix, then you must run your filter as a stand-alone program. Note: the localhost port 10025 SMTP server filter should announce itself as "220 localhost...", to silence warnings in the log. The /some/where/filter command is most likely a PERL script. PERL has modules that make talking SMTP easy. The command-line specifies that mail should be sent back into Postfix via localhost port 10026. For now, it is left up to the Postfix users to come up with a PERL/SMTP framework for Postfix content filtering. If done well, it can be used with other mailers too, which is a nice spin-off. The simplest content filter just copies SMTP commands and data between its inputs and outputs. If it has a problem, all it has to do is to reply to an input of `.' with `550 content rejected', and to disconnect without sending `.' on the connection that injects mail back into Postfix. The job of the content filter is to either bounce mail with a suitable diagnostic, or to feed the mail back into Postfix through a dedicated listener on port localhost 10026: /etc/postfix/master.cf: localhost:10026 inet n - n - 10 smtpd -o content_filter= -o local_recipient_maps= -o myhostname=localhost.domain.name This is just another SMTP server. It is configured NOT to request content filtering for incoming mail. The server has the same process limit as the filter master.cf entry. The "-o local_recipient_maps=" is a safety in case you have specified local_recipient_maps in the main.cf file. That setting could interfere with content filtering. The SMTP server is configured to use a different hostname in the greeting message (this is necessary for testing when I simply use no filtering program and let the SMTP content filtering interfaces talk directly to each other).