Split mails per recipient

As mentioned in the MAQ, this is needed if you need to apply different rules depending on the individual recipient in a multi-recipient message.

Postfix is very well able to do this, by use of the default_destination_recipient_limit = 1 setting, but unfortunately ... this will split the mails after MailScanner has done its thing. Not good. This behaviour has led a lot of people to believe that you cannot do this type of thing with Postfix1)... But they are wrong;-).

The actually quite simple solution is to have two separate instances of Postfix, one that receives mail on port 25, does the split, then passes everything on to the second instance, that does the normal HOLD thing, and delivers the mails onward after MailScanner is done.

How to do it

To facilitate this we need use the transport_map override feature, so we cannot just fiddle with a single instance of Postfix and its master.cf settings, but actually need the two instance setup. We need different settings in at least the transport map, main.cf, master.cf and header_checks.

The steps are a bit reminiscent of the deprecated and unsafe “two instance defer” method we used to use a couple of years back, with the glaring exception that this setup will be completely safe to use since we will only be using an SMTP interface between the two instances (in keeping with the recommendations of the postfix developers, this is a “supported interface”:-)) and MailScanner will not come into play until the second instace puts the messages into the hold queue. Here goes...

The steps

These examples are from a Mandriva setup close by me, which would be pretty much the same on most any system (especially RedHat descendants:-)). SuSE, Debian-ish, Solaris and *BSD systems might differ slightly in paths etc.

  1. Setup your MailScanner system as per the normal recommendations, with the HOLD feature and one instance (see here, and the MAQ). Do everything you usually do, and test that it really works.
  2. Stop MailScanner and postfix with service MailScanner stop
  3. Copy both the configuration directory and queue directory:
    cp -a /etc/postfix /etc/postfix.in
    cp -a /var/spool/postfix /var/spool/postfix.in
  4. Lets first setup the “incoming” instance. Edit /etc/postfix.in/transport so that it only contains (apart from comments) a line:
    *       smtp:[127.0.0.1]:10026
    
  5. Edit /etc/postfix.in/master.cf and see to it that it does not contain any smtpd apart from the default one:
    ...
    smtp    inet    n       -       y       -       -       smtpd
    ...
    #127.0.0.1:10026        inet    n       -       y       -       -       smtpd
    #  -o content_filter=
    ...
  6. Edit /etc/postfix.in/header_checks and see to it that it does not contain the HOLD line (but it should contain all other header checks you wish to employ):
    ...
    # This is needed for the one instance postfix implementation of MailScanner. -- Glenn
    #/^Received:/ HOLD
  7. Edit /etc/postfix.in/main.cf and see to it that it contains:
    ...
    queue_directory = /var/spool/postfix.in
    transport_maps = hash:/etc/postfix/transport.in
    default_destination_recipient_limit = 1
    ...

    This will force the delivery of all emails to the second instance, and thus the split into one message per recipient.

  8. postmap the transport table with:
    postmap -c /etc/postfix.in hash:/etc/postfix.in/transport
  9. Now, on to the second instance, where we start by editing /etc/postfix/header_checks and make sure it contains the HOLD thing:
    # This is needed for the one instance postfix implementation of MailScanner. -- Glenn
    /^Received:/ HOLD
  10. Edit /etc/postfix/master.cf and see to it that it does not contain the default smtpd, but rather one listening only to localhost port 10026:
    ...
    #smtp    inet    n       -       y       -       -       smtpd
    ...
    127.0.0.1:10026 inet    n       -       y       -       -       smtpd
      -o content_filter=
      -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 smtpd_authorized_xforward_hosts=127.0.0.0/8
      -o strict_rfc821_envelopes=yes
      -o receive_override_options=no_unknown_recipient_checks
      -o header_checks=regexp:/etc/postfix/header_checks_hold
      -o transport_maps=
      -o smtpd_client_connection_limit_exceptions=127.0.0.0/8
    ...

    I “borrowed” this from the default “AFTER filter” setup on my machine, but you could as well have set all those settings in /etc/postfix/main.cf ... I was just a tad lazy here:-). In fact, I do set the “transport_maps = ” explicitly, mostly because I had forgotten about the changes in master.cf:-). Note: If you need a transport map, look at point 12 below.

  11. Edit /etc/postfix/main.cf and explicitly set:
    ...
    transport_maps =
    ...

    Note: If you need a transport map, look at point 12 below.

  12. If you employ a transport map, the above override (in both files) shouldn’t be used though. Just see to it that it is set as per your normal operations, and postmap it... In my settings, I can rely on MX records (split DNS setup), so ... I need the above:).
  13. Restart MailScanner with “service MailScanner start” and the MailScanner init script will notice, and use, the new dual instance setup ... and do the right things “automagically”.
  14. You are basically done now... Test it thoroughly, watch your logs, be happy...;-D

