howto install postfix before plesks qmail

this is a cut&paste howto which helps You to install postfix for spam-filtering in front of qmail. motivation was my switch from postfix to qmail (forced by plesk which doesn't accept any other mailserver). even after using RBLs and SPF there was still 5 times as much spam coming through than with my old postfix-box.

also see here for a description to get pop3-before-smtp-authentication working using mysql.

why should one install one mail server before another one ?

this howto was tested on a plesk 8 server from 1&1 running suse 9.3 on AMD64 but should work on any unix-platform. only thing You need is another official IP on the server (costs 1 euro per month at my provider). i guess this might be done with only one IP but with two it's easier and less intrusive to the plesk-installation.

SMTPS is also not covered so far but shouldn't be too complicated.

1. get Your 2nd IP running

use Your distributions favorite tool to activate the IP on the internet-interface.

if You care about security, use Your firewall to only allow incoming port on that IP. i used iptables:
#!/bin/sh
IPADDR2="new.new.new.new"
# allow SMTP ...
/usr/sbin/iptables -A INPUT -p TCP -d $IPADDR2 -j ACCEPT
/usr/sbin/iptables -A OUTPUT -p TCP -s $IPADDR2 --sport 25 -m state --state ESTABLISHED -j ACCEPT
# .. and nothing else on this IP
/usr/sbin/iptables -A INPUT -d $IPADDR2 -j DROP
/usr/sbin/iptables -A INPUT -s $IPADDR2 -j DROP

2. configure xinetd

no we need to tell qmail that it should only listen to the old IP, but xinetd doesn't accept more than one IP per service and plesk always overwrites /etc/xinetd.d/smtp_psa. leaves the defaults in /etc/xinetd.conf, but again only one "interface"-parameter.

i've choosen the cheap trick to set the default in /etc/xinetd.conf ...
        interface       = old.old.old.old
... and add a portforwarding to my firewall-script to forward all xinetd-services from 127.0.0.1 to old.old.old.old
this is of course not nice, i'd appreciate if someones comes up with a better idea ;)

/sbin/modprobe ip_conntrack_ftp
/usr/sbin/iptables -t nat -A OUTPUT -p tcp  -d 127.0.0.1 --dport 25 -j DNAT --to old.old.old.old:25
/usr/sbin/iptables -t nat -A OUTPUT -p tcp  -d 127.0.0.1 --dport 21 -j DNAT --to old.old.old.old:21
/usr/sbin/iptables -t nat -A OUTPUT -p tcp  -d 127.0.0.1 --dport 106 -j DNAT --to old.old.old.old:106
make similar lines for other services You need (netstat -tulpen|grep xinetd). i'd appreciate if someones comes up with a better idea to solve this issue;)

maybe reboot at this point to check that everything comes up right.

the following commands should both show something like "220 SERVERNAME ESMTP":
# telnet 127.0.0.1 25
Escape character is '^]'.
220 myserversname ESMTP

# telnet old.old.old.old 25
Escape character is '^]'.
220 myserversname ESMTP
netstat should show that xinetd port 25 isn't bound to "0.0.0.0" but "old.old.old.old";
# netstat -tulpen|grep xinet
tcp        0      0 old.old.old.old:106         0.0.0.0:*               LISTEN      0          11292      3048/xinetd
tcp        0      0 old.old.old.old:465         0.0.0.0:*               LISTEN      0          11294      3048/xinetd
tcp        0      0 old.old.old.old:21          0.0.0.0:*               LISTEN      0          11291      3048/xinetd
tcp        0      0 old.old.old.old:25          0.0.0.0:*               LISTEN      0          11293      3048/xinetd

3. make a backup!

i really mean it!

4. install postfix

both qmail and postfix have /usr/sbin/sendmail so:
# cp /usr/sbin/sendmail /usr/sbin/sendmail.qmail
install postfix with whatever is Your distributions paket-tool (yast, apt, yum, ... ) ignoring the conflicts (don't skip the backup ;)
# cp /usr/sbin/sendmail /usr/sbin/sendmail.postfix
# cp /usr/sbin/sendmail.qmail /usr/sbin/sendmail

5. configure postfix

edit /etc/postfix/main.cf to Your needs, ignore most of the features as postfix will only accept local mail, filter it and pass it on to qmail on 127.0.0.1:25.
myhostname = myserversname
inet_interfaces = new.new.new.new
relay_domains = hash:/etc/postfix/relaydomains
relayhost = 127.0.0.1
# or whatever is unused
syslog_facility = local7
throw in a bunch of spam-filter options:
# anti-spam:
smtpd_helo_required=yes
smtpd_helo_restrictions =
        reject_unauth_pipelining,
        permit
reject_invalid_hostname=yes
strict_rfc821_envelopes = yes
html_directory = no

smtpd_soft_error_limit = 2
smtpd_hard_error_limit = 10
smtpd_error_sleep_time = 20s
there are lot of pages on the web on how to block spam with postfix, maybe start with these: we need a script to generate a list of relay-domains for postfix from the rcpthost of qmail, for me it's /usr/local/sbin/make_postfix_localdomains. adjust $rcpthost, $relaydomains, $postmap and $sort to Your needs.
#!/usr/bin/perl -w

# create relaydomains for postfix from rcpthosts from qmail
# 20060405,arnim rupp

my $rcpthost = "/var/qmail/control/rcpthosts";

# must be same as postfixs main.cf "relay_domains="
my $relaydomains = "/etc/postfix/relaydomains";

my $postmap="/usr/sbin/postmap";
my $sort="/bin/sort";

use strict;


open (RCPTHOSTS, "$sort $rcpthost|" ) || die "cant open $rcpthost\n";
open (RELAYDOMAINS, "> $relaydomains" ) || die "cant open $relaydomains\n";

while(<RCPTHOSTS>) {
        chomp;
        print RELAYDOMAINS "$_ RELAY\n" if ( $_ );
}

close (RELAYDOMAINS);
close (RCPTHOSTS);

system "$postmap $relaydomains";
if the script works, put it into crontab every 5 minutes:
*/5 * * * *   /usr/local/sbin/make_postfix_relaydomains
start postfix ( "rcpostfix start" on suse, "/etc/init.d/postfix start" on most other distributions). now we should see xinetd and postfix-master listening on port 25 of two different IPs:
# netstat -tulpen|grep :25
tcp        0      0 old.old.old.old:25          0.0.0.0:*               LISTEN      0          11293      3048/xinetd
tcp        0      0 new.new.new.new:25          0.0.0.0:*               LISTEN      0          23689      8476/master
if not, check the logfile where local7 is logged to (mostly /var/log/local*, if not check /etc/syslogd.conf or /etc/syslog-ng/syslog-ng.conf)

6. test 123 test

try to send a mail from Your PC:
a@ubuntu:~$ telnet new.new.new.new 25
Trying new.new.new.new ...
Connected to new.new.new.new.
Escape character is '^]'.
220 myserversname ESMTP Postfix
EHLO test.de
250-myserversname
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250 8BITMIME
MAIL FROM: 
250 Ok
RCPT TO: 
250 Ok
DATA
354 End data with .
this is a test via postfix
.
250 Ok: queued as 6CDF514459B4
QUIT
221 Bye

7. going live

if that shows up in the mailbox of existing-name@relaydomain.de everything is fine. You can now start to change the MX-record of an unimportant domain to an A-record with new.new.new.new for testing-purposes.

if it doesn't work out, the fallback is putting the MX-records back.

also make sure the configuration is boot-proof!