6.3 Spam-Checking All Incoming Mail

‚  < ‚  Day Day Up ‚  > ‚  

If you want to set up a spam-checking gateway for all recipients, local or not, you need a way to perform spam-checking as mail is received, before final delivery. Postfix provides a general-purpose filtering directive called content_filter .

The content_filter directive specifies a mail transport that Postfix will invoke after receiving a message. The mail transport hands the message to a filtering program. The filter checks the message and then either refuses it (which will cause Postfix to generate a bounce message), discards it, or reinjects the (possibly modified) message into Postfix for further delivery. Messages that pass the filter are reinjected so that Postfix can operate on them almost as if they were new messages; this allows Postfix to behave properly if the content filter rewrites message headers. You can use the content_filter directive in main.cf , in which case the directive will be used by both smtpd (for email received via SMTP) and pickup (for email received locally). You can also specify content_filter as an invocation option to smtpd or pickup , which is useful when you only want to filter email received from outside (or inside) the system.

Content filters can be programs that are invoked for each message. They read a message on standard input and reinject filtered messages via the sendmail program. They can also be daemons that listen on a local TCP port, receiving messages via SMTP or LMTP (Local Mail Transfer Protocol), and reinjecting filtered messages via SMTP by communicating with a second instance of smtpd listening on a local port.

Don't confuse Postfix's sendmail program with sendmail. sendmail is an entirely different MTA that also uses an executable named sendmail to perform nearly all of its functions. Postfix's sendmail program is much more limited but is designed to serve as a replacement for sendmail's to facilitate converting systems from sendmail to Postfix.


SpamAssassin itself is not suitable for use as a content filter, because it doesn't know how to reinject a tagged message. However, SpamAssassin can be invoked by a content filter in several ways.

6.3.1 Using a Program as a Content Filter

The simplest content filters are programs that accept messages on standard input, perform spam-checking, and either exit with an error status code or reinject the message to Postfix. When you use a program as a content filter, you do not need to run any additional daemons ‚ Postfix invokes the program for each message. If your system receives a lot of mail, you are likely to get better performance by using a daemonized content filter, which is discussed in the next section.

To use a program as a content filter requires a series of steps:

  1. Create a new system user that Postfix will use to run the filter program or shell script. SpamAssassin will use this user 's SpamAssassin preferences (in the .spamassassin/user_prefs file in their home directory) when checking messages that have multiple recipients. In the following steps, assume the user is named spamfilt .

  2. Create a program (or shell script) that can accept an email message on standard input, perform filtering, and pass the modified message to sendmail 's standard input. The filter should also return an appropriate status code, usually the exit code from sendmail , which Postfix will understand. Your program (or shell script) should expect to receive command-line arguments consisting of the sender's email address and a space-separated list of recipient email addresses.

    Here's an example of a filter script called pf-spamfilt that calls SpamAssassin using spamc . If the message being checked has only a single recipient, spamc 's -u option is used to load the per-user preferences. When the message has multiple recipients, the script runs spamc without -u , and, because the script will be running as the spamfilt user, spamc will use spamfilt 's preferences file.

     #!/bin/sh # # pf-spamfilt: An example spam filtering script for postfix # sender= shift recip="$@" if [ "$#" -eq 1 ]; then   /usr/bin/spamc -u $recip else   /usr/bin/spamc fi  /usr/sbin/sendmail -i -f $sender -- $recip exit $? 

    Because this filter uses the spamc client, you must be running a spamd server. Save the filter somewhere publicly accessible (e.g., /usr/local/bin/pf-spamfilt ) and set its permissions to allow anyone to read and execute it.

  3. Define a new mail transport in master.cf that invokes the filter you created in step 2. The following example shows how you add a transport called spamcheck , defined as a Unix service. By defining the transport as shown, you specify that the mail transport will use Postfix's pipe command to run /usr/local/bin/pf-spamfilt as user spamfilt , and will pass the email address of the sender and the email addresses of recipients as command-line arguments to pf-spamfilt . The flag argument includes the R flag (add a Return-Path header) and the q flag (quote the sender and recipient addresses for use in the command line).

     # ========================================================================= # service type  private unpriv  chroot  wakeup  maxproc command + args #               (yes)   (yes)   (yes)   (never) (50) # ========================================================================= spamcheck  unix   -       n       n       -      -       pipe   flags=Rq user=spamfilt argv=/usr/local/bin/pf-spamfilt ${sender} ${recipient} 

  4. Direct Postfix to use the new mail transport as a content filter for the smtpd daemon. Replace this line in master.cf :

     smtp      inet  n       -       -       -       -       smtpd 

    with these two lines:

     smtp      inet  n       -       -       -       -       smtpd   -o content_filter=spamcheck: 

  5. If you always want to use per-user preferences, instruct Postfix to call the spamcheck transport with only a single recipient per message by adding this line to main.cf :

     spamcheck_destination_recipient_limit = 1 

  6. Run postfix reload to re-read the configuration files. Test the system by sending an email from the Internet and see whether SpamAssassin is called to check the message.

6.3.2 Using a Daemon as a Content Filter

Although it's more complicated to run a daemonized content filter, most larger sites will want to do so in order to avoid the overhead associated with starting the content filter for each email and running sendmail for reinjection. In the daemonized approach, the filter listens on a TCP port bound to the loopback address (127.0.0.1). On receiving a message from the Internet, Postfix connects to the filter daemon and relays the message using the SMTP or LMTP protocol.

The daemon can reject the message during the SMTP/LMTP transaction, which will cause Postfix to bounce the message, or the daemon can accept the message, modify it, and reinject it by SMTP. To prevent mail loops , Postfix must run a second smtpd daemon, bound to another TCP port on the loopback address. The second smtpd is configured to accept messages without rerunning the filter (or performing the checks that would normally be performed on a message received from the Internet).

To use a daemon as a content filter requires five steps:

  1. Install a daemon that performs content-filtering in the fashion that Postfix expects. Section 6.4 provides an example. Typically, you will need to know or configure:

    • The port on which the daemon accepts incoming messages to check (e.g., 10024).

    • The protocol (SMTP or LMTP) by which the daemon expects to receive an incoming message.

    • The port to which the daemon will connect to reinject a message to Postfix (e.g., 10025).

    • The user that will run the daemon. Ideally, you should run daemons under a single-purpose, non- root user.

  2. Define a new mail transport in master.cf that sends mail to the daemon. In the following example, the transport is called spamcheck and is defined as a Unix service that will use Postfix's smtp command. You can use the disable_dns_lookups option to save overhead, as you know that the transport will be configured to relay mail to your loopback IP address, so the daemon will never need to perform a DNS MX lookup. The example uses the maxproc feature in master.cf to limit the number of messages that can use this mail transport at one time to two.

     # ========================================================================= # service type  private unpriv  chroot  wakeup  maxproc command + args #               (yes)   (yes)   (yes)   (never) (50) # ========================================================================= spamcheck  unix   -       -       n       -      2       smtp   -o disable_dns_lookups=yes 

    If you are using Postfix 2.0 or later, you can define the spamcheck transport to use Postfix's lmtp command instead of smtp . The LMTP protocol has some advantages over SMTP ‚ notably, LMTP servers (including amavisd ) can return individual accept/ refuse codes for each message recipient during an LMTP transaction. Postfix's lmtp client can also cache connections to an LMTP server for greater performance. Bugs in the lmtp client existed in Postfix versions earlier than 2.0 so using smtp is recommended with these versions.


  3. Define a new mail transport that receives mail from the daemon in master.cf. This transport will use Postfix's smtpd daemon and is defined by the IP address and port number on which it will listen (127.0.0.1 and 10025, respectively). smtpd is an inet service, and many option parameters are provided to prevent further filtering and to restrict access to this mail transport to the local host only. Here is an example of such a definition in master.cf :

     # ========================================================================= # service type  private unpriv  chroot  wakeup  maxproc command + args #               (yes)   (yes)   (yes)   (never) (50) # ========================================================================= 127.0.0.1:10025  inet   n      -      n      -     -       smtpd     -o content_filter=     -o myhostname=localhost.   yourdomain   -o local_recipient_maps=     -o relay_recipient_maps=     -o smtpd_restriction_classes=     -o smtpd_client_restrictions=     -o smtpd_helo_restrictions=     -o smtpd_sender_restrictions=     -o smtpd_recipient_restrictions=permit_mynetworks,reject     -o mynetworks=127.0.0.0/8     -o strict_rfc821_envelopes=yes     -o smtpd_error_sleep_time=0     -o smtpd_soft_error_limit=1001     -o smtpd_hard_error_limit=1000 

    The -o myhostname=localhost . yourdomain option is important if the content filter issues the SMTP HELO command with the same hostname that it originally received from Postfix. If Postfix sees a HELO from itself, it rejects the connection to avoid a mail loop. By telling the new smtpd that its hostname is something else, you prevent this problem.


  4. Direct Postfix to the use the daemon's mail transport as a content filter for mail received by the primary smtpd daemon. Replace this line in master.cf :

     smtp      inet  n       -       -       -       -       smtpd 

    with these two lines:

     smtp      inet  n       -       -       -       2       smtpd   -o content_filter=spamcheck:[127.0.0.1]:10024 

    The primary smtpd daemon will filter incoming messages by passing them to the spamcheck mail transport that is listening on port 10024 of the loopback address 127.0.0.1.

  5. Run postfix reload to re-read the configuration files. Test the system by sending email from the Internet.