Notes

  • I wouldn’t recommend doing this on a high volume server, since the overhead could be murderous. In fact, if you consult what the postfix developers have said in this matter, you’ll see that they are emphatically against using *_destination_recipient_limit=1 ...
  • If you normally set one of the transport-specific limits (relay_destination_recipient_limit etc) you will defeat the purpose of the default setting to “1”... So don’t do that.
  • Following the flow of a message through the logs will be slightly harder with this setup. If that is an issue for you then either don’t use it, or look into making the instances log to diffwerent files or use different “names” (by way of the syslog_facility and syslog_name settings)
  • If you don’t really need it, then don’t use it.
  • A warning:!:: If you have rulesets set up to “whitelist” 127.0.0.1 ... then remove them. The above config will make all mail match those rules, which would defeat the whole purpose of this excersise...
    One will have to find some better workaround to facilitate releasing falsly tagged and quarantined spam and dangerous content from your quarantine.
    This is mostly a problem for users of MailWatch, since it precludes quarantining the queue file (in which case you wouldn’t need the problematic whitelist in the first place, just see to it that you place the queue file in the incoming directory of the second instance when following the advice here).
    One could work around this by slightly changing how you release mails from MailWatch, perhaps adding a second smtpd handler to the second instance (listening to its own port, of course) that doesn’t include the HOLD thing, and using the old style release function in MailWatch (PEARs Mail factory) with a small tweak to make sure it uses the “pass through port”. One could probably code a small script to emulate the sendmail command, using telnet or somesuch, as well. Who knows, if I find some more time, I might implement something like that:-). If so, I’ll document this a bit better... And the time is now:-)

    In /etc/postfix/master.cf you need:
    # An smtpd to handle releasing from quarantine... By bypassing MS entirely
    127.0.0.1:10027 inet    n       -       y       -       -       smtpd
      -o content_filter=
      -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 smtpd_authorized_xforward_hosts=127.0.0.0/8
      -o strict_rfc821_envelopes=yes
      -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
      -o smtpd_client_connection_limit_exceptions=127.0.0.0/8

    which is very similar to the one needed for MailScanner, but with the important difference that it’ll actually deliver the mails since it skips the header checks (and hence the HOLD thing). A tool that will work with this as a “sendmail replacement” is Jef Poskanzers mini_sendmail, so get that and install it (you could very easily type up your own using Perl and Net::SMTP, but why bother?:). You use this a bit like the sendmail command, but it’ll talk SMTP and you can tell it to use non-standard ports, like

    mini_sendmail -p10027 -t -i < /path/to/message

    or

    mini_sendmail -p10027 -t -i < /path/to/message

    If you use MailWatch with the oldstyle Pear Mail functions you don’t really need mini_sendmail, just change functions.php from (this is for version 1.0.3)

     $mail_param = array('host' => QUARANTINE_MAIL_HOST, 'localhost' => QUARANTINE_MAIL_HELO); 

    to

    $mail_param = array('host' => QUARANTINE_MAIL_HOST, 'localhost' => QUARANTINE_MAIL_HELO, 'port' => 10027);

    .... and that would be “it”:-).
    If you use the new define(QUARANTINE_USE_SENDMAIL, true);, then you need use mini_sendmail, so set define(QUARANTINE_SENDMAIL_PATH, ‘/usr/sbin/mini_sendmail’); and then change functions.php from

    $cmd = QUARANTINE_SENDMAIL_PATH." -i -f ".QUARANTINE_FROM_ADDR." $to < ";

    to

    $cmd = QUARANTINE_SENDMAIL_PATH." -i -p10027 ".QUARANTINE_FROM_ADDR." $to < ";

    ... and you should be done. Again, if you don’t need this type of workaround, then don’t use it... Test first to release by your normal measures ... If they don’t work, then ... configure away:-)

1) This “tickled” me enough that I, although not needing this in my own setup, actually set up a testbed for this purpose... Who said temperament is a bad thing:-)...
 
documentation/configuration/mta/postfix/how_to/split_mails_per_recipient.txt · Last modified: 2006/09/21 21:21 by glenn
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki