E-mail is ever the complicated endeavor, from storage challenges at the
core, to database quarantining complexities, to all-out combat with mail
exchangers across the Internet.
Introduction
Audience
This document is intended primarily for experienced system administrators or
mail administrators interested in building mail infrastructure on top of
open-source technologies. It is written comprehensively, inspired by the likes
of Life with qmail, and presented in a
modular fashion down lines of function. Because of that, it should also be
suitable for entry-level admins who are looking for guidance dealing with the
complexities of email.
Technical Overview
This entire solution is built on open-source technology on open standards:
Qmail, Amavisd-new, Maia Mailguard, ClamAV, SpamAssassin, Dovecot, SquirrelMail,
389DS (LDAP), ATA over Ethernet storage, Apache, and dozens of purpose-built
Perl scripts and systems to keep it all humming. All on Linux.
The mail exchangers run qmail in a multi-MTA setup, with Amavisd-new/Maia
Mailguard doing the heavy lifting between them to disassemble messages for
content scanning and virus and spam checks using ClamAV and SpamAssassin, and
potential quarantine into a MySQL database.
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
clamav
spamassassin
127.0.0.1
qmail-inside
port 10025
Messages passing all checks are delivered to the mail store cluster running
qmail-ldap. There they are forwarded to the correct mailhost and subject to other
directions based on user LDAP attributes. Webmail is provided by SquirrelMail,
with the Maia Mailguard frontend integrated for user quarantine management,
on top of the Apache web server.
NOTE: DNS, LDAP, PAM/NSS, MySQL, and Apache system setup and configuration
are not covered in great depth, and are generally outside the scope of this
guide. However, we do touch upon application-level requirements among the
various services.
Legacy
This solution has continually evolved to keep up with emerging email trends
for over ten years. Beginning from a POP mailstore, then IMAP, adding a
dedicated antivirus mail gateway, becoming an anti-spam tagging and later
quarantining exchanger, eventually taking on SMTP-Auth. Mail storage has moved
from local storage, to DAS, NAS, and SAN storage through numerous iterations.
Directory integration began with NSS and some scripts, reaching its zenith with
qmail-ldap.
This solution is currently scaled out minimally to handle a relatively small
user base of approximately 10,000 active mail accounts and aliases/lists. The
mail exchangers see hundreds of thousands of connections per day, and about
100,000 final deliveries to user mailboxes. The principles outlined here can
and have been scaled out to much much larger installations. Thoughts about
scaling out the various pieces are provided at the end of each section.
Email, and the challenges it has presented, have driven innovation
throughout the entire host and network systems environment over the last
decade. User management, storage, high-availability, authentication, database,
LDAP, DNS, web administration interfaces, and untold other aspects of our
operation have benefitted greatly from having to meet the requirements of
email. It is the core of our central services.
Assumptions
In any document such as this, it is best to build for a somewhat realistic
case. We will assume the following throughout:
- madstop.edu is the domain name
- 10.137.0.0/16 is the local network range and is magically Internet-routable
Mail Storage
We begin with the central piece of the puzzle: the mailstore. To handle
transfer of messages into user home directories, a stock netqmail will suffice.
However, we desire the extra features that qmail-ldap can provide: centralized
storage and lookup of user and alias information, and the mobility and
flexibility afforded by the clustering extensions. We will also employ the
Dovecot IMAP/POP server for user mail retrieval. As an optional third
component, we will install a second instance of qmail to handle forwarding to a
backup machine.
Setting up multiple qmails on one box is actually very straightforward, as
they can each share the same ucspi-tcp and daemontools install, and all just
need different install directory locations defined.
For now we will concentrate on one machine known as hermes.madstop.edu, to
which all the exchangers will smtproute their deliveries. It will also carry
the aliases pop.madstop.edu and imap.madstop.edu, which will be advertised for
POP and IMAP client configuration. Ideas for scaling this out are at the end
of this section.
Qmail
The first qmail instance on TCP port 25 is a qmail-ldap installation. We
also set it up to listen on TCP port 628 for QMQP connections from other
qmail-ldap hosts in the cluster. We sometimes refer to this as qmail-outside
to differentiate it from the other qmails in the chain. It is installed in the
standard place: /var/qmail.
Begin with
qmail-ldap installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
We need to ensure required pieces are turned on at compile-time in the
heavily-commented qmail-ldap Makefile. Some salient configuration details follow:
- LDAPFLAGS=-DALTQUEUE -DEXTERNAL_TODO -DDASH_EXT -DCLEARTEXTPASSWD -DQLDAP_CLUSTER
- - enable qmailqueue, external todo, dash-extension addresses, and cluster extensions
- LDAPLIBS=-L/usr/lib64 -lldap -llber
- - adjust as necessary
- LDAPINCLUDES=-I/usr/include
- - adjust as necessary
- MDIRMAKE=-DAUTOMAILDIRMAKE
- - enable auto-creation of user Maildirs on mail delivery
- HDIRMAKE=-DAUTOHOMEDIRMAKE
- - enable auto-creation of user home directories on mail delivery
- SHADOWLIBS=-lcrypt
- - may or may not be necessary
We also need to take a look at qmail-ldap.h to ensure LDAP attributes are
mapped correctly. In our case, since we expect that mail will be delivered and
owned by individual users, and the uidNumbers and gidNumbers we have defined in
LDAP will suffice for each, we do the following:
#define LDAP_QMAILUID "uidNumber"
#define LDAP_QMAILGID "gidNumber"
Qmail Configuration Files: /var/qmail/control
We need to ensure that qmail is configured for our ldap environment across a
number of files in /var/qmail/control. Some salient configuration details follow:
- dirmaker: /var/qmail/bin/dirmaker
- - full path to script for autohomedirmake feature
- ldapbasedn: o=madstop.edu
- - site-specific distinguished name on which ldap searches are based
- ldapcluster: 1
- - toggles clustering extensions on/off with 1|0
- - lookups for user ldap attribute "mailhost" determine where mail is forwarded via qmqp across the cluster
- ldapdefaultdotmode: both
- - control how user delivery instructions are looked up
- - both (ldap attribute "deliveryProgramPath" and .qmail files are used)
- - dotonly (only .qmail files are used)
- - ldaponly (ldap attribute "deliveryProgramPath" and .qmail files are ignored)
- - ldapwithprog (attribute "deliveryProgramPath is used if existent, .qmail files are ignored)
- ldapserver: ldap.madstop.edu
- - site-specific hostname or address and/or port number of ldap server
The defaultdelivery control file is quite special in this setup:
#./Maildir/
|/var/qmail-forward/bin/qmail-inject -a $USER@madstop.edu
|/var/qmail/bin/preline -f /usr/libexec/dovecot/deliver
Rather than standard ./Maildir/ delivery, we first inject the message into
the qmail-forward queue (for double-delivery to backup box), and then pass it
on to the Dovecot local delivery agent (gaining features such as sieve
filtering), for final delivery into user homes. It is done in this order
because every command must be successful or the message is re-delivered. With
Dovecot deliver last, we ensure that users will not get duplicate messages due
to a problem with injection into the forward queue.
NOTE: If you elect not to install the qmail-forward instance for backup,
make sure to remove this directive from defaultdelivery.
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. Ensure
that the "name" for this qmail instance retains the default setting at the top
of the startup and run scripts:
QMAIL=qmail
TCPRULES=tcp.smtp
Next, while the our standard qmail-smtpd and qmail-send run scripts suffice,
we will also be running qmail-qmqpd to handle cluster deliveries. These
scripts are based off of the qmail-smtpd setup with substiution s/smtpd/qmqpd/g
for the most part:
mkdir -p /var/qmail/supervise/qmail-qmqpd/log
/var/qmail/supervise/qmail-qmqpd/run
#!/bin/sh
QMAIL=qmail
TCPRULES=tcp.qmqp
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
MAXSMTPD=`cat /var/qmail/control/concurrencyincoming`
LOCAL=`head -1 /var/qmail/control/me`
if [ -z "$QMAILDUID" -o -z "$NOFILESGID" -o -z "$MAXSMTPD" -o -z "$LOCAL" ]; then
echo QMAILDUID, NOFILESGID, MAXSMTPD, or LOCAL is unset in
echo /var/$QMAIL/supervise/qmail-qmqpd/run
exit 1
fi
if [ ! -f /var/$QMAIL/control/rcpthosts ]; then
echo "No /var/$QMAIL/control/rcpthosts!"
echo "Refusing to start SMTP listener because it'll create an open relay"
exit 1
fi
exec /usr/local/bin/softlimit -m 8000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 628 /var/$QMAIL/bin/qmail-qmqpd 2>&1
/var/qmail/supervise/qmail-qmqpd/log/run
#!/bin/sh
QMAIL=qmail
exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t s16777215 n50 /var/log/$QMAIL/qmqpd
Chmod, mkdir, chown, and symlink the qmqpd service into place:
chmod 755 /var/qmail/supervise/qmail-qmqpd/run
chmod 755 /var/qmail/supervise/qmail-qmqpd/log/run
mkdir -p /var/log/qmail/qmqpd
chown qmaill /var/log/qmail/qmqpd
ln -s /var/qmail/supervise/qmail-qmqpd /service
One last thing remains: integrating the qmail-qmqpd service into the
qmailctl script. This is nothing more than adding the equivalent calls to
qmqpd alongside all the qmail-smtpd invocations and setup
/var/qmail/bin/qmailctl
#!/bin/sh
# description: the qmail MTA
QMAIL=qmail
TCPRULES=tcp.smtp
TCPRULESQMQ=tcp.qmqp
PATH=/var/$QMAIL/bin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin
export PATH
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
case "$1" in
start)
echo "Starting $QMAIL"
if svok /service/$QMAIL-send ; then
svc -u /service/$QMAIL-send /service/$QMAIL-send/log
else
echo "qmail-send supervise not running"
fi
if svok /service/$QMAIL-smtpd ; then
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
else
echo "qmail-smtpd supervise not running"
fi
if svok /service/$QMAIL-qmqpd ; then
svc -u /service/$QMAIL-qmqpd /service/$QMAIL-qmqpd/log
else
echo "qmail-qmqpd supervise not running"
fi
if [ -d /var/lock/subsys ]; then
touch /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-qmqpd
svstat /service/$QMAIL-qmqpd/log
/var/$QMAIL/bin/qmail-qstat
;;
stop)
echo "Stopping $QMAIL..."
echo " qmail-smtpd"
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo " qmail-qmqpd"
svc -d /service/$QMAIL-qmqpd /service/$QMAIL-qmqpd/log
echo " qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
if [ -f /var/lock/subsys/$QMAIL ]; then
rm /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-qmqpd
svstat /service/$QMAIL-qmqpd/log
/var/$QMAIL/bin/qmail-qstat
;;
stat)
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-qmqpd
svstat /service/$QMAIL-qmqpd/log
/var/$QMAIL/bin/qmail-qstat
;;
doqueue|alrm|flush)
echo "Flushing timeout table and sending ALRM signal to qmail-send."
/var/$QMAIL/bin/qmail-tcpok
svc -a /service/$QMAIL-send
;;
queue)
/var/$QMAIL/bin/qmail-qstat
/var/$QMAIL/bin/qmail-qread
;;
reload|hup)
echo "Sending HUP signal to qmail-send."
svc -h /service/$QMAIL-send
;;
pause)
echo "Pausing qmail-send"
svc -p /service/$QMAIL-send
echo "Pausing qmail-smtpd"
svc -p /service/$QMAIL-smtpd
echo "Pausing qmail-qmqpd"
svc -p /service/$QMAIL-qmqpd
;;
cont)
echo "Continuing qmail-send"
svc -c /service/$QMAIL-send
echo "Continuing qmail-smtpd"
svc -c /service/$QMAIL-smtpd
echo "Continuing qmail-qmqpd"
svc -c /service/$QMAIL-qmqpd
;;
restart)
echo "Restarting $QMAIL:"
echo "* Stopping qmail-smtpd."
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo "* Stopping qmail-qmqpd."
svc -d /service/$QMAIL-qmqpd /service/$QMAIL-qmqpd/log
echo "* Sending qmail-send SIGTERM and restarting."
svc -t /service/$QMAIL-send /service/$QMAIL-send/log
echo "* Restarting qmail-smtpd."
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo "* Restarting qmail-qmqpd."
svc -u /service/$QMAIL-qmqpd /service/$QMAIL-qmqpd/log
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-qmqpd
svstat /service/$QMAIL-qmqpd/log
/var/$QMAIL/bin/qmail-qstat
;;
cdb)
tcprules /etc/$TCPRULES.cdb /etc/$TCPRULES.tmp < /etc/$TCPRULES
tcprules /etc/$TCPRULESQMQ.cdb /etc/$TCPRULESQMQ.tmp < /etc/$TCPRULESQMQ
chmod 644 /etc/$TCPRULES.cdb
chmod 644 /etc/$TCPRULESQMQ.cdb
echo "Reloaded: /etc/$TCPRULES /etc/$TCPRULESQMQ"
;;
sendstop)
echo "Stopping qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
;;
help)
cat <<HELP
stop -- stops mail service (smtp connections refused, nothing goes out)
start -- starts mail service (smtp connection accepted, mail can go out)
pause -- temporarily stops mail service (connections accepted, nothing leaves)
cont -- continues paused mail service
stat -- displays status of mail service
cdb -- rebuild the tcpserver cdb file for smtp
restart -- stops and restarts smtp, sends qmail-send a TERM & restarts it
doqueue -- schedules queued messages for immediate delivery
reload -- sends qmail-send HUP, rereading locals and virtualdomains
queue -- shows status of queue
alrm -- same as doqueue
flush -- same as doqueue
hup -- same as reload
sendstop -- stop qmail-send
HELP
;;
*)
echo "Usage: $0 {start|stop|restart|doqueue|flush|reload|stat|pause|cont|cdb|queue|sendstop|help}"
exit 1
;;
esac
exit 0
Tcpserver Configuration
There is not much to do here except make sure the config file exists.
/etc/tcp.smtp
127.:allow,RELAYCLIENT=""
/etc/tcp.qmqp
127.:allow,RELAYCLIENT=""
Auto Home/Maildir Creation
Compile-time options AUTOHOMEDIRMAKE and AUTOMAILDIRMAKE provide the nifty
benefit of (you guessed it) home directory and Maildir creation at time of mail
delivery, running the script specified in the dirmaker control file. This
eliminates any need for the complexities of out-of-band scripts and such to
create the same. The script runs as the recipient user's uid/gid, so for
actual home directory creation, that user will require write permissions to the
parent directory. One way to handle this is with group write permissions,
another is with a virtual user install where all users are mapped to a single
userid. We prefer the former.
AUTOHOMEDIRMAKE expects to find a dirmaker file in /var/qmail/control. This
contains the full path to a script responsible for home creation, for example:
/var/qmail/bin/dirmaker. The script gets the path for the homedir as first
parameter and aliasempty as the second one. Here is an example dirmaker
script:
#!/bin/sh
/bin/mkdir -m 700 -p $1
We must ensure the dirmaker script is executable, or messages to new users
will bounce with a permanent error:
chmod 755 /var/qmail/bin/dirmaker
Dovecot
We proceed now with Dovecot installation to provide POP/IMAP service. We
intend to end up with Dovecot, authenticating against LDAP, listening on TCP
ports 110/995 for POP/POPS, TCP ports 143/993 for IMAP/IMAPS, and TCP port 2000
for Managesieve. Lastly, we will setup Dovecot's local delivery agent, called
simply "deliver," for final qmail mail delivery into user homes. This LDA
supports server-side filtering of mail using sieve.
Installation
Dovecot packages should exist for your distribution:
yum install dovecot dovecot-sieve dovecot-managesieve
NOTE: These package names represent Fedora 11, Dovecot 1.2.6.
Configuration
Dovecot configuration is in the conventional place, /etc/doveconf.conf. We
highlight directives that setup LDAP integration. However, instead of
authenticating to LDAP directly, we instead elect to use the checkpassword
interface and our own perl script for maximum flexibility.
Tweaks
- protocols = imap imaps pop3 pop3s managesieve
- - ensure managesieve is enabled
- listen = *
- - listen on all IPv4 interfaces (we disable IPv6, bug in this version of Dovecot)
- login_process_per_connection = no
- - login process allowed to handle multiple connections, faster
- login_processes_count = 6
- - increase number of listening login processes
- login_max_processes_count = 1024
- - increase max number of login processes
- login_max_connections = 2048
- - max connections per login process
- mail_location = maildir:~/Maildir:INDEX=/var/indexes/%u
- - we take advantage of the flexibility to store indexes on a different high-performance volume outside of user homes
- mail_drop_priv_before_exec = no
- - this is required for our dovecot home creation process to work properly
- max_mail_processes = 4096
- - increase the maximum number of mail processes
IMAP Namespaces
The namespace parameter allows for tweaking the IMAP namespace. IMAP
namespaces are a source of contention across different IMAP servers and
clients, and the following block allows for supporting more modern clients that
do not require a prefix, as well as older setups that are still hardcoded to
look for an INBOX. For example, this setup prevents folders from being nested
beneath "Inbox" in Thunderbird regardless of whether or not the "IMAP server
directory" is set in the account preferences dialog.
namespace private {
separator = .
prefix =
inbox = yes
}
namespace private {
separator = .
prefix = INBOX.
inbox = no
hidden = yes
list = no # for v1.1+
}
Protocols
The protocol imap stanza contains more than the following, but these are
changes. Note in particular that we override the mail executable with our own
perl. We also increase the number of connections per ip, since some devices
are gluttons for imap. Finally, we point to our SSL cert.
protocol imap {
mail_executable = /opt/bin/dovelogin.pl imap
mail_max_userip_connections = 20
ssl_cert_file = /etc/pki/dovecot/certs/pop.crt
ssl_key_file = /etc/pki/dovecot/private/pop.key
}
The protocol pop3 stanza sees changes analogous to the imap section.
protocol pop3 {
mail_executable = /opt/bin/dovelogin.pl pop3
ssl_cert_file = /etc/pki/dovecot/certs/pop.crt
ssl_key_file = /etc/pki/dovecot/private/pop.key
}
The protocol lda stanza configures the local delivery agent. Only changes
are presented here. The hostname parameter is important as that will be the
domain name in the return address of vacation messages. And we need to enable
sieve support.
protocol lda {
postmaster_address = postmaster@madstop.edu
hostname = madstop.edu
mail_plugins = sieve
}
The protocol managesieve stanza sees no changes from the defaults.
protocol managesieve {
#...
}
Authentication
Authentication is highly flexible in Dovecot. There are two aspects to deal
with, passdb to auth users, and userdb to retrieve info about users. In our
case, we plan to use the checkpassword interface and my
chkpassldap script to bind against ldap. This
has the added benefit of being "prefetch-capable," which in Dovecot parlance
means that you can get away without doing a separate userdb lookup, as long as
the script sets certain environment variables. So we turn on the prefetch
userdb. Despite this, we still must turn on a userdb so that the Dovecot LDA
can look up information at delivery time. We choose the plain passwd
interface, since our machine is setup to do nss-ldap. The below is all inside
the auth default stanza.
auth default {
mechanisms plain
user root
passdb checkpassword {
args = /opt/bin/chkpassldap.pl
}
userdb prefetch {
}
userdb passwd {
args = blocking=yes
}
}
Plugins
There is not much to do here, but we do change the sieve plugin's sieve_dir value.
plugin {
sieve_dir=~/.sieve
}
Chkpassldap
With the chkpassldap script installed in
/opt/bin, we need to configure it for our environment, starting with typical
variables to define ldap settings:
my $ldap_server='ldap.madstop.edu:389';
my $ldap_base = 'o=madstop.edu';
my $people_base = "ou=People,$ldap_base";
my $group_base = "ou=Groups,$ldap_base";
my $search_scope = 'sub';
my $group = 'svc_email';
my $attribute = '';
my $envset = 1;
The group and attribute variables (if set) require group membership and/or
an attribute=value pairing for a user to be authorized, while the envset
variable enables the environment mangling we do next.
Dovecot offers some very complex manipulation of the login process through
the use of a variety of environment variables, and this is controlled in the
%envmap hash:
my %envmap = (
'HOME' => { ldap=>'homeDirectory' },
'USER' => { ldap=>'uid' },
'userdb_uid' => { ldap=>'uidNumber', extra=>1 },
'userdb_gid' => { ldap=>'gidNumber', extra=>1 },
'host' => { ldap=>'mailHost', extra=>1, optional=>1, nslookup=>1 },
'proxy_maybe' => { value=>1, extra=>1, depends=>'host' },
);
The above sets the environment variables HOME, USER, userdb_uid, userdb_gid,
and host to ldap attribute values as specified. Those flagged as extra are
saved in the EXTRA environment variable as a space-separated list by variable
name, a requirement as laid out in the Dovecot docs. The proxy_maybe variable
is hardcoded to 1, as long as the host was successfully returned, itself being
looked up in DNS to provide an IP number. This is because we reuse the
mailHost qmail attribute from the qmail-ldap schema, and Dovecot expects an IP
address and not a name.
You must plan on reading the Dovecot authentication documentation to see if
this is the approach for you, and what it all does.
Qmail-Forward
The second qmail instance on tcp port 10025 is a netqmail installation named
qmail-forward. This serves as nothing more than a queuing and forwarding
instance that will be used to double-deliver mail to our mail backup box.
NOTE: If this backup scheme is not to your taste, you can skip this section.
Begin with
netqmail installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
Because our primary qmail (outside) instance is installed in /var/qmail, we
need a new home for qmail-forward. Before compiling, we need to edit the
conf-qmail file in the qmail source directory and change it thus:
/var/qmail-forward
All other differences are handled in the run scripts.
Qmail Configuration Files: /var/qmail-forward/control
Standard qmail control considerations apply as always. Some salient
configuration details follow. Because this is merely the forward MTA, we are
mainly concerned with the smtproutes file:
:hades.madstop.edu
This indicates that mail to all domains is forwarded to hades.madstop.edu.
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. First we
need to "name" this qmail instance in the qmailctl script and run scripts so
the various bits can find their homes and log locations. At the top of each
script, change the following:
QMAIL=qmail-forward
TCPRULES=tcp.smtp-forward
Next, because this is our second qmail instance and we intend to run on port
10025, we need to adjust /var/qmail-forward/supervise/qmail-smtpd/run. We
already adjusted the script variables, so we concentrate on the relevant lines:
exec /usr/local/bin/softlimit -m 5000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 127.0.0.1 10025 /var/$QMAIL/bin/qmail-smtpd 2>&1
Last, we link this qmail's qmailctl script into place as qmailctlforward:
ln -s /var/qmail-forward/bin/qmailctl /usr/local/bin/qmailctlforward
Tcpserver Configuration
There is not much to do here except make sure it exists.
/etc/tcp.smtp-forward
127.:allow,RELAYCLIENT=""
Scalability
This setup is designed to be scaled at the application layer, adding
additional mailstores and spreading user mail storage and accounts across
multiple boxes as needs require.
Assume all users have their mailHost LDAP attribute set to one mailstore,
example hermes.madstop.edu. If we decide to augment this setup with an
additional machine, we can do so completely transparently. We need only clone
the machine, call it hermes2 and configure appropriately. Anyone whose
mailHost attribute is set to hermes2.madstop.edu will have mail delivered there
and will see their POP/IMAP sessions proxied there. With autohome and
automaildir creation via all protocols, new users just need to be created in
LDAP.
NOTE: Existing users need to be moved with care, lest mail delivered or
POP/IMAP login create new homes during the move. One way to accomplish this is
to chmod a-wx
the home directory before move, as that will cause
mail to stay in the queue with a tempfail. Copy the mail to the destination
mailstore, change the mailHost, and then change the perms back to normal.
The flexibility offered by this setup allows for many different approaches:
-
The smtproutes from the mail exchangers can hit one mailstore, which
delivers locally to itself when me=mailHost, otherwise qmqpd to other mailHost.
Likewise, IMAP/POP clients can be configured to hit one mailstore, which logs
in locally when ip=mailHost, otherwise proxies to other mailHost. This is not
ideal for any kind of high-availability, but may ease migrations.
-
All smtproutes and mail clients could instead deliver/login to a round-robin
DNS name that resolves to all the mailstores. They will all forward/proxy
appropriately.
-
All smtproutes and mail clients could instead deliver/login to dedicated
QMQP forwarding and Dovecot proxying machine(s) that have no local mail. These
machines could take advantage of whatever high-availability scheme is desired.
-
The mail exchangers themselves could have their qmail-inside instances built
with the qmail-ldap patch, so they forward directly to the appropriate
mailstore over QMQP. Dovecot proxying for POP/IMAP would still require a
separate scheme as above.
Mail Backup
Our backup strategy consists of setting up a qmail backup MTA to receive
mail from our primary mailstores doing double-delivery. This machine is for
mail receipt and storage only, so there are no POP/IMAP user services setup.
In addition to double-delivery, we also run a nightly sync script to pickup
mail from user's Sent folders. If this is not to your liking, skip this
section (and remember to disable the qmail-forward bits on the mailstores).
This machine will be known as hades.madstop.edu, to which all the mailstores
will forward all messages via double delivery out of their defaultdelivery
instructions. Ideas for scaling this out are at the end of this section.
Qmail
The one and only qmail instance on TCP port 25 is a qmail-ldap installation.
There are two main reasons for this:
- We can use its autohomedir/automaildir make features.
- We can set this up as a "vmail" instance where all mail is owned and
delivered as one user, avoiding unnecessary LDAP pressure from NSS
It is installed in the standard place: /var/qmail.
Begin with
qmail-ldap installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
NOTE: Since this will be a vmail installation, ensure the vmail user and
group is created.
Qmail Pre-compilation
We need to ensure required pieces are turned on at compile-time in the
heavily-commented qmail-ldap Makefile. Some salient configuration details follow:
- LDAPFLAGS=-DALTQUEUE -DEXTERNAL_TODO -DDASH_EXT -DCLEARTEXTPASSWD
- - enable qmailqueue, external todo, dash-extension addresses
- LDAPLIBS=-L/usr/lib64 -lldap -llber
- - adjust as necessary
- LDAPINCLUDES=-I/usr/include
- - adjust as necessary
- MDIRMAKE=-DAUTOMAILDIRMAKE
- - enable auto-creation of user Maildirs on mail delivery
- HDIRMAKE=-DAUTOHOMEDIRMAKE
- - enable auto-creation of user home directories on mail delivery
- SHADOWLIBS=-lcrypt
- - may or may not be necessary
NOTE: The main difference between this and the mailstore qmail-ldap install
is that we do not bother compiling in the cluster extensions.
We also need to take a look at qmail-ldap.h to ensure LDAP attributes are
mapped correctly. This is a very special-case qmail-ldap install, designed
purely for offline reception. Since it is LDAP-aware, it would normally expect
to read and follow the directions of the various user attributes to determine
delivery instructions and other features. Because these will be set for the
use of our mailstores, we do not want this box to read those at all. As an
example, if a user had mailReplyText set, you could risk sending a reply from
the backup box in addition to the mailstore. Or a user may have mailHost set
to one of the mailstores, and our backup box would see that when it attempted
to deliver, and try to deliver it back the way it came (if clustering was on)!
To prevent this, we need to cripple this install and disable all of these
features. The best way to turn these off is to map the attributes to values
that will never exist:
#define LDAP_QMAILUID "skipqmailUID"
#define LDAP_QMAILGID "skipqmailGID"
#define LDAP_MAILSTORE "skipmailMessageStore"
#define LDAP_QUOTA "skipmailQuota"
#define LDAP_QUOTA_SIZE "skipmailQuotaSize"
#define LDAP_QUOTA_COUNT "skipmailQuotaCount"
#define LDAP_MAXMSIZE "skipmailSizeMax"
#define LDAP_FORWARDS "skipmailForwardingAddress"
#define LDAP_PROGRAM "skipdeliveryProgramPath"
#define LDAP_MAILHOST "skipmailHost"
#define LDAP_MODE "skipdeliveryMode"
#define LDAP_REPLYTEXT "skipmailReplyText"
#define LDAP_DOTMODE "skipqmailDotMode"
#define LDAP_ISACTIVE "skipaccountStatus"
#define LDAP_PURGE "skipqmailAccountPurge"
After all of that, we really just need uid, mail, mailAlternateAddress, and
homeDirectory. We leave qmailUID and qmailGID as is, since we override them
anyway in the ldapuid and ldapgid files to values for the vmail user next.
NOTE: It is wise to minimize these kinds of scenarios further with an
egress firewall rule preventing any traffic destined for port 25.
Qmail Configuration Files: /var/qmail/control
We need to ensure that qmail is configured for our ldap environment across a
number of files in /var/qmail/control. Some salient configuration details
follow:
- dirmaker: /var/qmail/bin/dirmaker
- - full path to script for autohomedirmake feature
- ldapbasedn: o=madstop.edu
- - site-specific distinguished name on which ldap searches are based
- ldapdefaultdotmode: both
- - ldaponly (ldap attribute "deliveryProgramPath" and .qmail files are ignored)
- ldapgid: 402
- - group id of the vmail user we created
- ldapuid: 407
- - user id of the vmail user we created
- ldapserver: ldap.madstop.edu:389
- - site-specific hostname or address and/or port number of ldap server
NOTE: The ldapgid and ldapuid must match the vmail user we created. They
override the normal user values, so all mail will be owned by this user and
group.
Qmail Startup Scripts
Our stock setup suffices for the most part. Ensure that the "name" for this
qmail instance retains the default setting at the top of the startup and run
scripts:
QMAIL=qmail
TCPRULES=tcp.smtp
NOTE: No cluster means no need for qmqpd.
Auto Home/Maildir Creation
Compile-time options AUTOHOMEDIRMAKE and AUTOMAILDIRMAKE provide the nifty
benefit of (you guessed it) home directory and Maildir creation at time of mail
delivery.
AUTOHOMEDIRMAKE expects to find a dirmaker file in /var/qmail/control. This
contains the full path to a script responsible for home creation, for example:
/var/qmail/bin/dirmaker. The script gets the path for the homedir as first
parameter and aliasempty as the second one. Here is an example dirmaker script:
#!/bin/sh
/bin/mkdir -m 700 -p $1
Since we override user uid and gid values with the vmail user, we simply
need to ensure the vmail user has read/write access to the directory in which
user homes will be created. Example:
chown vmail:vmail /home
chmod 755 /home
Scalability
Scaling out the backup boxes is less seamless than our mailstores. Even
though we do not need to worry about user logins, we cannot rely on user
mailHost forwarding since we expressly disabled that to prevent delivery loops.
One approach would be to set each mailHost box to forward to a specific mail
backup machine.
Local Mail Exchanger
While we have a perfectly valid setup for mail delivery and reception in our
primary mailstores, we wish to concentrate SMTP user delivery services on
dedicated local mail exchangers. These will provide standard SMTP on TCP port
25 for the LAN, as well as SMTPS on TCP port 465, and Sumbission on TCP port
587. Both SMTPS and Submission will be encrypted using stunnel.
The local exchangers run qmail in a multi-MTA setup, with Amavisd-new doing
the heavy lifting between them to disassemble messages for content scanning and
virus checks using ClamAV. The three qmails on the box will share the same
ucspi-tcp and daemontools installs, each forwarding to the next in the chain,
including Amavisd-new, using smtproutes.
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
amavisd
port 10024
clamav
127.0.0.1
qmail-inside
port 10025
This machine will be known as smtp.madstop.edu, which will be advertised for
SMTP client configuration. Ideas for scaling this out are at the end of this
section.
Depending on local network circumstance and policy, there may not be a
reason to separate local mail exchange from Internet mail exchange. The
Internet MXes run the gamut of real-time blacklists, antivirus and anti-spam
tests, and the same versions of qmail capable of supporting all of the above
SMTP-Auth features. You may wish to subject your own users to the same
scrutiny as incoming Internet delivery. In our own course of development, we
had two reasons for separating local exchange:
First, we initially rolled out SMTP-Auth on SMTPS port 465 on an
Internet-facing mail exchanger. It was a self-signed certificate, and it
required authentication to proceed. Some remote mail exchangers, possibly
misconfigured and/or ill-administered, will see port 465 available and
automatically try to use it to send mail. It is really rather pointless
outside of special-case delivery situations to want to encrypt all mail
generally. If the remote exchangers do not balk on the self-signed
certificate, we will not allow delivery because they cannot authenticate
anyway.
This could be seen as a slight mis-use of port 465, since submission
should really use port 587 (with STARTTLS to secure passwords), and if that is
followed, remote mail exchangers will probably be just fine. Unfortunately,
all things SMTP are not so clearly defined. We support authentication on
both, implicitly encrypted with SSL (no STARTTLS).
Second, we had been a longtime Amavisd-new user in our mail setups when it
came time to look at quarantining mail. When we rolled out Maia Mailguard, we
decided it was better not to subject official campus mailings to the whims of
user blacklisting and repeated attempts at improper Bayes classification,
envisioning a day when something important was stuck in quarantine. Running
stock Amavisd-new doing anti-virus scanning gave us what we needed, and
potential for future malware handling.
Qmail
10.137.0.1
qmail-outside
port 25,465
The first qmail instance on TCP port 25 is a qmail JMS Combined installation.
The JMS Combined patch provides a lot of enhancements, and here we are
interested in one main one: SMTP-Auth. We tend to refer to this as
qmail-outside to differentiate it from the other qmails in the chain. It is
installed in the standard place: /var/qmail.
Begin with
qmail JMS Combined installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Configuration Files: /var/qmail/control
Because this is the user-facing MTA, much of this is SMTP related, taking
advantage of features offered by the JMS Combined setup. Salient details
follow:
- badmailfrom
- - qmail-smtpd considers these unacceptable envelope sender addresses and will reject every recpient address
- - easy way to block some incoming spam
- concurrencyincoming
- - we bump this up to 550
- concurrenyremote
- - qmail-send maximum number of simultaneous remote delivery attempts, default 10
- - all deliveries for this qmail are remote to the next in the chain
- - we set to something like 200
- databytes
- - qmail-smtp maximum number of bytes allowed in message, default 0 unlimited
- defaulthost
- - qmail-inject adds this name to any address without a host name
- - only mail from the exchanger itself would be injected
- doublebounceto
- - qmail-send user to receive double-bounces
- - with trim patch if file begins with a newline doublebounces are discarded
- envnoathost
- - qmail-send appends this domain name for recipient addresses without @ signs
- locals
- - we set this to localhost to ensure no deliveries are considered local
- rcpthosts
- - list all domains for which this exchanger will receive mail
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - we set this to :127.0.0.1:10023 to hand-off all deliveries to the next qmail in the chain
Qmail Startup Scripts
Our stock setup suffices for the most part but we will be running two
additional services. First, ensure that the "name" for this qmail instance
retains the default setting at the top of the startup and run scripts:
QMAIL=qmail
TCPRULES=tcp.smtp
Next, while the our standard qmail-smtpd and qmail-send run scripts suffice,
we will also be running SMTPS on port 465 and encrypted Submission on port 587.
There are several approaches to doing this, but we will use stunnel in the
chain with qmail-smtpd. These scripts are based off of the qmail-smtpd setup.
First we setup for SMTPS on port 465:
mkdir -p /var/qmail/supervise/qmail-smtpd-ssl/log
/var/qmail/supervise/qmail-smtpd-ssl/run
#!/bin/sh
QMAIL=qmail
TCPRULES=tcp.smtps
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
MAXSMTPD=`cat /var/qmail/control/concurrencyincoming`
LOCAL=`head -1 /var/qmail/control/me`
if [ -z "$QMAILDUID" -o -z "$NOFILESGID" -o -z "$MAXSMTPD" -o -z "$LOCAL" ]; then
echo QMAILDUID, NOFILESGID, MAXSMTPD, or LOCAL is unset in
echo /var/$QMAIL/supervise/qmail-qmqpd/run
exit 1
fi
if [ ! -f /var/$QMAIL/control/rcpthosts ]; then
echo "No /var/$QMAIL/control/rcpthosts!"
echo "Refusing to start SMTP listener because it'll create an open relay"
exit 1
fi
exec /usr/local/bin/softlimit -m 20000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 smtps \
/usr/sbin/stunnel /etc/stunnel/smtp.conf 2>&1
/var/qmail/supervise/qmail-qmqpd/log/run
#!/bin/sh
QMAIL=qmail
exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t s16777215 n50 /var/log/$QMAIL/smtpd-ssl
Chmod, mkdir, chown, and symlink the smtpd-ssl service into place:
chmod 755 /var/qmail/supervise/qmail-smtpd-ssl/run
chmod 755 /var/qmail/supervise/qmail-smtpd-ssl/log/run
mkdir -p /var/log/qmail/smtpd-ssl
chown qmaill /var/log/qmail/smtpd-ssl
ln -s /var/qmail/supervise/qmail-smtpd-ssl /service
Now repeat the process for Submission on port 587:
mkdir -p /var/qmail/supervise/qmail-submission/log
/var/qmail/supervise/qmail-submission/run
#!/bin/sh
QMAIL=qmail
TCPRULES=tcp.submission
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
MAXSMTPD=`cat /var/qmail/control/concurrencyincoming`
LOCAL=`head -1 /var/qmail/control/me`
if [ -z "$QMAILDUID" -o -z "$NOFILESGID" -o -z "$MAXSMTPD" -o -z "$LOCAL" ]; then
echo QMAILDUID, NOFILESGID, MAXSMTPD, or LOCAL is unset in
echo /var/$QMAIL/supervise/qmail-qmqpd/run
exit 1
fi
if [ ! -f /var/$QMAIL/control/rcpthosts ]; then
echo "No /var/$QMAIL/control/rcpthosts!"
echo "Refusing to start SMTP listener because it'll create an open relay"
exit 1
fi
exec /usr/local/bin/softlimit -m 20000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 smtps \
/usr/sbin/stunnel /etc/stunnel/submission.conf 2>&1
/var/qmail/supervise/qmail-submission/log/run
#!/bin/sh
QMAIL=qmail
exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t s16777215 n50 /var/log/$QMAIL/submission
Chmod, mkdir, chown, and symlink the submission service into place:
chmod 755 /var/qmail/supervise/qmail-submission/run
chmod 755 /var/qmail/supervise/qmail-submission/log/run
mkdir -p /var/log/qmail/submission
chown qmaill /var/log/qmail/submission
ln -s /var/qmail/supervise/qmail-submission /service
One last thing remains: integrating the qmail-smtpd-ssl and qmail-submission
services into the qmailctl script. This is nothing more than adding the
equivalent calls alongside all of the qmail-smtpd invocations and setup.
/var/qmail/bin/qmailctl
#!/bin/sh
# description: the qmail MTA
QMAIL=qmail
TCPRULES=tcp.smtp
TCPRULESSSL=tcp.smtps
TCPRULESSUB=tcp.submission
PATH=/var/$QMAIL/bin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin
export PATH
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
case "$1" in
start)
echo "Starting $QMAIL"
if svok /service/$QMAIL-send ; then
svc -u /service/$QMAIL-send /service/$QMAIL-send/log
else
echo "qmail-send supervise not running"
fi
if svok /service/$QMAIL-smtpd ; then
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
else
echo "qmail-smtpd supervise not running"
fi
if svok /service/qmail-smtpd-ssl ; then
svc -u /service/$QMAIL-smtpd-ssl /service/qmail-smtpd-ssl/log
else
echo "qmail-smtpd-ssl supervise not running"
fi
if svok /service/qmail-submission ; then
svc -u /service/$QMAIL-submission /service/qmail-submission/log
else
echo "qmail-submission supervise not running"
fi
if [ -d /var/lock/subsys ]; then
touch /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-smtpd-ssl
svstat /service/$QMAIL-smtpd-ssl/log
svstat /service/$QMAIL-submission
svstat /service/$QMAIL-submission/log
/var/$QMAIL/bin/qmail-qstat
;;
stop)
echo "Stopping $QMAIL..."
echo " qmail-smtpd"
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo " qmail-smtpd-ssl"
svc -d /service/$QMAIL-smtpd-ssl /service/$QMAIL-smtpd-ssl/log
echo " qmail-submission"
svc -d /service/$QMAIL-submission /service/$QMAIL-submission/log
echo " qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
if [ -f /var/lock/subsys/$QMAIL ]; then
rm /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-smtpd-ssl
svstat /service/$QMAIL-smtpd-ssl/log
svstat /service/$QMAIL-submission
svstat /service/$QMAIL-submission/log
/var/$QMAIL/bin/qmail-qstat
;;
stat)
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-smtpd-ssl
svstat /service/$QMAIL-smtpd-ssl/log
svstat /service/$QMAIL-submission
svstat /service/$QMAIL-submission/log
/var/$QMAIL/bin/qmail-qstat
;;
doqueue|alrm|flush)
echo "Flushing timeout table and sending ALRM signal to qmail-send."
/var/$QMAIL/bin/qmail-tcpok
svc -a /service/$QMAIL-send
;;
queue)
/var/$QMAIL/bin/qmail-qstat
/var/$QMAIL/bin/qmail-qread
;;
reload|hup)
echo "Sending HUP signal to qmail-send."
svc -h /service/$QMAIL-send
;;
pause)
echo "Pausing qmail-send"
svc -p /service/$QMAIL-send
echo "Pausing qmail-smtpd"
svc -p /service/$QMAIL-smtpd
echo "Pausing qmail-smtpd-ssl"
svc -p /service/$QMAIL-smtpd-ssl
echo "Pausing qmail-submission"
svc -p /service/$QMAIL-submission
;;
cont)
echo "Continuing qmail-send"
svc -c /service/$QMAIL-send
echo "Continuing qmail-smtpd"
svc -c /service/$QMAIL-smtpd
echo "Continuing qmail-smtpd-ssl"
svc -c /service/$QMAIL-smtpd-ssl
echo "Continuing qmail-submission"
svc -c /service/$QMAIL-submission
;;
restart)
echo "Restarting $QMAIL:"
echo "* Stopping qmail-smtpd."
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo "* Stopping qmail-smtpd-ssl."
svc -d /service/$QMAIL-smtpd-ssl /service/$QMAIL-smtpd-ssl/log
echo "* Stopping qmail-submission."
svc -d /service/$QMAIL-submission /service/$QMAIL-submission/log
echo "* Sending qmail-send SIGTERM and restarting."
svc -t /service/$QMAIL-send /service/$QMAIL-send/log
echo "* Restarting qmail-smtpd."
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo "* Restarting qmail-smtpd-ssl."
svc -u /service/$QMAIL-smtpd-ssl /service/$QMAIL-smtpd-ssl/log
echo "* Restarting qmail-submission."
svc -u /service/$QMAIL-submission /service/$QMAIL-submission/log
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
svstat /service/$QMAIL-smtpd-ssl
svstat /service/$QMAIL-smtpd-ssl/log
svstat /service/$QMAIL-submission
svstat /service/$QMAIL-submission
/var/$QMAIL/bin/qmail-qstat
;;
cdb)
tcprules /etc/$TCPRULES.cdb /etc/$TCPRULES.tmp < /etc/$TCPRULES
tcprules /etc/$TCPRULESSSL.cdb /etc/$TCPRULESSSL.tmp < /etc/$TCPRULESSSL
tcprules /etc/$TCPRULESSUB.cdb /etc/$TCPRULESSUB.tmp < /etc/$TCPRULESSUB
chmod 644 /etc/$TCPRULES.cdb
chmod 644 /etc/$TCPRULESSSL.cdb
chmod 644 /etc/$TCPRULESSUB.cdb
echo "Reloaded: /etc/$TCPRULES /etc/$TCPRULESSSL /etc/$TCPRULESSUB"
;;
sendstop)
echo "Stopping qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
;;
help)
cat <<HELP
stop -- stops mail service (smtp connections refused, nothing goes out)
start -- starts mail service (smtp connection accepted, mail can go out)
pause -- temporarily stops mail service (connections accepted, nothing leaves)
cont -- continues paused mail service
stat -- displays status of mail service
cdb -- rebuild the tcpserver cdb file for smtp
restart -- stops and restarts smtp, sends qmail-send a TERM & restarts it
doqueue -- schedules queued messages for immediate delivery
reload -- sends qmail-send HUP, rereading locals and virtualdomains
queue -- shows status of queue
alrm -- same as doqueue
flush -- same as doqueue
hup -- same as reload
sendstop -- stop qmail-send
HELP
;;
*)
echo "Usage: $0 {start|stop|restart|doqueue|flush|reload|stat|pause|cont|cdb|queue|sendstop|help}"
exit 1
;;
esac
exit 0
Tcpserver Configuration
There are a lot of features available in the JMS Combined patch, and we need to
ensure that environment variables expressing those we want enabled/disabled for
each sending IP range are set. We start with plain SMTP, setting DENY_TLS so
that STARTTLS support is not offered:
/etc/tcp.smtp
127.:allow,RELAYCLIENT="",RBLSMTPD="",DENY_TLS="1"
10.137.:allow,RELAYCLIENT="",RBLSMTPD="",DENY_TLS="1"
:allow,RBLSMTPD="",DENY_TLS="1"
NOTE: We do not anticipate ever allowing this port to be visible to the
Internet, but if it is somehow, we at least prevent relaying.
The configuration for SMTPS requires authentication. We still disable
STARTTLS, but because we are encrypting the entire session using Stunnel, we
need to indicate so by setting SSL=1, so qmail-smtpd will allow authentication
to proceed:
/etc/tcp.smtps
127.:allow,RELAYCLIENT="",RBLSMTPD="",SSL="1",DENY_TLS="1"
10.137.:allow,RELAYCLIENT="",RBLSMTPD="",SSL="1",DENY_TLS="1",REQUIRE_AUTH="1"
:allow,RBLSMTPD="",SSL="1",DENY_TLS="1",REQUIRE_AUTH="1"
The JMS patchset allows authenticated users to relay, so users off-network
will be able to use this to send email anywhere.
The configuration for Submission is identical to SMTPS:
/etc/tcp.submission
127.:allow,RELAYCLIENT="",RBLSMTPD="",SSL="1",DENY_TLS="1"
10.137.:allow,RELAYCLIENT="",RBLSMTPD="",SSL="1",DENY_TLS="1"
:allow,RBLSMTPD="",SSL="1",DENY_TLS="1",REQUIRE_AUTH="1"
NOTE: As mentioned above, it is probably more in-line with expectation not
to implicitly encrypt Submission port 587, instead offering STARTTLS. JMS
supports that and can be configured to require it before authentication can
proceed. We have seen client issues on our SSL port 587 since they did not
expect to encounter full session encryption. YMMV.
Stunnel
With a pair of qmail services setup to use stunnel, we need to define
them. Configuration is in /etc/stunnel:
/etc/stunnel/smtp.conf
cert = /etc/stunnel/smtp.pem
exec = /var/qmail/bin/qmail-smtpd
execargs = qmail-smtpd smtp.madstop.edu /opt/bin/chkpassldap.pl /bin/true
foreground = yes
/etc/stunnel/submission.conf
cert = /etc/stunnel/smtp.pem
exec = /var/qmail/bin/qmail-smtpd
execargs = qmail-smtpd smtp.madstop.edu /opt/bin/chkpassldap.pl /bin/true
foreground = yes
We now need to create the certificate that both configurations reference.
We will use openssl and create a self-signed certificate as an example.
cd /etc/stunnel
openssl req -new -x509 -days 999 -nodes -out smtp.pem -keyout smtp.pem
NOTE: This will obviously result in certificate warnings for most clients. For
public-facing servers, you will probably want to pay the necessary extortion
fees to get certificates from a recognized CA.
Chkpassldap
With the chkpassldap script installed in
/opt/bin, we need to configure it for our environment, starting with typical
variables to define ldap settings:
my $ldap_server='ldap.madstop.edu:389';
my $ldap_base = 'o=madstop.edu';
my $people_base = "ou=People,$ldap_base";
my $group_base = "ou=Groups,$ldap_base";
my $search_scope = 'sub';
my $group = 'svc_email';
my $attribute = 'accountStatus=active';
my $envset = 0;
The group and attribute variables (if set) require group membership and/or
an attribute=value pairing for a user to be authorized, while setting envset=0
disables any of the environment mangling (suitable mainly for Dovecot).
Qmail-Fixup
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
The second qmail instance on TCP port 10023 is a netqmail installation named
qmail-fixup. The entire purpose of this qmail instance is to facilitate
address rewriting for consistency, so user@mail.example.com becomes
user@example.com. For this, we additionally require the mess822 package, from
which we run the ofmipd daemon instead of qmail-smtpd to handle address
rewrites.
NOTE: This entire qmail instance could be considered overkill. Because we
are so familiar with its workings on our mail exchangers, for the sake of
consistency and because it is nice to have header and address rewriting in line
for future needs, we install it. If you choose to skip it, you merely need to
change the smtproute in qmail-outside to hit amavisd-new at :127.0.0.1:10024.
Begin with
netqmail installation noting the
differences below before moving on to
qmail configuration and startup.
We also require
mess822 installation.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
Because our primary qmail (outside) instance is installed in /var/qmail, we
need a new home for qmail-fixup. Before compiling, we need to edit the
conf-qmail file in the qmail and mess822 source directories and change
it thus:
/var/qmail-fixup
All other differences are handled in the run scripts.
Qmail Configuration Files: /var/qmail-fixup/control
Standard qmail control considerations apply as always. Some salient
configuration details follow. Because this is the fixup MTA, we are mainly
concerned with the rewrite file:
- rewrite
- - ofmipd rewrites headers according to this file
-
*.:
-localhost:
=:madstop.edu
=bugz.madstop.edu:madstop.edu
=mx1.madstop.edu:madstop.edu
=mx2.madstop.edu:madstop.edu
=mail.madstop.edu:madstop.edu
=pop.madstop.edu:madstop.edu
=hermes.madstop.edu:madstop.edu
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - we set this to :127.0.0.1:10024 to hand-off all deliveries to Amavisd-new
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. First we
need to "name" this qmail instance in the qmailctl script and run scripts so
the various bits can find their homes and log locations. At the top of each
script, change the following:
QMAIL=qmail-fixup
TCPRULES=tcp.smtp-fixup
Next, because we are actually running ofmipd instead of qmail-smtpd in this
qmail instance, and we need to run it on an alternate port, we need to adjust
/var/qmail-fixup/supervise/qmail-smtpd/run. We already adjusted the script
variables, so we concentrate on the relevant lines:
exec /usr/local/bin/softlimit -m 5000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 127.0.0.1 10023 /usr/local/bin/ofmipd 2>&1
Last, we link this qmail's qmailctl script into place as qmailctlfixup:
ln -s /var/qmail-fixup/bin/qmailctl /usr/local/bin/qmailctlfixup
Tcpserver Configuration
There is not much to do here except make sure it exists.
/etc/tcp.smtp-fixup
127.:allow,RELAYCLIENT=""
Qmail-Inside
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
qmail-inside
port 10025
The third qmail instance on TCP port 10025 is also a netqmail installation,
named qmail-inside. The purpose of this qmail instance is to act as the remote
delivery agent to our Mailstores and the Internet after processing by Amavis.
Begin with
netqmail installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
Because our primary qmail (outside) instance is installed in /var/qmail, we
need a new home for qmail-inside. Before compiling, we need to edit the
conf-qmail file in the qmail source directory and change it thus:
/var/qmail-inside
All other differences are handled in the run scripts.
Qmail Configuration Files: /var/qmail-inside/control
Standard qmail control considerations apply as always. Some salient
configuration details follow. Because this is the inside MTA, we are mainly
concerned with sending implications:
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - this is our most complicated routing but still simple
-
somehost.madstop.edu:somehost.madstop.edu
otherhost.madstop.edu:otherhost.madstop.edu
lists.madstop.edu:lists.madstop.edu
madstop.edu:hermes.madstop.edu
The above picks out some specific hosts which receive mail on their own
behalf. All other mail to the madstop.edu domain gets forwarded to the
mailstore. Remaining mail is delivered by standard Internet mail exchanger
lookup.
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. First we
need to "name" this qmail instance in the qmailctl script and run scripts so
the various bits can find their homes and log locations. At the top of each
script, change the following:
QMAIL=qmail-inside
TCPRULES=tcp.smtp-inside
Next, because this is our third qmail instance and we intend to run on port
10025, we need to adjust /var/qmail-forward/supervise/qmail-smtpd/run. We
already adjusted the script variables, so we concentrate on the relevant lines:
exec /usr/local/bin/softlimit -m 5000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 127.0.0.1 10025 /var/$QMAIL/bin/qmail-smtpd 2>&1
Last, we link this qmail's qmailctl script into place as qmailctlinside:
ln -s /var/qmail-inside/bin/qmailctl /usr/local/bin/qmailctlinside
Tcpserver Configuration
There is not much to do here except make sure it exists.
/etc/tcp.smtp-inside
127.:allow,RELAYCLIENT=""
#10.137.:allow,RELAYCLIENT=""
HINT: We could add lines for specific systems or ranges to allow for
relaying. Certain machines can be setup to send mail via this box on port
10025 and avoid the entire chain of checks, fixes, and content-scanning.
Care must be taken, but when setup properly in tandem with appropriate iptables
restrictions, this is a great way to have automated systems send bulk messages
without bogging down the content-scanner.
DNS and BIND
Email can and will generate a lot of DNS queries. We will install BIND as a
local caching nameserver to help take pressure off the DNS infrastructure.
NOTE: We will build off of this idea later to handle RBLs on the Internet
mail exchangers.
Installation:
yum install bind
NOTE: Install bind-chroot if you are paranoid, and work out of /var/named/chroot.
Configuration for BIND is in /etc/named.conf. We will forward requests for
.madstop.edu to our normal DNS resolvers for the domain, and all other requests
will be handled locally. There are other ways to accomplish this, and there is
probably some other configuration in the file already, but the following
stanzas do the trick:
options {
listen-on port 53 { 127.0.0.1; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query { localhost; };
recursion yes;
dnssec-enable no;
dnssec-validation no;
// dnssec-lookaside . trust-anchor dlv.isc.org.;
};
view "localhost_resolver"
{
/*
* This view sets up named to be a localhost resolver with forwarding to
* remote nameserver for .madstop.edu and to local rbldnsd instance for .dnsbl
*/
match-clients { localhost; };
match-destinations { localhost; };
recursion yes;
/*
* These are zones that contain definitions for all the localhost
* names and addresses, as recommended in RFC1912 - these names should
* ONLY be served to localhost clients:
*/
include "/etc/named.rfc1912.zones";
zone "." IN {
type hint;
file "named.ca";
};
zone "madstop.edu" IN {
type forward;
forward only;
forwarders {
10.137.110.101 port 53;
};
};
};
We update /etc/resolv.conf so that the resolver queries only the localhost:
search madstop.edu
nameserver 127.0.0.1
Content-Scanning: Amavisd-new
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
amavisd
port 10024
127.0.0.1
qmail-inside
port 10025
The most important and complex piece of the chain: content scanning.
Amavisd-new is a content-scanner written in Perl that takes messages over SMTP
(tcp port 10024 by default), unpacks them, scans them for viruses, bad headers,
attachment restrictions, spam scores, and determines the ultimate fate of
messages based on configuration surrounding all of the above. It relies on a
separate antivirus scanner (or multiple ones) and anti-spam in the guise of
SpamAssassin.
Amavisd-new originally offered mainly central configuration of message
destiny based on virus, bad header, or spam status. Over time it has taken on
a number of features like database quarantining and the like. In our case,
since this is just outgoing mail, it will be used only for basic scanning and
no spam handling.
NOTE: Given how outgoing spam and such from phished users and
compromised accounts are possibly the biggest issue facing email admins these
days, this would be a logical place to do more.
Installation
Amavisd-new requires a number of Perl libraries and external programs for it
to function fully. The
install docs are comprehensive, and we will concentrate only on the few
things that deviate or that we consider best practice for our setup.
The easiest way to pull in all the depencies is to make use of the package
management system for your distro:
yum install amavisd-new
This will create the amavis user, with home in /var/spool/amavisd. Since a
lot of documentation and some third-party scripts refer to /var/amavis or
/var/amavisd, we will create symlinks for good measure:
ln -s spool/amavisd /var/amavis
ln -s spool/amavisd /var/amavisd
We plan to configure file-based logging, so we may as well create the log
directory while we are at it:
mkdir -p /var/log/amavis
chown amavis:amavis /var/log/amavis
Configuration
All amavisd-new configuration is stored in /etc/amavisd/amavisd.conf. There
are myriad features available, but the following will concern tweaks and
relevant deviations from the defaults (amavisd-new-2.6.4):
- $max_servers = 20;
- - number of pre-forked children bumped up, 2-30 common
- $max_requests = 25;
- - retire child after this many requests, default 10
- $mydomain = 'madstop.edu';
- - convenient default for other settings
- $MYHOME = '/var/spool/amavisd';
- - convenient default for other settings
- $QUARANTINEDIR = "$MYHOME/quarantine";
- - defaults to undef for some reason
- $DO_SYSLOG = 0;
- - we prefer a logfile
- $LOGFILE = "/var/log/amavis/amavis.log";
- - path to the dedicated logfile, matching it to the rpm-provided amavisd location
- $enable_db = 0;
- - we do not care about snmp or the nanny
- $X_HEADER_TAG = 'X-Virus-Scanned';
- - set the x-header to be added to all messages
- $X_HEADER_LINE = "Madstop (amavisd-new)";
- - set the x-header line to be added to all messages
We then set all the destinies to be discard:
$final_virus_destiny = D_DISCARD;
$final_banned_destiny = D_DISCARD;
$final_spam_destiny = D_DISCARD;
$final_bad_header_destiny = D_DISCARD;
And then make everyone spam and bad header lovers, so these things will not
even be checked:
@bypass_spam_checks_maps = (1);
@spam_lovers_maps = (1);
@bypass_banned_checks_maps = (1);
@banned_files_lovers_maps = (1);
@bypass_header_checks_maps = (1);
@bad_header_lovers_maps = (1);
Amavisd-new can use an array (literally) of antivirus scanners, and several
are defined. Amavisd-new will use whichever ones it finds, but we should
ensure our ClamAV favorite is listed:
['ClamAV-clamd',
\&ask_daemon, ["CONTSCAN {}\n", "/var/spool/amavisd/clamd.sock"],
qr/\bOK$/m, qr/\bFOUND$/m,
qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
The antivirus backup scanners array contains non-daemonized versions of the
same typically. The non-daemonized clamscan will have trouble keeping up on a
busy box should clamd fail:
['ClamAV-clamscan', 'clamscan',
"--stdout --no-summary -r --tempdir=$TEMPBASE {}",
[0], qr/:.*\sFOUND$/m, qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
Antivirus: ClamAV
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
amavisd
port 10024
clamav
127.0.0.1
qmail-inside
port 10025
Antivirus is a core aspect of email content-scanning, and we configured Maia
to use ClamAV.
Installation
Our initial amavisd-new yum install should have taken care of this, but if not:
yum install clamav clamav-db clamd
This installation stores the database files in /var/lib/clamav, and is
configured in /etc/clamd.d
Configuration: /etc/clamd.d/amavisd.conf
The defaults are mostly fine, but we also turn on file-based logging into our
amavisd log directory:
LogFile /var/log/amavis/clamd.amavis
LogFileMaxSize 0
LogTime yes
NOTE: Because amavisd initiates scanning as the amavis user, hence an
amavisd.conf file in clamd.d
Updates Configuration: /etc/freshclam.conf
An anti-virus solution is nothing without updates. Typically this will run
as the clam user and update the database in /var/lib/clamav. Make sure to
comment out the Example line. Salt to taste:
#Example
Updates are downloaded using the freshclam binary. Our yum install may have
dropped a file in /etc/cron.daily to handle this. Either use that, or schedule
in cron with crontab -e
as root. The following updates at 17
minutes after every hour:
17 * * * * /usr/bin/freshclam
NOTE: Remember to comment out everything in crond.daily if you use this. If
you simply remove the file, another update might put it back.
Third-party Updates
Because of the flexibility and open nature of clamav, third-party virus
definitions are available. We will pull down
Sanesecurity clamav
definitions. Sanesecurity provides several
download
scripts, and we will use the
second one by Gerard.
NOTE: The first one works great but no longer seems to be available.
Download the script, untar it, install it:
tar -zxvf scamp-5.3b.tar.gz
cd scamp-5.3b
cp scamp.sh /usr/local/bin
cp scamp.1 /usr/local/share/man/man1
Now create the config file by running scamp.sh. It will create an
/etc/scamp/default file like the below. We follow its suggestions most of the
way but need to match up directory locations and the clam user.
SCAMP_VERSION=5.3b
CLAMAV_DB=/var/lib/clamav
T_DIR=/var/lib/clamav/tmp
C_GROUP=clam
C_PID=/var/run/amavisd/clamd.pid
C_USER=clam
GET_LDB=1
GET_MALWARE=1
GET_MSRBL=0
GET_SANE=1
GET_SECURITE=4
GET_WINNOW=1
gpg_key_url=http://www.sanesecurity.net/publickey.gpg
L_TYPE=0
MK_LOG=1
MSRBL=rsync://rsync.mirror.msrbl.com/msrbl/
msrbl_Images=MSRBL-Images.hdb
msrbl_SPAM=MSRBL-SPAM.ndb
msrbl_SPAM_CR=MSRBL-SPAM-CR.ndb
MSR_DIR=/var/lib/clamav/tmp/msr
MW_DIR=/var/lib/clamav/tmp/malware
MW_FILE=mbl.ndb
MW_URL=http://www.malwarepatrol.com.br/cgi/submit?action=list_clamav_ext
RELOAD=0
REST=0
SANE=rsync://rsync.sanesecurity.net/sanesecurity
SANE_DB=/var/lib/clamav/tmp/sane
SI_DIR=/var/lib/clamav/tmp/securite
SYS_LOG=1
WPC=3
W_SUM=0
We now schedule this in cron with crontab -e
as root. The
following updates at 19 minutes after every hour:
19 * * * * /usr/local/bin/scamp.sh
Update Schedules
We noted a typical cron entry for each of our update sources: the official
clam sources and our third-party locations. Throughout, we disabled in-script
randomization and auto-updates because we want to manually reload clamd once
we pull down updates from all sources on our schedule. For example:
17 * * * * /usr/bin/freshclam
19 * * * * /usr/local/bin/scamp.sh
25 * * * * /etc/init.d/clamd.amavisd reload
Scalability
10.137.0.1
qmail-outside
port 25,465
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
amavisd
port 10024
clamav
127.0.0.1
qmail-inside
port 10025
Scaling out the local mail exchangers is straightforward. We simply clone
the setup and use some kind of high-availability setup on smtp.madstop.edu as
needs require.
If we have a split-zone DNS setup, and we care about MX redundancy for the
local zone, it is easy to take advantage of the inherent ability to define
multiple MX records in DNS. For example:
@ IN MX 0 smtp1.madstop.edu.
@ IN MX 0 smtp2.madstop.edu.
NOTE: @ is a BIND shortcut for the domain in question, here madstop.edu.
NOTE: It is critical that our local mail exchanger records do not propagate
to public DNS, and are available only to our local domain. This requires
separate internal/external DNS, either provided by separate machines or
split-zone DNS. This is handy to ensure that machines on the LAN doing
deliveries by MX lookup use the local exchanger, keeping mail out of the
Internet exchanger database. If split-zone DNS is not available, or we do not
care, it is not the end of the world to simply skip setting up MX records for
the local exchanger in DNS.
Internet Mail Exchanger
We now focus on the dedicated Internet mail exchangers, providing an array
of features geared towards handling the never-ending onslaught of Internet
email:
- Real-time blacklists
- Valid recipient checks
- Antivirus scanning
- Anti-spam examination
- Message quarantine
The Internet exchangers run qmail in a multi-MTA setup, with Maia Mailguard
doing the heavy lifting between them to disassemble messages for content
scanning and virus and spam checks using ClamAV and SpamAssassin. The three
qmails on the box will share the same ucspi-tcp and daemontools installs, each
forwarding to the next in the chain, including Amavisd-maia, using smtproutes.
If the message does not pass muster according to each recipient's settings, it
is quarantined into a MySQL database for possible user review.
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
clamav
spamassassin
127.0.0.1
qmail-inside
port 10025
These machines will be known as mx1.madstop.edu, mx2.madstop.edu, etc.
These will be advertised as the MX records for the domain. Ideas for scaling
this out are at the end of this section.
NOTE: The basic structure of the Internet MX is virtually identical to the
local MX. Because there are subtle differences in numerous components, we
document it separately in (near) entirety. That said, it is worth it to review
the introduction to the local MX which provides an
overview and rationale for the separation. At the very least, we provide
complete documentation for exchangers based on both Amavisd-new and Maia
Mailguard and either/both may be suitable for your network in either capacity.
Qmail
10.137.0.1
qmail-outside
port 25
greetdelay
validrcptto
The first qmail instance on TCP port 25 is a qmail JMS Combined
installation. JMS Combined provides a lot of enhancements, and we are
interested in two main ones: validrcptto recipient checking, and greetdelay
SMTP delayed response. We tend to refer to this as qmail-outside to
differentiate it from the other qmails in the chain. It is installed in the
standard place: /var/qmail.
Begin with
qmail JMS Combined installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Configuration Files: /var/qmail/control
Because this is an Internet-facing MTA, much of this is related to SMTP
defense, taking advantage of features offered by the JMS Combined setup.
Salient details follow:
- badhelo
- - qmail-smtp considers these unacceptable HELO/EHLO host names and regexes and will reject every recpient address, ex:
-
!\.
mx1\.madstop\.edu
mx2\.madstop\.edu
^[0-9.]+$
- badmailfrom
- - qmail-smtp considers these unacceptable envelope sender addresses and will reject every recpient address
- - easy way to block some incoming spam
- concurrencyincoming
- - we bump this up to 550
- concurrenyremote
- - qmail-send maximum number of simultaneous remote delivery attempts, default 10
- - all deliveries for this qmail are remote to the next in the chain
- - we set to something like 200
- databytes
- - qmail-smtp maximum number of bytes allowed in message, default 0 unlimited
- defaulthost
- - qmail-inject adds this name to any address without a host name
- - only mail from the exchanger itself would be injected
- doublebounceto
- - qmail-send user to receive double-bounces
- - with trim patch if file begins with a newline doublebounces are discarded
- envnoathost
- - qmail-send appends this domain name for recipient addresses without @ signs
- locals
- - we set this to localhost to ensure no deliveries are considered local
- rcpthosts
- - list all domains for which this exchanger will receive mail
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - we set this to :127.0.0.1:10023 to hand-off all deliveries to the next qmail in the chain
- validrcptto.cdb
- - qmail-smtp rejects any recipient not listed in this cdb file
- - file built on periodic basis by mkvalidrcptto script
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. Ensure
that the "name" for this qmail instance retains the default setting at the top
of the startup and run scripts:
QMAIL=qmail
TCPRULES=tcp.smtp
Next, because we want to add RBL checks to this Internet-facing qmail
instance, we need to adjust /var/qmail/supervise/qmail-smtpd/run. We already
adjusted the script variables, so we concentrate on the relevant rbldnsd lines:
exec /usr/local/bin/softlimit -m 40000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 smtp \
/usr/local/bin/rblsmtpd \
-r sbl-xbl.dnsbl \
/var/$QMAIL/bin/qmail-smtpd 2>&1
Tcpserver Configuration
There are a lot of features available in the JMS Combined patch, and we need to
ensure that environment variables expressing those we want enabled/disabled for
each sending IP range are set.
/etc/tcp.smtp
127.:allow,RELAYCLIENT="",RBLSMTPD="",DENY_TLS="1"
10.137.:allow,RELAYCLIENT="",RBLSMTPD="",DENY_TLS="1"
# Baddies
10.100.1.1:deny
10.101.1.1:deny
# Greetdelay exceptions
10.200.1.1:allow,DENY_TLS="1"
10.201.1.1:allow,DENY_TLS="1"
# Default
:allow,VALIDRCPTTO_CDB="/var/qmail/control/validrcptto.cdb",
VALIDRCPTTO_LIMIT="10",GREETDELAY="30",DROP_PRE_GREET="0",DENY_TLS="1",
LOGREGEX="1"
We start by allowing localhost and our local network range the ability to
relay (send to any domain), though that is unlikely outside of testing
scenarios: localhost will probably inject, and the local net range will use
another local smtp server dedicated to local sending. We then may have some
network ranges we declare off-limits for incoming mail with deny statements.
Following that, in practice some mail exchangers do not adhere to RFCs and do
not like waiting for our HELO after our artificial greetdelay wait, so we
provide specific records exclusion the delay.
We end with our default line (should all be one line) for all other hosts,
which provides the path to our validrcptto file, our validrcptto limit which
defines how many invalid recipients before an entire message is dropped, our
artifical greetdelay in seconds to throw spammers off an apparently slow SMTP
server, drop pre greet turned off because of too many false positives for
servers that send data before waiting for HELO, TLS off because this is our
Internet-facing mail exchanger and we do not care if other sites send to us
over TLS/SSL (some would try solely if it was available), and finally we
log regexes tripped by the badhelo check. Not too shabby. On to the next
qmail.
Qmail-Fixup
10.137.0.1
qmail-outside
port 25
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
The second qmail instance on TCP port 10023 is a netqmail installation named
qmail-fixup. The entire purpose of this qmail instance is to facilitate
address rewriting for consistency, so user@mail.madstop.edu becomes
user@madstop.edu. For this, we additionally require the mess822 package, from
which we run the ofmipd daemon instead of qmail-smtpd to handle address
rewrites.
Begin with
netqmail installation noting the
differences below before moving on to
qmail configuration and startup.
We also require
mess822 installation.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
Because our primary qmail (outside) instance is installed in /var/qmail, we
need a new home for qmail-fixup. Before compiling, we need to edit the
conf-qmail file in the qmail and mess822 source directories and change
it thus:
/var/qmail-fixup
All other differences are handled in the run scripts.
Qmail Configuration Files: /var/qmail-fixup/control
Standard qmail control considerations apply as always. Some salient
configuration details follow. Because this is the fixup MTA, we are mainly
concerned with the rewrite file:
- rewrite
- - ofmipd rewrites headers according to this file
-
*.:
-localhost:
=:madstop.edu
=bugz.madstop.edu:madstop.edu
=mx1.madstop.edu:madstop.edu
=mx2.madstop.edu:madstop.edu
=mail.madstop.edu:madstop.edu
=pop.madstop.edu:madstop.edu
=hermes.madstop.edu:madstop.edu
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - we set this to :127.0.0.1:10024 to hand-off all deliveries to Amavisd-new/Maia Mailguard
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. First we
need to "name" this qmail instance in the qmailctl script and run scripts so
the various bits can find their homes and log locations. At the top of each
script, change the following:
QMAIL=qmail-fixup
TCPRULES=tcp.smtp-fixup
Next, because we are actually running ofmipd instead of qmail-smtpd in this
qmail instance, and we need to run it on an alternate port, we need to adjust
/var/qmail-fixup/supervise/qmail-smtpd/run. We already adjusted the script
variables, so we concentrate on the relevant lines:
exec /usr/local/bin/softlimit -m 5000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 127.0.0.1 10023 /usr/local/bin/ofmipd 2>&1
Last, we link this qmail's qmailctl script into place as qmailctlfixup:
ln -s /var/qmail-fixup/bin/qmailctl /usr/local/bin/qmailctlfixup
Tcpserver Configuration
There is not much to do here except make sure it exists.
/etc/tcp.smtp-fixup
127.:allow,RELAYCLIENT=""
Qmail-Inside
10.137.0.1
qmail-outside
port 25
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
qmail-inside
port 10025
The third qmail instance on TCP port 10025 is also a netqmail installation,
named qmail-inside. The purpose of this qmail instance is to act as the remote
delivery agent to our Mailstores.
Begin with
netqmail installation noting the
differences below before moving on to
qmail configuration and startup.
The following sections build from there, highlighting changes and enhancements
to that base.
Qmail Pre-compilation
Because our primary qmail (outside) instance is installed in /var/qmail, we
need a new home for qmail-inside. Before compiling, we need to edit the
conf-qmail file in the qmail source directory and change it thus:
/var/qmail-inside
All other differences are handled in the run scripts.
Qmail Configuration Files: /var/qmail-inside/control
Standard qmail control considerations apply as always. Some salient
configuration details follow. Because this is the inside MTA, we are mainly
concerned with sending implications:
- smtproutes
- - qmail-remote artificial smtp routes in form domain:relay
- - this is our most complicated routing but still simple
-
somehost.madstop.edu:somehost.madstop.edu
otherhose.madstop.edu:otherhost.madstop.edu
lists.madstop.edu:lists.madstop.edu
madstop.edu:mailstore.madstop.edu
The above picks out some specific hosts which receive mail on their own
behalf. All other mail to the madstop.edu domain gets forwarded to the
mailstore. Remaining mail is delivered by standard Internet mail exchanger
lookup.
Qmail Startup Scripts
Our stock setup suffices for the most part with but few changes. First we
need to "name" this qmail instance in the qmailctl script and run scripts so
the various bits can find their homes and log locations. At the top of each
script, change the following:
QMAIL=qmail-inside
TCPRULES=tcp.smtp-inside
Next, because this is our third qmail instance and we intend to run on port
10025, we need to adjust /var/qmail-forward/supervise/qmail-smtpd/run. We
already adjusted the script variables, so we concentrate on the relevant lines:
exec /usr/local/bin/softlimit -m 5000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 127.0.0.1 10025 /var/$QMAIL/bin/qmail-smtpd 2>&1
Last, we link this qmail's qmailctl script into place as qmailctlinside:
ln -s /var/qmail-inside/bin/qmailctl /usr/local/bin/qmailctlinside
Tcpserver Configuration
There is not much to do here except make sure it exists.
/etc/tcp.smtp-inside
127.:allow,RELAYCLIENT=""
Realtime Block Lists: RBLDNSD and BIND
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
qmail-inside
port 10025
Now that the qmails are in place, we will fill in the first missing piece:
RBLs. The qmail(-outside) instance is configured to query rbldnsd in its
qmail-smtpd run script so we will set that up. We subscribe to Spamhaus feeds,
but this will easily apply to others as well.
While that will cover direct RBL checks in the MTA, we also plan to have
spamassassin query these new dedicated RBL zones. This requires making the
local machine a full-fledged caching/forwarding nameserver using BIND. In a
nutshell:
- Two nameservers: bind and rbldnsd
- Rbldnsd listening on port 530 serving up the blocklist zones
- Bind configured to be a caching nameserver on port 53, but forwarding requests to fake domain suffix .dnsbl to localhost port 530
- Local resolver set to query only the local host caching nameserver (/etc/resolv.conf)
- Outside qmail set to call rbldnsd in the binary chain (done above)
- Spamassassin Spamhaus RBL checks modified to query sbl.dnsbl (done below)
Installation:
yum install rbldnsd bind
NOTE: Install bind-chroot if you are paranoid, and work out of /var/named/chroot.
Configuration for rbldnsd is in /etc/sysconfig/rbldnsd. We will create an
imaginary ".dnsbl" top-level domain. Add the following to the bottom of the
file:
RBLDNSD="- -r/var/lib/rbldnsd -q -b127.0.0.1/530 -f \
sbl.dnsbl:ip4set:spamhaus/sbl \
xbl.dnsbl:ip4set:spamhaus/xbl \
sbl-xbl.dnsbl:ip4set:spamhaus/sbl \
sbl-xbl.dnsbl:ip4set:spamhaus/xbl \
"
We need to create the subdirectory for our zone files and put them there:
mkdir -p /var/lib/rbldnsd/spamhaus
cp /path/to/sbl /var/lib/rbldnsd/spamhaus/sbl
cp /path/to/xbl /var/lib/rbldnsd/spamhaus/xbl
NOTE: This scheme scales pretty well for various RBL subscriptions.
Test the setup with the following:
dig A 2.0.0.127.sbl.dnsbl @localhost -p 530
dig A 2.0.0.127.xbl.dnsbl @localhost -p 530
dig A 2.0.0.127.sbl-xbl.dnsbl @localhost -p 530
These tests should all return A records like the following:
2.0.0.127.sbl.dnsbl. 300 IN A 127.0.0.2
It is now time to move on to the local caching nameserver using BIND.
Configuration for BIND is in /etc/named.conf. We will forward requests for
.dnsbl hosts to the local rbldnsd instance on udp port 530, requests for
.madstop.edu to our normal DNS resolvers for the domain, and all other requests
will be handled locally. There are other ways to accomplish this, and there is
probably some other configuration in the file already, but the following
stanzas do the trick:
options {
listen-on port 53 { 127.0.0.1; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query { localhost; };
recursion yes;
dnssec-enable no;
dnssec-validation no;
// dnssec-lookaside . trust-anchor dlv.isc.org.;
};
view "localhost_resolver"
{
/*
* This view sets up named to be a localhost resolver with forwarding to
* remote nameserver for .madstop.edu and to local rbldnsd instance for .dnsbl
*/
match-clients { localhost; };
match-destinations { localhost; };
recursion yes;
/*
* These are zones that contain definitions for all the localhost
* names and addresses, as recommended in RFC1912 - these names should
* ONLY be served to localhost clients:
*/
include "/etc/named.rfc1912.zones";
zone "." IN {
type hint;
file "named.ca";
};
zone "madstop.edu" IN {
type forward;
forward only;
forwarders {
10.137.110.101 port 53;
};
};
zone "dnsbl" IN {
type forward;
forward only;
forwarders {
127.0.0.1 port 530;
};
};
};
We update /etc/resolv.conf so that the resolver queries only the localhost:
search madstop.edu
nameserver 127.0.0.1
Now the dnsbl domain is seamlessly available to the resolver:
dig A 2.0.0.127.sbl.dnsbl
2.0.0.127.sbl.dnsbl. 300 IN A 127.0.0.2
NOTE: For setups that do not use SpamAssassin, or where SpamAssassin will
not be configured to query RBLs, we can skip all the BIND and resolver setup
since qmail queries rbldnsd directly.
Content-Scanning: Maia Mailguard
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
127.0.0.1
qmail-inside
port 10025
The most important and complex piece of the chain:
content scanning. Maia Mailguard is a fork of Amavisd-new, a content-scanner
written in Perl that takes messages over SMTP (TCP port 10024 by default),
unpacks them, scans them for viruses, bad headers, attachment restrictions,
scores them for spam, and ultimately determines their fate based on
configuration surrounding all of the above. It relies on a separate antivirus
scanner (or multiple ones) and anti-spam in the guise of SpamAssassin.
The main benefit Maia Mailguard provides over stock Amavisd-new is that it
interacts with a database to determine message destiny based on user
preferences associated with all of these threshholds. That same database
provides a quarantine for messages that fail these tests, with a web frontend
for users both to interact with the application, and for quarantine review and
ham/spam SpamAssassin Bayesian learning.
Amavisd-new itself has taken on many of the features Maia Mailguard
provides, and it may be more in line with your needs. See the
Maia Mailguard FAQ and
the specific article on their
differences.
Installation
Maia Mailguard requires all the pieces that an Amavisd-new install would,
(same thing plus database), and that is a lot of dependencies. The
install documentation
is comprehensive, and we will concentrate only on the few things that deviate
or that we consider best practice for our setup.
The easiest way to pull in all the depencies is to make use of the package
management system for your distro:
yum install amavisd-new
This will create the amavis user that Maia needs, homed in
/var/spool/amavisd. We will build around that space, but since much
documentation and many third-party scripts (inluding Maia) tend to assume
/var/amavis or /var/amavisd, we create symlinks for good measure:
ln -s spool/amavisd /var/amavis
ln -s spool/amavisd /var/amavisd
With zillions of dependencies installed and the OS framework in place
for launching the daemon, we will now move it out of the way to make room for
Maia Mailguard. Find the stock daemon with 'which amavisd' and back it up:
mv /usr/sbin/amavisd /usr/sbin/amavisd-new
mv /etc/amavisd/amavisd.conf /etc/amavisd/amavisd-new.conf
NOTE: Pay heed to the fact that future updates might update this package and
affect our work. It is a good idea to exclude amavisd-new from your package
management system.
Now download the latest package at
http://www.maiamailguard.com/download.php
tar -zxvf /path/to/maia-1.0.2c.tar.gz
cd maia-1.0.2c
cp amavisd-maia /usr/sbin
ln -s amavisd-maia /usr/sbin/amavisd
cp amavisd.conf.dist /etc/amavisd/amavisd-maia.conf
ln -s amavisd-maia.conf /etc/amavisd/amavisd.conf
NOTE: These symlinks are just here to preserve the original amavisd-new
alongside amavisd-maia for comparison.
We plan to configure file-based logging, so we may as well create the log
directory while we are at it:
mkdir -p /var/log/amavis
chown amavis:amavis /var/log/amavis
Configuration
All amavisd-maia configuration is stored in /etc/amavisd/amavisd.conf (which
should now be a symlink to amavisd-maia.conf as above). There are myriad
features available, but the following will concern tweaks and relevant
deviations from the defaults (maia-1.0.2c):
- $max_servers = 10;
- - number of pre-forked children bumped up, 2-15 common
- $max_requests = 1;
- - retire child after this many requests, default 10
- - normally better to let a child handle numerous requests, but a bug prevents file logging after one request
- $child_timeout=5*60;
- - abort child if it does not finish in n secs, default 8*60
- $mydomain = 'madstop.edu';
- - convenient default for other settings
- $MYHOME = '/var/spool/amavisd';
- - convenient default for other settings, matching it to the rpm-provided amavisd home
- $QUARANTINEDIR = "$MYHOME/quarantine";
- - we quarantine to the database but set this anyway, matching it to the rpm-provided amavisd location
- $LOGFILE = "/var/log/amavis/amavis.log";
- - path to the dedicated logfile, matching it to the rpm-provided amavisd location
- $enable_db = 0;
- - we do not care about snmp or the nanny
- $enable_global_cache = 0;
- - we do not care about snmp or the nanny
- $sa_local_tests_only = 1;
- - only test which do not require internet access, probably for performance?
- @lookup_sql_dsn = ( ['DBI:mysql:maia:localhost', 'amavis', 'password'] );
- - set this appropriate for mysql access to the database we will create next
- $myhostname = 'mx1.madstop.edu';
- - set this appropriately
- $X_HEADER_LINE = "Madstop (Maia Mailguard 1.0.2a)";
- - add a handy header to scanned messages
- $banned_filename_re = ...
- - check and adjust for local needs
- @av_scanners = ...
- - check and adjust for local needs, default is clamd
The @av_scanners array defines a list of antivirus scanners to run on
messages. It defaults to clamd:
['ClamAV-clamd',
\&ask_daemon, ["CONTSCAN {}\n", "/var/amavisd/clamd.sock"],
qr/\bOK$/, qr/\bFOUND$/,
qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ],
The @av_scanners_backup array defines a list of fallback antivirus scanners,
typically non-daemonized versions of the same. It defaults to clamscan, but
that will have trouble keeping up on a busy box should clamd fail:
['ClamAV-clamscan', 'clamscan',
"--stdout --disable-summary -r --tempdir=$TEMPBASE {}", [0], [1],
qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
MySQL Database
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
127.0.0.1
qmail-inside
port 10025
Maia Mailguard requires a decent database server, and in our case this is a
separate box with 4GB of RAM. Again, the
install documentation
has plenty of info. The basics are:
[root]# mysql -u root -p mysql
mysql> CREATE DATABASE maia;
[root]# mysql -u root -p maia < maia-mysql.sql
Grant the appropriate privileges:
[root]# mysql -u root -p maia
mysql> GRANT CREATE, DROP, ALTER, SELECT, INSERT, UPDATE, DELETE
ON maia.* TO amavis@localhost IDENTIFIED BY 'passwd';
Mysql installations should ensure these tables are specified to use the
InnoDB engine (the current maia-mysql.sql file creates them this way). This
database will face constant read-writes from an unyielding mail stream. This
mysql configuration has been tuned for our setup, in line with recommendations
from the Maia install docs. Your mileage may vary.
/etc/my.cnf
[mysqld]
datadir=/mnt/dbfiles/mysqldata
socket=/var/lib/mysql/mysql.sock
user=mysql
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1
max_allowed_packet=50M
skip-external-locking
#max_connections=200
read_buffer_size=1M
sort_buffer_size=1M
# Default InnoDB config
innodb_data_file_path=ibdata1:10M:autoextend
# Set buffer pool size to 50-80% of your computer's memory,
# but make sure on Linux x86 total memory usage is < 2GB
innodb_buffer_pool_size=2G
innodb_additional_mem_pool_size=20M
# Set the log file size to about 25% of the buffer pool size (changing these details is a pain)
innodb_log_file_size=250M
innodb_log_buffer_size=8M
innodb_flush_log_at_trx_commit=0
innodb_lock_wait_timeout=50
# Uncomment the next lines if you want to use them
#innodb_thread_concurrency=5
NOTE: You will want to check the Maia
install docs for
the latest info on all of the above.
Maintenance Scripts and Templates
Maia Mailguard relies on number of Perl maintenance scripts to keep things
humming. These, along with templates, are available in the Maia installation
archive. We copy them into place and tighten up permissions:
mkdir -p /var/spool/amavisd/maia
cp -a /path/to/maia-1.0.2c/scripts /var/spool/amavisd/maia
cp -a /path/to/maia-1.0.2c/templates /var/spool/amavisd/maia
chown -R amavis:amavis /var/spool/amavisd/maia
chmod 640 /var/spool/amavisd/maia/templates/*.tpl
chmod 750 /var/spool/amavisd/maia/scripts/*.pl
The scripts all expect to read configuration bits from /etc/maia.conf, a
sample provided in the Maia install archive:
cp /path/to/maia-1.0.2c/maia.conf /etc/maia.conf
We need to ensure the database connection information is correct, auth set
to ldap, and that we re-path around our amavisd home:
$dsn = "DBI:mysql:maia:mailguarddb.madstop.edu:3306";
$password = "password";
$script_dir = "/var/spool/amavisd/maia/scripts";
$auth_method = "ldap";
$pid_file = "/var/spool/amavisd/.process-quarantine.pid";
#$key_file = "/var/spool/amavisd/maia.key";
$report_options = 0;
$base_url = "http://mailguard.madstop.edu/";
$template_dir = "/var/spool/amavisd/maia/templates";
NOTE: We comment out the key file. This is an interesting option if we desire
encryption on all messages stored in the database.
The scripts will run out of cron at various periods. This example root
cron schedule will capture output to syslog:
0 0 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/send-quarantine-reminders.pl | logger -t maia"
0 0 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/send-quarantine-digests.pl | logger -t maia"
5 * * * * su - amavis -c "/var/spool/amavisd/maia/scripts/stats-snapshot.pl | logger -t maia"
10 */3 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/process-quarantine.pl | logger -t maia"
10 0 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/expire-quarantine-cache.pl | logger -t maia"
NOTE: There is a load-sa-rules script that also needs to run, and we will
handle that during SpamAssassin setup.
Antivirus: ClamAV
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
clamav
127.0.0.1
qmail-inside
port 10025
Antivirus is a core aspect of email content-scanning, and we configured Maia
to use ClamAV.
Installation
Our initial amavisd-new yum install should have taken care of this, but if not:
yum install clamav clamav-db clamd
This installation stores the database files in /var/lib/clamav, and is
configured in /etc/clamd.d
Configuration: /etc/clamd.d/amavisd.conf
The defaults are mostly fine, but we also turn on file-based logging into our
amavisd log directory:
LogFile /var/log/amavis/clamd.amavis
LogFileMaxSize 0
LogTime yes
NOTE: Because amavisd initiates scanning as the amavis user, hence an
amavisd.conf file in clamd.d
Updates Configuration: /etc/freshclam.conf
An anti-virus solution is nothing without updates. Typically this will run
as the clam user and update the database in /var/lib/clamav. Make sure to
comment out the Example line. Salt to taste:
#Example
Updates are downloaded using the freshclam binary. Our yum install may have
dropped a file in /etc/cron.daily to handle this. Either use that, or schedule
in cron with crontab -e
as root. The following updates at 17
minutes after every hour:
17 * * * * /usr/bin/freshclam
NOTE: Remember to comment out everything in crond.daily if you use this. If
you simply remove the file, another update might put it back.
Third-party Updates
Because of the flexibility and open nature of clamav, third-party virus
definitions are available. We will pull down
Sanesecurity clamav
definitions. Sanesecurity provides several
download
scripts, and we will use the
second one by Gerard.
NOTE: The first one works great but no longer seems to be available.
Download the script, untar it, install it:
tar -zxvf scamp-5.3b.tar.gz
cd scamp-5.3b
cp scamp.sh /usr/local/bin
cp scamp.1 /usr/local/share/man/man1
Now create the config file by running scamp.sh. It will create an
/etc/scamp/default file like the below. We follow its suggestions most of the
way but need to match up directory locations and the clam user.
SCAMP_VERSION=5.3b
CLAMAV_DB=/var/lib/clamav
T_DIR=/var/lib/clamav/tmp
C_GROUP=clam
C_PID=/var/run/amavisd/clamd.pid
C_USER=clam
GET_LDB=1
GET_MALWARE=1
GET_MSRBL=0
GET_SANE=1
GET_SECURITE=4
GET_WINNOW=1
gpg_key_url=http://www.sanesecurity.net/publickey.gpg
L_TYPE=0
MK_LOG=1
MSRBL=rsync://rsync.mirror.msrbl.com/msrbl/
msrbl_Images=MSRBL-Images.hdb
msrbl_SPAM=MSRBL-SPAM.ndb
msrbl_SPAM_CR=MSRBL-SPAM-CR.ndb
MSR_DIR=/var/lib/clamav/tmp/msr
MW_DIR=/var/lib/clamav/tmp/malware
MW_FILE=mbl.ndb
MW_URL=http://www.malwarepatrol.com.br/cgi/submit?action=list_clamav_ext
RELOAD=0
REST=0
SANE=rsync://rsync.sanesecurity.net/sanesecurity
SANE_DB=/var/lib/clamav/tmp/sane
SI_DIR=/var/lib/clamav/tmp/securite
SYS_LOG=1
WPC=3
W_SUM=0
We now schedule this in cron with crontab -e
as root. The
following updates at 19 minutes after every hour:
19 * * * * /usr/local/bin/scamp.sh
Update Schedules
We noted a typical cron entry for each of our update sources: the official
clam sources and our third-party locations. Throughout, we disabled in-script
randomization and auto-updates because we want to manually reload clamd once
we pull down updates from all sources on our schedule. For example:
17 * * * * /usr/bin/freshclam
19 * * * * /usr/local/bin/scamp.sh
25 * * * * /etc/init.d/clamd.amavisd reload
Anti-spam: SpamAssassin
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
clamav
spamassassin
127.0.0.1
qmail-inside
port 10025
Anti-spam is the other core aspect of email content-scanning, and we
configured Maia to use SpamAssassin.
Installation
Our initial amavisd-new yum install should have taken care of this, but if
not:
yum install spamassassin
This installation stores the rules files in /usr/share/spamassassin, and is
configured in /etc/mail/spamassassin.
Configuration: /etc/mail/spamassassin/local.cf
The default configuration is sparse. In our case, settings in amavisd.conf
override several things, and user preferences out of the Maia Mailguard
database determine spam threshholds. However, we will tune some rules based on
experience, and it is critical that we do the following:
- Override the rules that govern RBL checks so that they query our
fake local .dnsbl domain instead of nailing the Internet. Aside from increased
performance, this is more important for some blocklists, as their terms of
service might shut you out if you keep hitting their public servers.
- Configure Bayes and the Autowhitelist to use a MySQL database to allow
for easy scaling to multiple mail exchangers and to reduce filesystem
contention. Bayes is one of the main strengths of the
Maia Mailguard solution, as users confirming message status allows for
site-wide learning of ham/spam tendencies. Autowhitelist (unfortunately named)
is an automatic heuristic system for adjusting spam scores up/down based on
previous sender behavior. Tables in the format SpamAssassin expects were
installed in the maia database we already installed.
NOTE: While we could consider turning off Autowhitelist, we definitely want
Bayes running. In any case, both features run off of standalone databases on
the filesystem by default. This may be perfectly suitable for small sites
and/or those with no plans for multiple mail exchangers. But since the tables
already exist in the databse, we may as well use them and avoid any potential
for filesystem performance issues.
This configuration handles the above, and provides other tweaks gleaned from
experience:
# These values can be overridden by editing ~/.spamassassin/user_prefs.cf
# (see spamassassin(1) for details)
# These should be safe assumptions and allow for simple visual sifting
# without risking lost emails.
required_hits 5
report_safe 0
#rewrite_header Subject ***SPAM***
ok_languages en
ok_locales en
# Bayes
use_bayes 1
bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn DBI:mysql:maia:mailguarddb.madstop.edu:3306
bayes_sql_username amavis
bayes_sql_password password
bayes_sql_override_username amavis
bayes_auto_expire 0
bayes_auto_learn 0
#bayes_auto_learn_threshold_nonspam -0.5
#bayes_auto_learn_threshold_spam 10.0
bayes_ignore_header ReSent-Date
bayes_ignore_header ReSent-From
bayes_ignore_header ReSent-Message-ID
bayes_ignore_header ReSent-Subject
bayes_ignore_header ReSent-To
bayes_ignore_header Resent-Date
bayes_ignore_header Resent-From
bayes_ignore_header Resent-Message-ID
bayes_ignore_header Resent-Subject
bayes_ignore_header Resent-To
bayes_ignore_header X-Received-From-IP
bayes_ignore_header X-Virus-Scanned
bayes_ignore_header X-Spam-Status
bayes_ignore_header X-Spam-Level
bayes_ignore_header X-Sender
bayes_ignore_header X-Mailer
# Auto-whitelist
use_auto_whitelist 1
auto_whitelist_factory Mail::SpamAssassin::SQLBasedAddrList
user_awl_dsn DBI:mysql:maia:mailguarddb.madstop.edu:3306
user_awl_sql_username amavis
user_awl_sql_password password
# Network test settings
dns_available test: madstop.edu
skip_rbl_checks 1
rbl_timeout 10
use_pyzor 0
use_razor2 0
# Custom scores
score URIBL_SBL 0
score URIBL_SC_SURBL 0
score URIBL_WS_SURBL 0
score URIBL_PH_SURBL 0
score URIBL_OB_SURBL 0
score URIBL_AB_SURBL 0
score URIBL_JP_SURBL 0
score URIBL_BLACK 0
score URIBL_GREY 0
score URIBL_RED 0
score BAYES_00 0
score BAYES_05 0
score BAYES_20 0
score BAYES_40 0.5
score BAYES_50 1
score BAYES_60 2
score BAYES_80 3
score BAYES_95 4
score BAYES_99 5
# Custom rule to enable local sbl
uridnsbl URIBL_SBL sbl.dnsbl. TXT
body URIBL_SBL eval:check_uridnsbl('URIBL_SBL')
describe URIBL_SBL Contains an URL listed in the SBL blocklist
score URIBL_SBL 10
tflags URIBL_SBL net
# Custom rule to enable local xbl (no xbl period for that matter)
# Note: Justin Mason on the SA mailing list said the reason XBL was not included
# was that "it doesn't correlate well with spam." Since these are mostly infected
# machines rather than those that host spam domains, it is fairly useless at the moment,
# but someone else mentioned past experiments with bot networks of web servers and
# redirectors. We shall see.
#uridnsbl URIBL_XBL xbl.dnsbl. TXT
#body URIBL_XBL eval:check_uridnsbl('URIBL_XBL')
#describe URIBL_XBL Contains an URL listed in the XBL blocklist
#score URIBL_XBL 6
#tflags URIBL_XBL net
There are some other configuration bits remaining. In
/etc/mail/spamassassin/v310.pre, we need to enable a few modules:
loadplugin Mail::SpamAssassin::Plugin::AWL
loadplugin Mail::SpamAssassin::Plugin::TextCat
Third-Party Plugins
We have run some third-party plugins over the years, and each has their own
installation method. I will briefly cover a few of them here:
- FuzzyOcr: http://fuzzyocr.own-hero.net/ (no longer maintained as of 2009-06-01)
- - combat image spam using optical character recognition
- PDFInfo: http://www.rulesemporium.com/plugins.htm#pdfinfo (no longer available)
- - combat pdf spam using a variety of rules obtained from the pdf information
NOTE: While we have used these in the past with some success, given the current
state of the projects (as of late 2012), we will not cover installation.
Updates
SpamAssassin rules do see updates, and our Maia Mailguard setup requires
some special consideration. The following root cron schedule handles updates,
and also loading rules into the Maia Mailguard database so they can be
interpreted at scanning time and for display in the web interface:
30 1 * * * /usr/bin/sa-update && /var/spool/amavisd/maia/scripts/load-sa-rules.pl
40 1 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/maiadbtool.pl --expire-bayes | logger -t spamassassin"
50 1 * * * su - amavis -c "/var/spool/amavisd/maia/scripts/maiadbtool.pl --prune-awl --debug | logger -t spamassassin"
Additionally, we use facilities provided by the maiadbtool to prune the
Bayes and Autowhitelist information in the database.
Scalability
10.137.0.1
qmail-outside
port 25
rbldnsd
greetdelay
validrcptto
127.0.0.1
qmail-fixup
port 10023
127.0.0.1
maia
port 10024
clamav
spamassassin
127.0.0.1
qmail-inside
port 10025
Scaling out the Internet mail exchangers is trivial because the ability to
define multiple MX records is inherent to DNS. We simply clone the setup and
add an MX record for each exchanger in the external zone. For example:
@ IN MX 0 mx1.madstop.edu.
@ IN MX 0 mx2.madstop.edu.
NOTE: @ is a BIND shortcut for the domain in question, here madstop.edu.
If this is not sufficient, some kind of high-availability setup on mx1 and
mx2 can be added as needs require.
Webmail
We have our mailstores providing IMAP and POP and content-scanning mail
exchangers for incoming SMTP. We will round our our client access needs
with a webmailer. We will do this on a separate machine, using the venerable
IMAP webmail client Squirrelmail. We
will also add the bits necessary for users to manage their server-side sieve
scripts over the Managesieve protocol. Lastly, we will finish off the Maia
Mailguard installation with its PHP frontend, and integrate it for single
sign-on with Squirrelmail.
Squirrelmail
Squirrelmail is a PHP application, which obviously requires a web
environment capable of PHP. We will assume Apache. And though packages
probably exist for your distribution, we will download the latest source. It
is fairly standard as far as PHP installations go, but we will highlight a few
tweaks and changes.
Installation
First download the latest package at
http://sourceforge.net/projects/squirrelmail/files and untar to a directory
of your choice. We will do it in the typical web root:
cd /var/www/html
tar -zxvf /path/to/squirrelmail-webmail-1.4.22.tar.gz
mv squirrelmail-webmail-1.4.22 squirrelmail
We will also create two more directories necessary for preferences and
attachments:
mkdir /var/sqdata
mkdir /var/sqattach
Configuration
We now have a squirrelmail folder in our webroot. Squirrelmail
configuration is stored in a typical config/config.php file, but there is also
a provided perl /config/conf.pl script to step through configuration in a
menu-driven interface. It is easiest to simply run conf.pl at least initially.
- Organization Preferences
- - customize to local site requirements
- Server Settings
- - change domain
- Server Settings/IMAP Settings
- - imap server points to our mailstore
- - server software set to dovecot
- - enable ssl if you wish
- Server Settings/SMTP Settings
- - smtp server points to our local exchanger
- Folder Defaults
- - trash, sent, drafts folders remove INBOX. prefix
- - default sub of inbox false
- - folder delete bypasses trash true
- General Options
- - data directory /var/sqdata
- - attachment directory /var/sqattach
- - force usernames lowercase true
- - hide sm attributions true
- - allow server-side sorting true
- - php session name PHPSESSID
NOTE: Recent SquirrelMail versions have an option at the main menu to set
pre-defined settings for specific IMAP servers. That may be worth a spin.
NOTE: Older SquirrelMail versions defaulted the data and attachment
directories inside the source tree. Recent squirrelmails default this outside
the tree. In any event make sure the paths are outside the tree and therefore
outside the web server document root.
NOTE: Changing the PHP session name is only necessary for tying
authentication to a patched Maia Mailguard, and does no harm either way.
Plugins
Squirrelmail offers maximal flexibility through its plugin architecture. We
make use of several, including some of our own. This list includes some we
find useful, and following sections will detail setup for some of the more
generally applicable ones. Review the list, check the Squirrelmail site, and
salt to taste:
- compatibility
- - provides backward and forward compatiblity for various plugins
- msg_flags
- - visual enhancements to bearmail (requires patching the squirrelmail source)
- image_buttons
- - convert top row text links to buttons based on user preference
- retrieveuserdata
- - retrieve user data from ldap upon first login to fill in base preferences
- filters
- - disabled, included in base squirrelmail but we will use avelseive instead
- avelsieve (and javascript_libs)
- - managesieve filters interface to configure server-side filtering
- login_check
- - prevent multiple tabs/browsers in same session in effort to prevent preference cross-contamination
- squirrel_logger
- - more logging info
- html_inject
- - custom plugin to stuff html into various places in the interface
- phishhook
- - custom plugin to lock out users based on phishy heuristics
- login_log
- - custom plugin to log user logins in digestible form for troubleshooting and for phishhook
- - must come after the phishhook plugin in the enable list for phishhook to operate properly
Plugin: Avelsieve
Dovecot on the mailstore also provides a Managesieve server running on port
2000. Managesieve is a standard network protocol to allow for sieve capability
negotiation and management of server-side sieve filters. We will add
Avelsieve to SquirrelMail to take
advantage of it.
Download the latest development package at
http://code.uoa.gr/p/avelsieve/download.php
and untar it into the squirrelmail plugins directory of your choice. We will
also need the javascript_libs plugin for full functionality:
cd /path/to/squirrelmail/plugins
wget http://email.uoa.gr/download/squirrelmail/avelsieve/avelsieve-1.9.9.tar.gz
tar -zxvf avelsieve-1.9.9.tar.gz
wget http://email.uoa.gr/download/squirrelmail/javascript_libs/javascript_libs-0.1.2.tar.gz
tar -zxvf javascript_libs-0.1.2.tar.gz
NOTE: We would normally take the stable package, but the development package
is much more up-to-date. We have used it for years.
Configuration is straightforward starting from the sample config:
cd avelsieve
cp config_sample.php config.php
The following will turn on debugging, and disable a few features we do not
want exposed as an example:
define('AVELSIEVE_DEBUG', 2);
$disable_avelsieve_capabilities = array("notify", "date", "reject");
We need to run the main squirrelmail config/conf.pl
and enable
the plugin:
- Plugins
- XX. avelsieve
- YY. javascript_libs
Plugin: Login Log
Login
Log is a custom plugin we developed to provide an easy to parse log of user
logins, one-per-line.
cd /path/to/squirrelmail/plugins
wget http://fritz.potsdam.edu/projects/squirrelmail_plugins/login_log-1.0.0-1.4.x.tar.gz
tar -zxvf login_log-1.0.0-1.4.x.tar.gz
NOTE: While this plugin provides a concise listing of login activity, the
Squirrel Logger
plugin vastly exceeds this if you are looking for more detail. For now,
Phishook currently requires login_log. You may not find any of this useful.
Configuration consists merely of setting the log file location in
config.php:
$login_log_file = '/var/log/squirrelmail/login_log';
NOTE: The apache user will require write access to this directory.
We need to run the main squirrelmail config/conf.pl
and enable
the plugin:
- Plugins
- XX. login_log
NOTE: The login_log plugin must come after the phishhook function in the
plugin list. This is because they are both called on the same hook, and if
login_log is first, phishhook will see "this" login as the "last" login and
never fire.
Plugin: Phishhook
NOTE: Phishhook has been superseded by various tests in Qmail-Skim during
SMTP-Auth on the local mail exchanger (as of mid 2013). To be documented.
Phishhook is a site-specific plugin that checks for two things we have
determined to be phishy behavior:
- Has this user's country of login changed within the last few hours?
(country-hopping)
- Is the user mailing a large batch of recipients using a from address that
is not their standard username? (mass-mailing)
If either of these conditions are detected, based on a number of
configurable variables, the plugin runs some associated Perl to reach out
across a range of systems to do the following:
- Create a ticket in our RT tracking
system.
- Blacklist the user's IP address/range in the main border firewall.
- Scramble the account password
- Add them to an LDAP group marking them as someone requiring education and
manual intervention to regain account access from Helpdesk staff.
Due to its highly site-specific behavior, Phishhook is not (yet) generally
available, but hopefully this gives an idea of some of the possibilities.
Plugin: HTML Inject
HTML
Inject is a custom plugin we developed to target various Squirrelmail hooks
to provide easy access for HTML insertion.
cd /path/to/squirrelmail/plugins
wget http://fritz.potsdam.edu/projects/squirrelmail_plugins/html_inject-1.0.0-1.4.x.tar.gz
tar -zxvf html_inject-1.0.0-1.4.x.tar.gz
Configuration consists of picking which locations (hooks) to enable in
setup.php, and then altering the associated function to display what we wish.
This example enables the menuline hook in the main init function, and then
calls a function to display a link, with the result that we have a Mailguard
link among the top row of buttons in the main Squirrelmail display:
function squirrelmail_plugin_init_html_inject() {
global $squirrelmail_plugin_hooks;
$squirrelmail_plugin_hooks['menuline']['html_inject'] = 'inject_menuline';
// ...
}
function inject_menuline() {
displayInternalLink('mailguard/xlogin.php',_("Mailguard"),'mailguard');
echo " \n";
}
NOTE: We actually target the xlogin.php file within Mailguard, since that
will allow for single-sign on if we setup integration properly (next).
Mailguard Web
After all of our work on the mail exchanger, we are left with one remaining
piece to setup for Maia Mailguard: the web interface. We install this on our
webmail box alongside Squirrelmail.
Installation
The
install documentation
is very comprehensive and is the best resource for setting things up. We will
concentrate only on the basics, and the few things that deviate or that we
consider best practice for our setup.
The web scripts are in the php subdirectory of the Maia install archive. We
already downloaded this for work on the mail exchanger, or grab it from
http://www.maiamailguard.com/download.php. We will extract the web files
alongside squirrelmail in the typical web root:
cp -a /path/to/maia-1.0.2c/php /var/www/html/mailguard
Maia Mailguard relis on the Smarty template
engine, and a variety of other php components. We specifically expect LDAP
support. Your system may vary, but this command will hit a lot of them:
yum install php php-pear php-mysql php-gd php-Smarty php-imap php-ldap php-mcrypt
The template engine requires that the user under which the web server runs
has write access to the templates directory. You may see nothing but a blank
page otherwise:
cd /var/www/html/mailguard
chown -R apache:apache themes
Configuration
A PHP standard config.php stores configuration information, and Maia
Mailguard ships with a sample starting config:
cp config.php.dist config.php
We must at least configure our database connection information and user
authentication parameters. We will tweak a few other things as well:
$loglevel = PEAR_LOG_INFO; // messages >= this level are sent to php log
$default_session_timeout = 300; // 15 minute default
$maia_sql_dsn = "mysql://amavis:password@tcp(mailguarddb.madstop.edu:3306)/maia";
The protection array requires some deep thought and excellent hand-eye
coordination to get right. This sets the four pre-defined protection levels
users can select in the web interface. A full description of all of this is
available in the in the
install
documentation. We define the four levels thus:
- off => spam scanning disabled
- low => spam scanning enabled, spam at score 5 is passed through with
modified subject
- medium => spam scanning enabled, spam at score 5 is passed through with
modified subject, mail with invalid headers is quarantined
- high => spam scanning enabled, spam at score 5 is quarantined, mail with
invalid headers is quarantined
In all cases, we discard viruses and files with banned attachments. We
decided there was no reason to quarantine these, least of all because we did
not want users faced with the decision to release the horde upon themselves.
In line with this, we actually modified the template we assign to all users to
hide these other quarantine areas in the web interface.
All these decisions weigh out thus in the protection array:
$protection = array(
'off' => array ('N','Y','N','Y','N','Y','N','Y','Y','N','Y','N','N','999','999','999'),
'low' => array ('N','Y','N','Y','N','N','N','N','Y','N','Y','N','Y','-999','5','999'),
'medium' => array ('N','Y','N','N','N','N','N','N','Y','N','Y','N','Y','-999','5','999'),
'high' => array ('N','N','N','N','N','N','N','N','Y','N','Y','N','N','-999','5','5')
);
NOTE: Do yourself a favor and read up on the protection array in the install
documentation.
Lastly we configure authentication for LDAP:
$auth_method = "ldap";
$auth_ldap_server = "ldap.madstop.edu";
$auth_ldap_password = "";
$auth_ldap_query = "uid=%%USER%%";
$auth_ldap_bind_dn = "o=madstop.edu";
$auth_ldap_base_dn = "o=madstop.edu";
$auth_ldap_attribute = "mail";
Testing PHP and Database
The best way to test that things are in order is to go to the
admin/configtest.php page. This will present a nice report on the status of
all of the needed pieces. You can probably assume something will be missing.
Domain Configuration
The next steps will involve logging in to the web interface to create an
administrator:
http://mailguard.madstop.edu/mailguard/login.php?super=register
From there, it is on to create a domain account that will serve as a default
for all users of that domain. User accounts are created at time of login, or
at time of email receipt.
As an administrator:
Admin -> Domains -> New domain: @madstop.edu -> Add Domain
When configuring the domain, it is best to match all of the settings to
those of one of the protection levels we pre-configured earlier. If it matches
exactly, the "Current Protection Level" box on the home page will have the
level selected when users first login, rather than a confusing note about a
custom level being in use. If users deviate from the standard levels, it is by
their own choice. We expect that users will want "High" protection and match
it accordingly.
SquirrelMail Integration
If the SquirrelMail and Maia Mailguard web scripts are installed in such a
way that they can share a PHP session, as we have done here, my
session auth patch to Maia Mailguard
will allow for seamless login for already authenticated SquirrelMail users.
NOTE: See the
SquirrelMail Maia Integraton writeup for more info.
There are some limitations with this setup currently:
- The Maia PHP scripts must run on the same box as Squirrelmail so they may
share a PHP session
- The Maia patch presented here only works with LDAP authentication
($auth_method = "ldap")
- The LDAP server must allow retrieval of a user's email attribute via an
anonymous bind
Download and apply the patch:
wget http://fritz.potsdam.edu/projects/maia_extras/maia-1.0.2a-sessauth-1.0.0.patch
cd /path/to/maia/php
patch -p1 </path/to/patch
It is easy to test whether this is working by logging into SquirrelMail, and
then browsing directly to http://url/to/maikguard/xlogin.php (the link we
placed with the html_inject plugin). If the patch is not applied, or not
working, or you are not authenticated already as a Squirremail user, you should
get a "Login for user failed. ( )" message. If it works, you should skip right
in to the Maia welcome page as your Squirrelmail user. You should also test
that normal logins work by going to your main Maia URL and logging in normally.
You should also note that logging out of the Maia application does not end your
Squirrelmail session.
Scalability
Scaling out webmail is straightforward, since we just need to clone the
machine. Mailguard reads all of its configuration and user preferences/data
out of the database. SquirrelMail requires a bit more work, since user
preferences are stored on the filesystem. There are plugins and enhancements
to store this in a database instead.
Other Topics
Storage
Email is a very IO intensive service, and storage must be capable of
sustained random access loads. We rely on local SCSI and SAS disk for qmail
queues and Dovecot indexes. In the past we have stored user mail on direct
attached U320 SCSI drives, and ATAoE SATA drives in various RAID
configurations.
Current storage is ATAoE SAS drives in RAID10 configurations. In our
primary mailstore, home directory locations are symlinks, pointing to real
homes on one of three LUNs. This was an effort to introduce more drive
spindles to the storage equation on older drives, and is probably no longer
necessary.
Perhaps this gives an idea of the need for storage to be elastic to keep up
with changing loads. Things will vary greatly from one configuration, site,
and network to the next.
Account Management
We took great pains to ensure that user homes and Maildirs are created
automatically on qmail-ldap delivery, or POP/IMAP login. How these user
accounts are actually created and managed deserves a page of its own. We have
users stored in two information stores: the student information system is the
master, and a purpose-built creation and syncronization system (CASL) keeps
LDAP up-to-date. A secondary system handles user access changes over time
(WAM), changing email service groups and the qmail accountStatus attribute
based on local policy. The ZeusAdmin web
interface allows for user and alias creation and management. All make use of
our extensive Perl LDAP library.
Calendaring
Calendar solutions that work well integrate tightly with email. We are
working to replace our proprietary vendor solution with
SOGo, an open-source solution based on the
CalDAV protocol. This merits mention because SOGo offers a rich web interface
for email as well as calendaring, and it will probably come to replace our
webmailer.
Future Work
The evolving landscape of email inanity now includes phish, turning our own
users against us. In particular, we see our webmailer used to send outgoing
spam once an account has been compromised. For years we have included an
in-house Squirrelmail plugin "phishhook," described in detail in our webmailer
setup. That has kept the lid on a lot of phish behavior, and also doubles as a
bit of a honeypot for compromised accounts: webmail is an attractive target.
Work on the phish problem proceeds in the guise of qmail-skim. Qmail-skim
is a qmail-queue replacement that incorporates a number of heuristics that we
have determined over the years to be indicative of phish behavior. It can
easily be inserted into the qmail chain on qmail instances that include the
qmailqueue-patch (all of
our installs). The advantage of tying this in on our qmail-outside instances
is that if it determines a message should be rejected, it is rejected during
the SMTP conversation. It is a work in progress.
A: Scripts
A comprehensive listing of all of the scripting and custom coding that plays
an essential role in the core of the email system. Most of these are Perl
scripts, and most (not all) were authored by me. Since many of them play a
part across multiple components, we document them separately.
- chkpassldap
- My implementation of DJB's checkpassword interface, used on both the
mailstores and the mail exchangers for Dovecot and SMTP authentication.
- dovelogin
- A script to create user homes and Maildirs on Dovecot login. It also
touches a .last_login_imap or .last_login_pop file in in user homes so that
we can track last login times. It also drops these in a separate cache
location for LDAP update.
- doveloginsync
- Updates LDAP user leaf custom attribute "mailLastLoginTime" from the
cache created by dovelogin. Will also set accountStatus=active
(qmail-ldap attribute that determines deliverability) for users who have
left their accounts fallow long enough that they have been disabled.
- hermesbackups
- Complements double-delivery for mail backup on the backup boxes.
Responsible both for backup up of user Sent folders from the mailstores and
culling old mail from the backup set based on a configurable number of days
(30).
- maialinksync
- Links qmail aliases to user accounts in Maia, using the provided
maiadbtool to interace with the database directly. Aliases that cannot be
linked, either because they are a list or to a remote address, are assigned
to an administrator account.
- mkvalidrcptto
- Builds the validrcptto.cdb file for the recipient checking feature
provided by the JMS patch on the Internet mail exchangers. Pulls in LDAP
users with accountStatus=active (as determined by the WAM system), LDAP
aliases, filesystem dotqmail files in user homes, and filesystem aliases on
the mailstores.
- qmqtool
- An incredibly handy tool written by Jeremy Kister to administer qmail
queues. We use it on numerous qmail installs for deleting the occassional
swathe of mail originating from a phished user (the $qmail variable must be
altered to reflect the appropriate qmail instance on multi-qmail boxes).
B: Ucspi-tcp Installation
http://cr.yp.to/ucspi-tcp.html
One of two crucial pieces of DJB software (along with daemontools) for qmail,
ucspi-tcp provides the tcpserver which listen on the network on behalf of
qmail-smtp/qmail-qmqpd/ofmipd, etc.
Download the source tarball to a directory of your choice:
http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
Also pull down the errno patch here, or available in the other-patches
directory of the netqmail distribution:
diff -u ucspi-tcp-0.88.old/error.h ucspi-tcp-0.88/error.h
--- ucspi-tcp-0.88.old/error.h 2000-03-18 09:18:20.000000000 -0600
+++ ucspi-tcp-0.88/error.h 2003-01-08 13:39:12.000000000 -0600
@@ -1,7 +1,7 @@
#ifndef ERROR_H
#define ERROR_H
-extern int errno;
+#include <errno.h>
extern int error_intr;
extern int error_nomem;
Unpack the ucspi-tcp source and apply patches.
tar -zxvf ucspi-tcp-0.88.tar.gz
cd ucspi-tcp-0.88
patch -p1 < /path/to/ucspi-tcp-0.88.errno.patch
Installation.
make
make setup check
End result: Several binaries will end up in /usr/local/bin.
C: Daemontools Installation
http://cr.yp.to/daemontools.html
One of two crucial pieces of DJB software (along with ucspi-tcp) for qmail,
daemontools provides the processes necessary for program initialization and
daemonization.
Download the source tarball to a directory of your choice:
http://cr.yp.to/daemontools/daemontools-0.76.tar.gz
Also pull down the errno patch here, or available in the other-patches directory of the netqmail distribution:
diff -ur daemontools-0.76.old/src/error.h daemontools-0.76/src/error.h
--- daemontools-0.76.old/src/error.h 2001-07-12 11:49:49.000000000 -0500
+++ daemontools-0.76/src/error.h 2003-01-09 21:52:01.000000000 -0600
@@ -3,7 +3,7 @@
#ifndef ERROR_H
#define ERROR_H
-extern int errno;
+#include <errno.h>
extern int error_intr;
extern int error_nomem;
Unpack the daemontools source and apply patches. The deamontools install is a bit peculiar and should be unpacked into /package where it will create an admin/deamontools subdirectory.
mkdir -p /package
chmod 1755 /package
cd /package
tar -zxvf /path/to/daemontools-0.76.tar.gz
cd /package/admin/daemontools-0.76
patch -p1 < /path/to/daemontools-0.76.errno.patch
Installation.
cd /package/admin/daemontools-0.76
./package/install
The daemontools install includes svscan, which is configured to run via init. If your system does not use old-school init, as modern Fedora do not for instance, other changes must be made.
Init
The daemontools installer already creates the following entry in /etc/inittab which should work fine for most init-based systems:
SV:123456:respawn:/command/svscanboot
NOTE: If you want to keep qmail services down at boot, you must ensure symlinks in /service are deleted.
Upstart
The following /etc/event.d/svscanboot will work for Fedora 9+ upstart-based systems:
# svscanboot
start on runlevel 2
start on runlevel 3
start on runlevel 4
start on runlevel 5
stop on runlevel 0
stop on runlevel 1
stop on runlevel 6
console output
exec /command/svscanboot
respawn
Upstart configuration changed in later versions, and the following /etc/init/svscanboot.conf will work for Fedora 13+ upstart-based systems:
# svscanboot
start on runlevel [2345]
stop on runlevel [016]
console output
exec /command/svscanboot
respawn
Fire it up:
# initctl start svscanboot
NOTE: If you want to keep qmail services down at boot, simply adjust the
start on and stop on configs accordingly.
End result: Several symlinks will end up in /usr/local/bin, which point to
symlinks in /command, which point to binaries in
/package/admin/daemontools-0.76/command. Also, a /service directory will be
created, into which we symlink our services.
D: Mess822 Installation
http://cr.yp.to/mess822.html.
An interesting piece of DJB software, mess822 provides several tools for
qmail pertaining to address handling and rewriting.
Download the source tarball to a directory of your choice:
http://cr.yp.to/software/mess822-0.58.tar.gz
Also pull down the errno patch here, or available in the other-patches directory of the netqmail distribution:
diff -u mess822-0.58.old/cdb_seek.c mess822-0.58/cdb_seek.c
--- mess822-0.58.old/cdb_seek.c 1998-09-04 21:33:37.000000000 -0500
+++ mess822-0.58/cdb_seek.c 2003-01-13 23:17:30.000000000 -0600
@@ -1,6 +1,5 @@
#include
#include
-extern int errno;
#include "cdb.h"
#ifndef SEEK_SET
diff -u mess822-0.58.old/error.h mess822-0.58/error.h
--- mess822-0.58.old/error.h 1998-09-04 21:33:37.000000000 -0500
+++ mess822-0.58/error.h 2003-01-13 23:18:09.000000000 -0600
@@ -1,7 +1,7 @@
#ifndef ERROR_H
#define ERROR_H
-extern int errno;
+#include
extern int error_intr;
extern int error_nomem;
diff -u mess822-0.58.old/leapsecs_read.c mess822-0.58/leapsecs_read.c
--- mess822-0.58.old/leapsecs_read.c 1998-09-04 21:33:37.000000000 -0500
+++ mess822-0.58/leapsecs_read.c 2003-01-13 23:19:17.000000000 -0600
@@ -2,7 +2,6 @@
#include
#include
#include
-extern int errno;
#include "tai.h"
#include "leapsecs.h"
Unpack the mess822 source and apply patches.
tar -zxvf mess822-0.58.tar.gz
cd mess822-0.58.tar.gz
patch -p1 < /path/to/mess822-0.58.errno.patch
The mess822 package assumes /var/qmail, and we change this in conf-qmail on
multi-qmail boxes.
Installation.
make
make setup check
E: Netqmail Installation
http://www.qmail.org/netqmail
"A motley krewe of qmail contributors (see the README) has put together a
netqmail-1.06 distribution of qmail. It is derived from Daniel Bernstein's
qmail-1.03 plus bug fixes, a few feature enhancements, and some documentation."
Source
Download the source tarballs to a directory of your choice:
http://www.qmail.org/netqmail-1.06.tar.gz
Download additional qmail patches:
- big-concurrency: http://qmail.org/big-concurrency.patch
- - raises concurrency limits allowing more deliveries at once
- ext-todo: http://www.nrg4u.com/qmail/ext_todo-20030105.patch
- - eliminates bottleneck by separating todo processing into a separate binary
- dns patch for large queries: http://www.ckdhr.com/ckd/qmail-103.patch
- - handles sites that return large responses to mx queries
- doublebounce-trim: http://qmail.org/doublebounce-trim.patch
- - prevents bounces of bounces from being queued using doublebounceto control file
- qmail-inject null sender fix: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch
- - fixes qmail inject so it no longer tacks the defaultdomain onto the null sender, a la <>@madstop.edu
Unpacking and Patching
Unpack the qmail source and apply patches. If applied in the following
order, they should apply cleanly to netqmail-1.06 with nothing more than small
offsets:
tar -zxvf netqmail-1.06.tar.gz
cd netqmail-1.06
patch -p1 < /path/to/big-concurrency.patch
patch -p1 < /path/to/ext_todo-20030105.patch
patch -p1 < /path/to/qmail-103.patch # big dns
patch -p1 < /path/to/doublebounce-trim.patch
patch -p3 < /path/to/qmail-inject-null-sender.patch
Users and Directories
First, qmail needs a home:
mkdir /var/qmail
NOTE: This is defined in conf-qmail in the qmail source tree. This is what
we will change on multi-qmail boxes.
Qmail requires several users to be created for its various components.
There is an INSTALL.ids file in the qmail source tree for reference. Use the
following to keep the uidnumbers below 500: in the system range and out of the
user range:
groupadd -g 400 nofiles
useradd -u 400 -g nofiles -d /var/qmail/alias alias
useradd -u 401 -g nofiles -d /var/qmail qmaild
useradd -u 402 -g nofiles -d /var/qmail qmaill
useradd -u 403 -g nofiles -d /var/qmail qmailp
groupadd -g 401 qmail
useradd -u 404 -g qmail -d /var/qmail qmailq
useradd -u 405 -g qmail -d /var/qmail qmailr
useradd -u 406 -g qmail -d /var/qmail qmails
Building and Installation
We now compile and install qmail:
cd netqmail-1.06
make setup check
./config
F: Qmail JMS Combined Installation
http://qmail.jms1.net/patches/combined.shtml
John Simpson has created and maintained a mega patch set to qmail which
offers a plethora of features geared at Internet-facing SMTP. We use it for a
number of things, as outlined on the mail exchangers.
Source
Download the source tarball of stock qmail-1.03 to a directory of your choice:
qmail-1.03: http://cr.yp.to/software/qmail-1.03.tar.gz
Download the JMS combined patch. It includes many commonly employed qmail
patches, including most of those in netqmail, but we also want a few additional
ones:
- jms combined: http://qmail.jms1.net/patches/qmail-1.03-jms1.7.08.patch
- - provides several enhancements and edge smtp features
- qmail-inject null sender fix: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch
- - fixes qmail inject so it no longer tacks the defaultdomain onto the null sender, a la <>@madstop.edu
Unpacking and Patching
Unpack the qmail source and apply patches. If applied in the following
order, they should apply cleanly to qmail-1.03:
tar -zxvf qmail-1.03.tar.gz
cd qmail-1.03
patch -p1 < /path/to/qmail-1.03-jms1.7.08.patch
patch -p3 < /path/to/qmail-inject-null-sender.patch
Users and Directories
First, qmail needs a home:
mkdir /var/qmail
NOTE: This is defined in conf-qmail in the qmail source tree. This is what
we will change on multi-qmail boxes.
Qmail requires several users to be created for its various components.
There is an INSTALL.ids file in the qmail source tree for reference. Use the
following to keep the uidnumbers below 500: in the system range and out of the
user range:
groupadd -g 400 nofiles
useradd -u 400 -g nofiles -d /var/qmail/alias alias
useradd -u 401 -g nofiles -d /var/qmail qmaild
useradd -u 402 -g nofiles -d /var/qmail qmaill
useradd -u 403 -g nofiles -d /var/qmail qmailp
groupadd -g 401 qmail
useradd -u 404 -g qmail -d /var/qmail qmailq
useradd -u 405 -g qmail -d /var/qmail qmailr
useradd -u 406 -g qmail -d /var/qmail qmails
Building and Installation
We now compile and install qmail:
cd qmail-1.03
make setup check
./config
NOTE: you may need to install the openssl-devel libraries if make cannot find
openssl/ssl.h
G: Qmail-ldap Installation
http://www.nrg4u.com/
"qmail-ldap is a patch to qmail 1.03 to retrieve all user data from a
ldap-directory rather then from files on the disk. This allows easier
administration, especially in distributed environments. There is also
clustering support builtin making qmail-ldap very well suited for big mail
installations at ISPs."
Source
Download the source tarball of stock qmail-1.03 to a directory of your choice:
qmail-1.03: http://cr.yp.to/software/qmail-1.03.tar.gz
Download the qmail-ldap patch. It includes many commonly employed qmail
patches, such as most of those in netqmail, but we also want a few additional
ones:
- qmail-ldap: http://www.nrg4u.com/qmail/qmail-ldap-1.03-20120221.patch.gz
- - allows for retrieval of user data from ldap and application clustering
- doublebounce-trim: http://qmail.org/doublebounce-trim.patch
- - prevents bounces of bounces from being queued using doublebounceto control file
- qmail-inject null sender fix: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch
- - fixes qmail inject so it no longer tacks the defaultdomain onto the null sender, a la <>@madstop.edu
NOTE: We have run the 20060221 version of the patch for years. There is a
newer 20120221
version of the patch that is a convergence of some common features of JMS and
other qmail distros. It seems to require some Makefile touchup to disable the
auth_dovecot module in order to get it to compile and install cleanly. Your
mileage my vary.
Unpacking and Patching
Installation is covered more fully elsewhere, but in a nutshell:
tar -zxvf qmail-1.03.tar.gz
cd qmail-1.03
patch -p1 < /path/to/qmail-ldap-1.03-xxxxxxxx.patch
patch -p1 < /path/to/qmail-inject-null-sender.patch
Users and Directories
First, qmail needs a home:
mkdir /var/qmail
NOTE: This is defined in conf-qmail in the qmail source tree. This is what
we will change on multi-qmail boxes.
Qmail requires several users to be created for its various components.
There is an INSTALL.ids file in the qmail source tree for reference. Use the
following to keep the uidnumbers below 500: in the system range and out of the
user range:
groupadd -g 400 nofiles
useradd -u 400 -g nofiles -d /var/qmail/alias alias
useradd -u 401 -g nofiles -d /var/qmail qmaild
useradd -u 402 -g nofiles -d /var/qmail qmaill
useradd -u 403 -g nofiles -d /var/qmail qmailp
groupadd -g 401 qmail
useradd -u 404 -g qmail -d /var/qmail qmailq
useradd -u 405 -g qmail -d /var/qmail qmailr
useradd -u 406 -g qmail -d /var/qmail qmails
And for vmail setups, additionally:
groupadd -g 402 vmail
useradd -u 407 -g vmail vmail
Compile-time Configuraton: Makefile
Qmail-ldap is a somewhat unique qmail installation as it has a number of
features that can easily be left out of compilation entirely in the heavily
commented Makefile. There are a few to which you might pay particular
attention:
- LDAPFLAGS=-DALTQUEUE -DEXTERNAL_TODO -DDASH_EXT -DCLEARTEXTPASSWD -DQLDAP_CLUSTER
- - a number of core features
- LDAPLIBS=-L/usr/lib64 -lldap -llber
- - adjust as necessary
- LDAPINCLUDES=-I/usr/include
- - adjust as necessary
- MDIRMAKE=-DAUTOMAILDIRMAKE
- - enables auto-creation of user Maildirs on mail delivery
- HDIRMAKE=-DAUTOHOMEDIRMAKE
- - enables auto-creation of user home directories on mail delivery
- SHADOWLIBS=-lcrypt
- - may or may not be necessary
Compile-time LDAP Attributes: qmail-ldap.h
This file maps the queried ldap attributes for qmail-ldap, so you will have
to look things over and ensure they are set to LDAP attributes that make sense
for your local installation. Most of these will be provided with the
qmail-ldap LDAP schema, and others will be set to logical defaults. This is
the place to override them.
For example, LDAP_QMAILUID and LDAP_QMAILGID default to qmailUID and
qmailGID which are provided as part of the qmail-ldap ldap schema. If we want
to re-use our existing uidNumbers and gidNumbers rather than maintain yet more
attributes, we can do the following:
#define LDAP_QMAILUID "uidNumber"
#define LDAP_QMAILGID "gidNumber"
Building and Installation
We now compile and install qmail:
cd qmail-1.03
make setup check
./config
NOTE: You wil almost certainly require the openldap-devel libraries on your
system.
LDAP Schema
Qmail-ldap ships with an LDAP schema that needs to be installed into your LDAP
infrastructure. That is documented fully in the LDAP install documentation,
but to give an idea what kind of information is retrieved, the following is
sample output from the qmail-ldaplookup troubleshooting tool:
# /var/qmail/bin/qmail-ldaplookup -m hardyjm@madstop.edu
Searching ldap for: (|(mail=hardyjm@madstop.edu)(mailAlternateAddress=hardyjm@madstop.edu))
under dn: o=madstop.edu
Found 1 entry:
dn: uid=hardyjm,ou=People,o=madstop.edu
-------------------------------------------------------
objectClass: top
objectClass: account
objectClass: posixAccount
objectClass: sambaSamAccount
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgPerson
objectClass: spotperson
objectClass: qmailuser
objectClass: sunyPerson
mail: hardyjm@madstop.edu
mailAlternateAddress: hardy@madstop.edu
mailAlternateAddress: hardyjm-null@madstop.edu
uid: hardyjm
accountStatus: active
mailHost: hermes.madstop.edu
homeDirectory: /mnt/home/hardyjm
aliasEmpty: using default
qmailDotMode: both
uidNumber: 16814
gidNumber: 100
mailQuotaSize: 0 (unlimited)
mailQuotaCount: 0 (unlimited)
mailSizeMax: 0 (unlimited)
mailReplyText: undefined
As you can see, everything centers around the mail and mailAlternateAddress
attributes.
H: Qmail Configuration and Startup
Qmail relies on daemontools
run scripts for initialization. These scripts setup the environment for each
of the moving parts of qmail. For those pieces exposed to the network by
tcpserver (qmail-smtpd,
qmail-ofmipd, qmail-qmqpd, etc), tcpserver configuration can also set
environment variables, exposing different features and functions to different
networks.
The following qmail startup scripts, daemontools run scripts, and tcp
configs are stock examples based on those at
Life With Qmail, and as such, are
placed in /var/qmail (or qmail-fixup, inside, etc) and symlinked into the
daemontools /service directory. They are largely universal across all machines
in our setup. The exception are those that provide smtps/ofmpid/qmqpd
service. The stock example below for smtp serves as a good starting point, and
where this differs in our installs, it will be noted.
NOTE: The following assumes installation in /var/qmail. If this is a second
instance of qmail on the same box, that will be /var/qmail-fixup,
/var/qmail-inside, /var/qmail-forward, etc. Adjust accordingly.
Qmail Configuration: /var/qmail/control
Qmail is controlled by a variety of files in /var/qmail/control. The stock
qmail ./config creates the following control files. Many more controls are
available, and patches may provide even more. See man qmail-control.
- defaultdomain
- - qmail-inject appends this to any hostname without dots, default me
- locals
- - qmail-send delivers mail locally to current host for all listed domains, default me
- me
- - default for other hostname-related control files, ./config probably set this to your full hostname
- - qmail can survive with only this in the most basic install
- plusdomain
- - qmail-inject adds this name to any host name that ends with a plus sign, default me
- rcpthosts
- - qmail-smtpd will reject any evelope recipient address with a domain not listed in rcpthosts if rcpthosts exists
Qmail Startup Scripts
/var/qmail/rc
#!/bin/sh
# Using stdout for logging
# Using control/defaultdelivery from qmail-local to deliver messages by default
QMAIL=qmail
exec env - PATH="/var/$QMAIL/bin:$PATH" qmail-start "`cat /var/$QMAIL/control/defaultdelivery`"
NOTE: defaultdelivery is not a standard qmail control file, but is used by
our scripts
echo ./Maildir/ >/var/qmail/control/defaultdelivery
Set the proper perms and create the log directory:
chmod 755 /var/qmail/rc
mkdir /var/log/qmail
/var/qmail/bin/qmailctl
A handy script for exposing all of the commonly used administrative functions:
#!/bin/sh
# description: the qmail MTA
QMAIL=qmail
TCPRULES=tcp.smtp
PATH=/var/$QMAIL/bin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin
export PATH
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
case "$1" in
start)
echo "Starting $QMAIL"
if svok /service/$QMAIL-send ; then
svc -u /service/$QMAIL-send /service/$QMAIL-send/log
else
echo "qmail-send supervise not running"
fi
if svok /service/$QMAIL-smtpd ; then
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
else
echo "qmail-smtpd supervise not running"
fi
if [ -d /var/lock/subsys ]; then
touch /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
/var/$QMAIL/bin/qmail-qstat
;;
stop)
echo "Stopping $QMAIL..."
echo " qmail-smtpd"
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo " qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
if [ -f /var/lock/subsys/$QMAIL ]; then
rm /var/lock/subsys/$QMAIL
fi
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
/var/$QMAIL/bin/qmail-qstat
;;
stat)
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
/var/$QMAIL/bin/qmail-qstat
;;
doqueue|alrm|flush)
echo "Flushing timeout table and sending ALRM signal to qmail-send."
/var/$QMAIL/bin/qmail-tcpok
svc -a /service/$QMAIL-send
;;
queue)
/var/$QMAIL/bin/qmail-qstat
/var/$QMAIL/bin/qmail-qread
;;
reload|hup)
echo "Sending HUP signal to qmail-send."
svc -h /service/$QMAIL-send
;;
pause)
echo "Pausing qmail-send"
svc -p /service/$QMAIL-send
echo "Pausing qmail-smtpd"
svc -p /service/$QMAIL-smtpd
;;
cont)
echo "Continuing qmail-send"
svc -c /service/$QMAIL-send
echo "Continuing qmail-smtpd"
svc -c /service/$QMAIL-smtpd
;;
restart)
echo "Restarting $QMAIL:"
echo "* Stopping qmail-smtpd."
svc -d /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
echo "* Sending qmail-send SIGTERM and restarting."
svc -t /service/$QMAIL-send /service/$QMAIL-send/log
echo "* Restarting qmail-smtpd."
svc -u /service/$QMAIL-smtpd /service/$QMAIL-smtpd/log
sleep 1;
svstat /service/$QMAIL-send
svstat /service/$QMAIL-send/log
svstat /service/$QMAIL-smtpd
svstat /service/$QMAIL-smtpd/log
/var/$QMAIL/bin/qmail-qstat
;;
cdb)
tcprules /etc/$TCPRULES.cdb /etc/$TCPRULES.tmp < /etc/$TCPRULES
chmod 644 /etc/$TCPRULES.cdb
echo "Reloaded: /etc/$TCPRULES"
;;
sendstop)
echo "Stopping qmail-send"
svc -d /service/$QMAIL-send /service/$QMAIL-send/log
;;
help)
cat <<HELP
stop -- stops mail service (smtp connections refused, nothing goes out)
start -- starts mail service (smtp connection accepted, mail can go out)
pause -- temporarily stops mail service (connections accepted, nothing leaves)
cont -- continues paused mail service
stat -- displays status of mail service
cdb -- rebuild the tcpserver cdb file for smtp
restart -- stops and restarts smtp, sends qmail-send a TERM & restarts it
doqueue -- schedules queued messages for immediate delivery
reload -- sends qmail-send HUP, rereading locals and virtualdomains
queue -- shows status of queue
alrm -- same as doqueue
flush -- same as doqueue
hup -- same as reload
sendstop -- stop qmail-send
HELP
;;
*)
echo "Usage: $0 {start|stop|restart|doqueue|flush|reload|stat|pause|cont|cdb|queue|sendstop|help}"
exit 1
;;
esac
exit 0
NOTE: $QMAIL and $TCPRULES "name" the qmail instance to ease script re-use
NOTE: sendstop is another additon of mine to target sending shutdown
chmod 755 /var/qmail/bin/qmailctl
ln -s /var/qmail/bin/qmailctl /usr/local/bin
Daemontools Run Scripts
We create these inside the qmail tree and symlink them later into /service
mkdir -p /var/qmail/supervise/qmail-send/log
mkdir -p /var/qmail/supervise/qmail-smtpd/log
Qmail-send and logging
/var/qmail/supervise/qmail-send/run
#!/bin/sh
QMAIL=qmail
exec /var/$QMAIL/rc
/var/qmail/supervise/qmail-send/log/run
#!/bin/sh
QMAIL=qmail
exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t s16777215 n50 /var/log/$QMAIL
NOTE: ssize and nnum default to 99999 bytes and 10 log files so adjust accordingly
Qmail-smtpd and logging
/var/qmail/supervise/qmail-smtpd/run
#!/bin/sh
QMAIL=qmail
TCPRULES=tcp.smtp
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`
MAXSMTPD=`cat /var/qmail/control/concurrencyincoming`
LOCAL=`head -1 /var/qmail/control/me`
if [ -z "$QMAILDUID" -o -z "$NOFILESGID" -o -z "$MAXSMTPD" -o -z "$LOCAL" ]; then
echo QMAILDUID, NOFILESGID, MAXSMTPD, or LOCAL is unset in
echo /var/$QMAIL/supervise/qmail-smtpd/run
exit 1
fi
if [ ! -f /var/$QMAIL/control/rcpthosts ]; then
echo "No /var/$QMAIL/control/rcpthosts!"
echo "Refusing to start SMTP listener because it'll create an open relay"
exit 1
fi
exec /usr/local/bin/softlimit -m 8000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/$TCPRULES.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 25 /var/$QMAIL/bin/qmail-smtpd 2>&1
NOTE: This script is typically all that is changed between qmail instances, as
we may need to setup RBLs or run a different service.
NOTE: concurrencyincoming is not a standard control file
/var/qmail/supervise/qmail-smtpd/log/run
#!/bin/sh
QMAIL=qmail
exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t s16777215 n50 /var/log/$QMAIL/smtpd
Final tasks
Create the concurrencyincoming control file to limit the number of concurrent
smtp sessions:
echo 20 > /var/qmail/control/concurrencyincoming
chmod 644 /var/qmail/control/concurrencyincoming
Make the supervise run scripts executable:
chmod 755 /var/qmail/supervise/qmail-send/run
chmod 755 /var/qmail/supervise/qmail-send/log/run
chmod 755 /var/qmail/supervise/qmail-smtpd/run
chmod 755 /var/qmail/supervise/qmail-smtpd/log/run
Create the log directories:
mkdir -p /var/log/qmail/smtpd
chown qmaill /var/log/qmail /var/log/qmail/smtpd
Link the supervise directories into the deamontools /service directory:
ln -s /var/qmail/supervise/qmail-send /var/qmail/supervise/qmail-smtpd /service
qmailctl stop
NOTE: We issue a qmailctl stop because services begin as soon as the symlinks
are created.
Tcpserver config
The final bit of initial configuration is the tcpserver configuration.
Variables set here can impact more deeply than just the qmail-smtpd process
depending on the feature concerned. This initial configuration allows
localhost and our local network to relay (send emails to other domains).
/etc/tcp.smtp
127.:allow,RELAYCLIENT=""
10.137.:allow,RELAYCLIENT=""
Our qmail-smtpd run script expects to find a cdb created from this file:
qmailctl cdb
TODO http://toaster.godshell.com/index.php/Software/FakeMTA
I: Resources