Figure 6-2 illustrates this configuration.

Figure 6-2. Postfix with a daemonized content filter

6.3.3 Filtering Before Address-Rewriting

The Postfix queue manager invokes content filters once it has queued a message. A potential problem with the simple content-filtering approaches outlined earlier is that the messages to be filtered have passed through the cleanup service on their way to the queue, and cleanup performs virtual address lookups and address canonicalization ‚ that is, cleanup may rewrite addresses in message headers. Accordingly, the message that Postfix sends to the content filter (and thus to SpamAssassin) to check is not exactly the same as the message that Postfix received. The changes to addresses may rob SpamAssassin's rules (or the Bayesian classifier) of useful determinants of spam.

If you are running Postfix 2.0 or later, you can fix this problem by setting up a separate, pre-cleanup service that does not perform address canonicalization. Messages received by Postfix's smtpd and pickup can be routed through the pre-cleanup and then to the queue. Filter-checked messages received by the second smtpd instance can then be routed through the standard cleanup service for address-rewriting before returning to the queue for further delivery processing.

To use a two-cleanup design, set up a daemonized filter configuration as described in the previous section and then make the following configuration changes:

  1. Add a new pre-cleanup service to /etc/postfix/master.cf that calls the cleanup daemon but turns off address canonicalization:

     pre-cleanup         unix  n      -      n      -       0      cleanup         -o canonical_maps=         -o sender_canonical_maps=         -o recipient_canonical_maps=         -o masquerade_domains=         -o virtual_alias_maps= 

  2. Configure smtpd and pickup to use the pre-cleanup service in /etc/postfix/master.cf by changing their entries from

     smtp      inet  n       -       -       -       -       smtpd pickup    fifo  n       -       -       60      1       pickup 

    to

     smtp      inet  n       -       -       -       -       smtpd   -o cleanup_service_name=pre-cleanup pickup    fifo  n       -       -       60      1       pickup   -o cleanup_service_name=pre-cleanup 

  3. To improve performance, modify the entry for cleanup so that it does not perform some of the message checks that will have already been handled by pre-cleanup . You can turn off any checks that would have already been performed on message headers (via the Postfix header_checks , mime_header_checks , or nested_header_checks options) or bodies (via the Postfix body_checks options) by defining each option to be empty:

     cleanup            unix  n      -      n      -       0      cleanup     -o header_checks=     -o mime_header_checks=     -o nested_header_checks=     -o body_checks= 

Figure 6-3 illustrates this configuration.

Figure 6-3. Postfix with a daemonized content filter and two cleanup services

‚  < ‚  Day Day Up ‚  > ‚  


SpamAssassin
SpamAssassin
ISBN: 0596007078
EAN: 2147483647
Year: 2004
Pages: 88

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net