From WBITT's Cooker!

Jump to: navigation, search



Created: 2009-07-08

Last Modified: 2013-04-05

Note: Please read the steps completely before implementing them. This is not a copy/paste howto. This is a careful comparison of LWQ, QMR and JMS methods to implement a fully functional mail server. The steps shown below were applied on 64 bit CENTOS 6.4 . The server is fully functional and is in production now.

Important sites:-

  • Life with Qmail [LWQ] : lifewithqmail.org
  • John Simpson's Qmail website [JMS] : http://qmail.jms1.net/
  • Qmail Rocks website [QMR]: qmailrocks.org

The intent of this installation is to install qmail the way DJB intended, but bring along those, who are used to installing QMR only. That is the method followed in LifeWithQmail. QMR has made a mix of many of these and other qmail installations and created QMR. John Simpson and others have some serious objections on the way QMR is setup. My objective in this document is to provide an easy to follow approach (as easy as QMR), yet confirming to the suggestions and standards of JMS and LWQ and others.

Basic Information

  • OS: CENTOS 6.4 64 bit on a KVM virtual machine (05 Apr 2013)
  • OS: CENTOS 5.3 32 bit on a KVM virtual machine
  • Hostname: qmail.example.com
  • IP:

Step: Preparations

  • Disable SELINUX
  • Disable Default Firewall

Install nmap and disable unnecessary services.

yum -y install nmap

[root@qmail ~]# for i in avahi-daemon autofs cups exim sendmail gpm ip6tables iptables lvm2-monitor mdmonitor netfs nfslock pcscd portmap restorecond rpcgssd rpcidmapd xfs yum-updatesd ; do chkconfig --level 35 $i off; service $i stop ; done

Shutting down Avahi daemon:                                [  OK  ]
Stopping automount:                                        [  OK  ]
Stopping cups:                                             [  OK  ]
Shutting down exim:                                        [  OK  ]
error reading information on service sendmail: No such file or directory
sendmail: unrecognized service
Shutting down console mouse services:                      [  OK  ]
Not stopping monitoring, this is a dangerous operation. Please use force-stop to override.
Stopping NFS statd:                                        [  OK  ]
Stopping PC/SC smart card daemon (pcscd):                  [  OK  ]
Stopping portmap:                                          [  OK  ]
Stopping RPC idmapd:                                       [  OK  ]
Shutting down xfs:                                         [  OK  ]
Stopping yum-updatesd:                                     [  OK  ]
[root@qmail ~]#

Check the open ports:

[root@qmail ~]# nmap localhost
Not shown: 1679 closed ports
22/tcp open  ssh

Nmap finished: 1 IP address (1 host up) scanned in 0.091 seconds
[root@qmail ~]#

Step: Local CENTOS repository (Optional)

Setup the local CentOS repository on the server and update the Yum repository configuration on qmail server. This step is not necessary on servers, which have direct high speed connection to the internet.

[root@qmail ~]# vi /etc/yum.repos.d/CentOS-Local.repo

# CentOS-Local.repo

name=CentOS-$releasever - Local

Step: Kernel update / OS update

It is also right time to update your kernel. Or, better yet, update the entire OS installation.


[root@qmail ~]# uname -r

[root@qmail ~]# yum list | grep kernel
kernel.i686                              2.6.18-128.el5                installed
kernel-devel.i686                        2.6.18-128.el5                installed
kernel-headers.i386                      2.6.18-128.el5                installed
kernel.i686                              2.6.18-128.4.1.el5            updates
kernel-PAE.i686                          2.6.18-128.4.1.el5            updates
kernel-PAE-devel.i686                    2.6.18-128.4.1.el5            updates
kernel-debug.i686                        2.6.18-128.4.1.el5            updates
kernel-debug-devel.i686                  2.6.18-128.4.1.el5            updates
kernel-devel.i686                        2.6.18-128.4.1.el5            updates
kernel-doc.noarch                        2.6.18-128.4.1.el5            updates
kernel-headers.i386                      2.6.18-128.4.1.el5            updates
kernel-xen.i686                          2.6.18-128.4.1.el5            updates
kernel-xen-devel.i686                    2.6.18-128.4.1.el5            updates
yum-kernel-module.noarch                 1.1.16-13.el5.centos          base
[root@qmail ~]#

[root@qmail ~]# yum -y update kernel kernel-devel kernel-headers

or just 

[root@qmail ~]# yum -y update

[root@qmail ~]# cat /etc/grub.conf
title CentOS (2.6.18-128.4.1.el5)
        root (hd0,0)
        kernel /boot/vmlinuz-2.6.18-128.4.1.el5 ro root=LABEL=/
        initrd /boot/initrd-2.6.18-128.4.1.el5.img
title CentOS (2.6.18-128.el5)
        root (hd0,0)
        kernel /boot/vmlinuz-2.6.18-128.el5 ro root=LABEL=/
        initrd /boot/initrd-2.6.18-128.el5.img
[root@qmail ~]#

System reboot needed after kernel update.

Step [optional]: SSH key based authentication


Step: Required software components, including basic PERL modules

  1. httpd, httpd-devel, apr
  2. php, php-imap, php-mysql, php-gd, php-pear, php-zlib, php-mbstring, php-xml,
  3. perl, perl-libwww-perl, perl-Digest-SHA1, perl-Digest-HMAC, perl-Net-DNS, perl-HTML-Tagset, perl-HTML-Parser

perl-Time-HiRes, perl-TimeDate, perl-suidperl, perl-DateManip

  1. gcc, gcc-c++, libtool-ltdl, libtool-ltdl-devel
  2. mysql-server, mysql-devel, postgresql-devel
  3. openssl, openssl-devel, openldap-servers
  4. wget
  5. patch, patchutils
  6. pcre-devel
  7. gdbm-devel
  8. db4, db4-devel
  9. fam fam-devel gamin-devel
  10. net-snmp + net-snmp-utils + net-snmp-libs
  11. mrtg
  12. spamassassin, expect, zlib-devel
yum -y install net-snmp net-snmp-utils net-snmp-libs mrtg \
httpd httpd-devel php php-imap php-mysql php-gd php-pear php-zlib php-mbstring php-xml \
gcc gcc-c++ gdbm-devel pcre-devel libtool-ltdl libtool-ltdl-devel \
mysql-server mysql-devel db4 db4-devel postgresql-devel \
openssl openssl-devel openldap-servers \
perl perl-libwww-perl perl-Digest-SHA1 perl-Digest-HMAC perl-Net-DNS perl-HTML-Tagset perl-HTML-Parser perl-Time-HiRes perl-TimeDate perl-suidperl perl-DateManip \
spamassassin expect zlib-devel zlib unzip \
fam fam-devel gamin-devel patch patchutils

Note: Basic PERL modules required are as follows:


From the yum install command above, you can see that all of these listed PERL modules are installed. However, Time::HiRes will not be installed.

Update: CENTOS 6.3 64 bit will install the Time::HiRes perl module.

Check with:

# yum list installed perl* | egrep -i "sha1|hmac|DNS|HiRes|Target|Parser"

If any perl module is not installed, try installing it manually. You can use the following command to install it through perl -MCPAN option:

perl -MCPAN -e "install Time::HiRes" 
All tests successful.
Files=1, Tests=40, 25 wallclock secs (11.49 cusr +  1.32 csys = 12.81 CPU)
  /usr/bin/make test -- OK
Running make install
Installing /usr/lib/perl5/5.8.8/i386-linux-thread-multi/auto/Time/HiRes/HiRes.so
Installing /usr/lib/perl5/5.8.8/i386-linux-thread-multi/auto/Time/HiRes/HiRes.bs
Files found in blib/arch: installing files in blib/lib into architecture dependent library tree
Installing /usr/lib/perl5/5.8.8/i386-linux-thread-multi/Time/HiRes.pm
Installing /usr/share/man/man3/Time::HiRes.3pm
Writing /usr/lib/perl5/5.8.8/i386-linux-thread-multi/auto/Time/HiRes/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
  /usr/bin/make install  -- OK

How to check if these modules are installed?

Use the perldoc -l command for each PERL module you want to check. You should see a “.pm” line for each module that is installed on the system:-

[root@qmail ~]# perldoc -l Time::HiRes Digest::SHA1 Digest::HMAC Net::DNS Time::HiRes HTML::Tagset HTML::Parser

[root@qmail ~]#

There is a long list of PERL modules, required for the enhanced functionality of SpamAssassin, Razor, DCC, etc. You can install them after you are done with your base qmail installation . Or you may want to install them before (now). It doesn't matter. There is also an option to update your perl CPAN system module. If you want to, you can execute the following two commands to update. This is an optional step though.

Update: CENTOS 6.3 You may need to have perl-CPAN installed through RPM.

perl -MCPAN -e "install Bundle::CPAN"
perl -MCPAN -e "reload"

And here is the list of PERL modules, as discussed above:

Razor2::Client::Agent  (This will get installed with the Razor software, later)

Here is a tip for checking this whole list: Save this list in a text file, say /tmp/perl-list.txt on the server. Then run a loop to check existence of each module, such as:

[root@qmail ~]# for i in `cat /tmp/perl-list.txt`; do perldoc -l $i ; done

You will get the output such as:-

[root@qmail ~]# for i in `cat /tmp/perl-list.txt`; do perldoc -l $i ; done
No documentation found for "Parse::Syslog".                               
No documentation found for "Statistics::Distributions".
... . 

I will use a grep filter to just see which modules are not installed:-

[root@qmail ~]# for i in `cat /tmp/perl-list.txt`; do perldoc -l $i ; done | grep "No documentation"
No documentation found for "Parse::Syslog".
No documentation found for "Statistics::Distributions".
No documentation found for "ClamAV::Client".
No documentation found for "Mail::SPF::Query".
No documentation found for "IP::Country::Fast".
No documentation found for "Mail::SPF".
No documentation found for "Mail::DKIM".
No documentation found for "Mail::DomainKeys".
No documentation found for "Crypt::OpenSSL::Bignum".
No documentation found for "Net::Ident".

[root@qmail ~]#

As you can see above, I now have a list of modules I will need to install manually. I will utilize the opportunity now and install them. Again, you can install them before you start with Anti-Spam software.

The following will install without a problem:-

perl -MCPAN -e "install Parse::Syslog"
perl -MCPAN -e "install Statistics::Distributions"
perl -MCPAN -e "install IP::Country::Fast"
perl -MCPAN -e "install Crypt::OpenSSL::Bignum"

The following will not install easily with “perl -MCPAN ....” command. But when you do, it will save you from actually downloading the software. When you issue the “perl -MCPAN -e install ........” command, the module will get downloaded and will be saved in the .cpan/build directory of current user's home directory. Since you will be performing these steps as root, you will find them downloaded under /root/.cpan/build directory.

perl -MCPAN -e "install ClamAV::Client"
perl -MCPAN -e "install Mail::SPF::Query"
perl -MCPAN -e "install Mail::SPF"
perl -MCPAN -e "install Mail::DKIM"
perl -MCPAN -e "install Mail::DomainKeys"
perl -MCPAN -e "install Net::Ident"

At this point, you just need to change into each directory of the module and install it using the actual manual compilation technique.

[root@qmail build]# cd /root/.cpan/build/Net-Ident-1.20/

[root@qmail Net-Ident-1.20]# perl Makefile.PL && make && make install
Installing /usr/lib/perl5/site_perl/5.8.8/Net/Ident.pm
Installing /usr/share/man/man3/Net::Ident.3pm
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Net/Ident/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
[root@qmail Net-Ident-1.20]#

[root@qmail ~]# cd /root/.cpan/build/Mail-DKIM-0.36/

[root@qmail Mail-DKIM-0.36]# perl Makefile.PL && make && make install
Installing /usr/share/man/man3/Mail::DKIM::SignerPolicy.3pm
Installing /usr/share/man/man3/Mail::DKIM::DkPolicy.3pm
Installing /usr/share/man/man3/Mail::DKIM::Canonicalization::DkimCommon.3pm
Installing /usr/share/man/man3/Mail::DKIM::Policy.3pm
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Mail/DKIM/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod

[root@qmail build]# cd /root/.cpan/build/Mail-SPF-v2.006/
[root@qmail Mail-SPF-v2.006]# perl Makefile.PL && make && make install 
This module requires Module::Build to install itself.                  
  Install Module::Build now from CPAN? [y] y 
Installing /usr/share/man/man3/Mail::SPF::Mod::Exp.3pm
Installing /usr/share/man/man3/Mail::SPF::Result.3pm
Installing /usr/share/man/man3/Mail::SPF::Term.3pm
Installing /usr/share/man/man3/Mail::SPF::v1::Record.3pm
Installing /usr/share/man/man3/Mail::SPF::Server.3pm
Installing /usr/sbin/spfd
Installing /usr/bin/spfquery
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Mail/SPF/.packlist

[root@qmail Mail-SPF-v2.006]# cd /root/.cpan/build/Mail-SPF-Query-1.999.1/

Mail-SPF-Query module expects two more modules. Let's install them too:

[root@qmail ~]# perl -MCPAN -e "install Net::CIDR::Lite"

[root@qmail ~]# perl -MCPAN -e "install Sys::Hostname::Long"

[root@qmail Mail-SPF-Query-1.999.1]# perl Makefile.PL && make && make install
Installing /usr/bin/spfd
Installing /usr/bin/spfquery
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Mail/SPF/Query/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
[root@qmail Mail-SPF-Query-1.999.1]#

[root@qmail Mail-DomainKeys-1.0]# perl Makefile.PL && make && make install

You appear to be directly connected to the Internet.  I have some tests
that try to query live nameservers.

Do you want to enable these tests? [y] y
Warning: prerequisite Crypt::OpenSSL::RSA 0 not found.
Writing Makefile for Mail::DomainKeys
Installing /usr/lib/perl5/site_perl/5.8.8/Mail/DomainKeys/Key/Private.pm
Installing /usr/share/man/man3/Mail::DomainKeys.3pm
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Mail/DomainKeys/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
[root@qmail Mail-DomainKeys-1.0]#

Install the additional module:

[root@qmail Mail-DomainKeys-1.0]# cd ../Crypt-OpenSSL-RSA-0.25/

[root@qmail Crypt-OpenSSL-RSA-0.25]# perl Makefile.PL && make && make install 
Writing Makefile for Crypt::OpenSSL::RSA                                      
Writing /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Crypt/OpenSSL/RSA/.packlist
Appending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
[root@qmail Crypt-OpenSSL-RSA-0.25]#


From here onwards Qmail installation steps will start. So if you want to take a snapshot of your virtual machine, now is the time.

Downloading and installing Qmail

We will not use NetQmail-1.06, because it is basically qmail-1.03 + “Some patches already applied”. This is what we don't want. We will patch qmail-1.03 with John Simpson's patches.

QMR guide asks us to download the packages in /usr/src/qmail. Whereas LWQ asks us to download it in /usr/local/src . Both guides ultimately place daemontools in /package and the first two (qmail and ucspi-tcp) in /var/qmail/*. I will try to be as close to LWQ recommendations. I will also make a directory named /downloads , and will download all the software I will be downloading during this installation, in this location.

Download Software

mkdir /downloads
cd /downloads

[root@qmail downloads]# wget http://cr.yp.to/software/qmail-1.03.tar.gz
[root@qmail downloads]# wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
[root@qmail downloads]# wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz

cp  /downloads/*.tar.gz   /usr/local/src/

Unpack qmail-1.03.tar.gz and ucspi-tcp-0.88.tar.gz in /usr/local/src/ .

cd /usr/local/src/
tar xzf qmail-1.03.tar.gz 
tar xzf ucspi-tcp-0.88.tar.gz

Copy and unpack the daemontools package to the /package directory.

mkdir /package
chmod 1755  /package

Question: Why do we need to do "chmod 1755 /package" ????

cp /downloads/daemontools-0.76.tar.gz    /package/
cd /package
tar xzf  daemontools-0.76.tar.gz

There should now be directories called /usr/local/src/qmail-1.03, /usr/local/src/ucspi-tcp-0.88, and /package/admin/daemontools-0.76.

Create users and groups

Since qmail's installation program creates the sub-directories as they're needed, you only need to create the qmail "home" directory:

mkdir /var/qmail

Create users and groups:

groupadd nofiles
useradd  -g nofiles -d /var/qmail/alias -s /sbin/nologin -p '*'  -c 'QMail alias user'  alias
useradd  -g nofiles -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail daemon user'       qmaild
useradd  -g nofiles -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail log user'          qmaill
useradd  -g nofiles -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail password user'     qmailp

groupadd qmail
useradd  -g qmail -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail queue user'  qmailq
useradd  -g qmail -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail remote user' qmailr
useradd  -g qmail -d /var/qmail -s /sbin/nologin -p '*'  -c 'QMail send user'   qmails

Basically, at this time, we are ready to do the build. Remember we have Qmail-1.03, which we need to patch, ideally with the latest combined patch from John Simpson.

conf-split and conf-spawn

QMR, at this stage creates a lot of directories and sets ownerships, which we are not going to do now. We will be careful, and will try to follow LWQ steps. There are, in particular, two files in Qmail source tree, in which QMR pushes some values. Refer to the following code:

# Don't execute the following two lines blindly.
# echo 211 > /usr/src/qmail/qmail-1.03/conf-split
# echo 255 > /usr/src/qmail/qmail-1.03/conf-spawn

According to the example we are following, these files are actually located in /usr/local/src/qmail-1.03 directory. Let's see these files contents from the default source:-

# cat /usr/local/src/qmail-1.03/conf-split

This is the queue subdirectory split.
# cat /usr/local/src/qmail-1.03/conf-spawn
This is a silent concurrency limit. You can't set it above 255. On some  systems you can't set it above 125. qmail will refuse to compile if the limit is too high. 

The details gathered on these two files from internet :-


The conf-split file specifies the number of subdirectories that the qmail directories for mail-queuing: info, local, mess and remote, are divided into. The default value for this parameter is 23. This is the number of split subdirectories for those queue directories which are hashed. The default for qmail is 23. Appropriate values depend on the volume of mail handled, OS filesystem efficiency, and other factors, but this should always be a prime number. (Ref: http://pyropus.ca/software/queue-repair/docs.html ) Research has shown that you get a more even distribution of hash values, and thus fewer collisions, if you choose your table size to be a prime number. Question: Are there any negative sideeffects of setting conf-split too high? Answer: Yes there are . Scanning todo/ takes longer, if you are using the big-todo patch, because every subdir has to be scanned, instead of just one dir.

Reference: http://www.mail-archive.com/qmail@id.wustl.edu/msg46322.html The default, 23, is prime, and in his only message to the list on the topic of conf-split, DJB suggested a value of 401, also prime, for a queue with 100000 entries. Reference: http://www.ornl.gov/its/archives/mailing-lists/qmail/1997/07/msg00295.html

Bottom-line: For smaller sites / mail-servers/ mail-queues, use the default. For larger sites, you can use larger prime numbers, going all the way to 401.


The conf-spawn file specifies the maximum number of simultaneous qmail-local and qmail-remote sessions that can be running on the system. The default value for this parameter is 120.

Conclusion for conf-split and conf-spawn:

We will use the default values in these files, unless John Simpson's combined patch makes changes in them.

Patch qmail source with John Simpson's patch

Time to apply the combined patch to qmail-1.03 . Download John Simpson's latest combined patch from his site: http://qmail.jms1.net/patches/combined-details.shtml

cd /downloads
wget http://qmail.jms1.net/patches/qmail-1.03-jms1.7.10.patch

cd /usr/local/src/qmail-1.03
patch < /downloads/qmail-1.03-jms1.7.10.patch

As you can see below, not a “single” hunk / patch failed!

# patch < /downloads/qmail-1.03-jms1.7.10.patch                                   
patching file EXTTODO                                                                                    
patching file EXTTODO-INFO                                                                               
patching file tcp-env.c
patching file timeoutconn.c
patching file timeoutconn.h

Just for the sake of verification, lets see what the patch has done to the two files: conf-split and conf-spawn. Here they are after patching:

# cat conf-split

This is the queue subdirectory split.

# cat conf-spawn

This is a silent concurrency limit. You can't set it above 65000. Many systems have a "hidden limit" of 509, because a single process cannot have more than 1023 handles open at once, and each concurrent delivery uses two handles. If you set it any higher than your system's "hidden limit", qmail will refuse to compile. 

As you can see, there is just a more detailed comment added in the conf-spawn. Rest no values are changed. Good!

Compile qmail

Once the patching is done, you are in line with the step of LWQ. You can go ahead and compile qmail. As shown below:

cd /usr/local/src/qmail-1.03
make clean
make man
make setup check

# make setup check
chmod 755 binm3+df
./compile spfquery.c
spfquery.c: In function ‘main’:
spfquery.c:30: warning: incompatible implicit declaration of built-in function ‘strdup’
spfquery.c:24: warning: return type of ‘main’ is not ‘int’
./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \
        now.o dns.o env.o datetime.a stralloc.a alloc.a str.a substdio.a \
        case.a error.a fs.a `cat dns.lib` `cat socket.lib` envread.o qregex.o
./compile -DEXTERNAL_TODO  qmail-todo.c
qmail-todo.c: In function ‘main’:
qmail-todo.c:620: warning: return type of ‘main’ is not ‘int’
./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \
        readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \
        alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o

Configure basic qmail structures

According to LWQ, you can now go ahead with using either “./config” or “./config-fast server.domain.com” method. QMR asks you to run “./config-fast server.domain.com” directly. However there is no harm in executing any of these two scripts. For "./config" you should make sure that your hostname is retrievable from DNS.

According to LWQ:

If your DNS is configured properly, this script should be all you need at this point:
If, for some reason, config can't find your hostname in DNS, you'll have to run the config-fast script:
    ./config-fast the.full.hostname
For example, if your domain is example.com and the hostname of your computer is dolphin, your config-fast line would look like this:
    ./config-fast dolphin.example.com

Run the configuration script:

# ./config-fast qmail.example.com
Your fully qualified host name is qmail.example.com.
Putting qmail.example.com into control/me...
Putting example.com into control/defaultdomain...
Putting example.com into control/plusdomain...
Putting qmail.example.com into control/locals...
Putting qmail.example.com into control/rcpthosts...
Now qmail will refuse to accept SMTP messages except to qmail.example.com.
Make sure to change rcpthosts if you add hosts to locals or virtualdomains!

That's it. qmail is now installed on your system and is ready to be run!

Please note that you must not put your domain name in the locals file. Means, if you are configuring your server to host emails for the domain example.com using vpopmail, then the locals file must not contain the domain "example.com". It should only contain the hostname of your server, such as "qmail.example.com" .

For reference check the following explanations:

QMR suggests to “make cert” at this point. There is no fun in doing so, as there are couple of things we still need to do. More-over, the “make cert” method suggested by QMR is wrong and is seriously questioned by JMS. We will deal with certificates at a later stage, when we will add SMTPS service.


ucspi-tcp is DJB's original implementation of UCSPI, the Unix Client-Server Program Interface, using the TCP protocol. In other words, it's a set of programs which make it easy to write and run service programs which accept connections on TCP sockets, as well as client programs which connect to services on TCP sockets. The programs in the ucspi-tcp package take care of the messy networking details, and allow the programmer or system administrator to concentrate on making their service or client work correctly.

ucspi-tcp [LWQ]:

If you modified conf-cc and conf-ld, you'll need to make the same changes in this directory. Note that we did not do any changes to conf-cc and conf-ld while compiling / building qmail-1.03 .

Now change to the ucspi-tcp directory, patch the source code and compile it:

cd /usr/local/src/ucspi-tcp-0.88
patch < /usr/local/src/netqmail-1.06/other-patches/ucspi-tcp-0.88.errno.patch
make setup check

That's it. ucspi-tcp is installed.

Ucspi-tcp [JMS]:

From JMS site (http://qmail.jms1.net/ucspi-tcp/ ):-

ucspi-tcp is DJB's original implementation of UCSPI, the Unix Client-Server Program Interface, using the TCP protocol. In simple words, it's a set of programs which make it easy to write and run service programs which accept connections on TCP sockets, as well as client programs which connect to services on TCP sockets. The programs in the ucspi-tcp package take care of the messy networking details, and allow the programmer or system administrator to concentrate on making their service or client work correctly.

It's mentioned on this web site (http://qmail.jms1.net) because the normal method of running an SMTP or POP3 service is to use the "tcpserver" program, which is part of the ucspi-tcp package. It's also possible to use tcpserver to run other TCP-based services, such as IMAP services. If you're running a qmail server, you should definitely understand how tcpserver works.

Like qmail, the programs in the ucspi-tcp package are lacking in some features which many people, myself included, need on their servers. Below is a list of the ucspi-tcp patches I use on my own systems:

  • ucspi-tcp-0.88.errno.patch
  • ucspi-rss.diff
  • The tcpserver limits patch
ucspi-tcp-0.88.errno patch

The ucspi-tcp package, like all of DJB's other packages, has an issue with the errno variable when compiled using glibc version 2.3 or higher. (e.g. CENTOS 5.3 has glibc 2.5 ). The solution is the same as for DJB's other packages as well.

Edit the file error.h in the source code. Find this line, near the top of the file:

extern int errno; 

Comment this line out, and add the following line below it:

/* extern int errno; */
#include <errno.h> 

If you would rather not edit the file by hand, the patch file can be used to make the change as well.

ucspi-rss.diff patch

ucspi-rss.diff is a patch by Alan Curry which makes the rblsmtpd program work with A records. This is necessary because the owners of "rss" (one of the first anti-spam "blacklists") removed the TXT records from their zone files, because the DNS server they were using to serve the zone (BIND, which they also wrote) had trouble with large zone files, a problem which the "rbldns" program (from DJB's djbdns package) does not share. I have updated the patch. With the original patch, when rblsmtpd retrieves a TXT record, it scans the value for the string "%IP%" and replaces it with the value of TCPREMOTEIP (i.e. the IP address of the client.) My updated version does the same substitution on the value of the RBLSMTPD environment variable when it starts. You can download the original patch from qmail.org, or from my server. You can download the updated patch here: http://qmail.jms1.net/ucspi-tcp/ucspi-rss2.patch

tcpserver limits patch

The tcpserver limits patch, by Matija Nalis, gives tcpserver the ability to reject connections when the server's load average is above a certain number, when more than a certain number of connections are received from the same IP address, or when more than a certain number of connections are received from machines in the same class-C block (i.e. the "first three numbers" in their IP addresses are the same. "" and "" are in the same class-C block.)

The limits are configured by setting the MAXLOAD, MAXCONNIP, and MAXCONNC environment variables before tcpserver runs. If you want tcpserver to send a message to the client before dropping their connection, you can configure this by setting a DIEMSG environment variable. The patch I was using, dated 2006-01-26, does work as advertised, and has saved my own server and several of my clients' servers from being overloaded by over-zealous attackers (i.e. spammers) over the past few months. However, I did notice a few minor cosmetic issues which I thought needed to be fixed, so I updated the patch.

My updates are: When MAXCONNIP or MAXCONNC cause a connection to be rejected, tcpserver adds "MAXCONNIP:" or "MAXCONNC:" with the limit at the end of the "deny line in the logs. This makes it easy to debug- the error message tells you which environment variable caused the connection to be rejected.

However, when MAXLOAD causes a connection to be rejected, it adds "LOAD:" and the current load average to the log. This doesn't match the environment variable, which "feels funny" to me. (I told you it was a minor cosmetic issue.) . For the sake of consistency, I changed the "LOAD:" label to say "MAXLOAD:", so it matches the environment variable name, like the other two messages do.

The older patch only has a provision for a single DIEMSG variable, whose value is sent to a client whose connection is being rejected, regardless of which of the three limits the client triggered. I thought it would be nice to be able to set one message which says something like "Server too busy, try again later" and another message which says "Too many connections from your IP address".

I added three new environment variables: DIEMSG_MAXLOAD, DIEMSG_MAXCONNIP, and DIEMSG_MAXCONNC, whose values are used instead of the generic DIEMSG message. However, to avoid breaking older scripts, if one of these new variables is not set, the DIEMSG value will be used instead. The original author's web page has a link where you can download the previous versions of the patch. As of the time I'm writing this (2007-12-22) the most recent version of his patch is dated 2006-01-26, and is what I used as the starting point for my own changes. Below is the link to download the patch. I also have the CHANGES.tcpserver-limits-patch and README.tcpserver-limits-patch files available, if you want to read them before downloading the patch.

I highly recommend you to read the README.tcpserver-limits-patch file. http://qmail.jms1.net/ucspi-tcp/tcpserver-limits-2007-12-22.patch

Conclusion and actual installation of UCSPI:

We did not do any changes to conf-cc and conf-ld while compiling / building qmail-1.03 . So we don't need to change anything in the ucspi-tcp directory. I cannot use the patch suggested by LWQ, as I am not using netqmail-1.06 . So I will use JMS method and manually edit the error.h file:

cd /usr/local/src/ucspi-tcp-0.88

# vi error.h 

Replace :
extern int errno;

With :
/* extern int errno; */

And add another line after it:
#include <errno.h>

(save exit).

Then, I would download the two other patches mentioned by JMS and apply them to UCSPI source:

cd /downloads/
wget http://qmail.jms1.net/ucspi-tcp/ucspi-rss2.patch
wget http://qmail.jms1.net/ucspi-tcp/tcpserver-limits-2007-12-22.patch

cd /usr/local/src/ucspi-tcp-0.88

# patch < /downloads/ucspi-rss2.patch
patching file rblsmtpd.c

# patch < /downloads/tcpserver-limits-2007-12-22.patch
patching file CHANGES.tcpserver-limits-patch
patching file README.tcpserver-limits-patch
patching file tcpserver.c

Now run “make” and “make setup check”:

# make
./compile install.c                                                                        
./compile hier.c                                                                           
./compile auto-str.c                                                                       
auto-str.c:9: warning: conflicting types for built-in function ‘puts’                      
./load auto-str unix.a byte.a                                                              
./auto-str auto_home `head -1 conf-home` > auto_home.c                                     
./compile auto_home.c                                                                      
./load install hier.o auto_home.o unix.a byte.a                                            
./compile instcheck.c                                                                      
./load instcheck hier.o auto_home.o unix.a byte.a                                          

# make setup check

That's it. ucspi-tcp is installed.


The daemontools package monitors, controls, and logs the execution and output of long-running programs, often called daemons.

Daemontools [LWQ]

Change to the daemontools build directory:

cd /package/admin/daemontools-0.76

Once again, if you modified <conf-cc> and conf-ld during the qmail and ucspi-tcp builds, you'll need to make the same changes in the src directory.

Then do:

cd src
patch < /usr/local/src/netqmail-1.06/other-patches/daemontools-0.76.errno.patch
cd ..

Daemontools [QMR]:

QMR wants us to patch daemontools, using the patch copied from the netqmail distribution, which is ok to do.

Conclusion and Installation of Daemontools:

We know that are not using LWQ's netqmail. Also we know that the patch applied to daemontools during QMR installation is in-fact a patch copied from LWQ's distribution. Basically this “errno” patch is exactly the same as it was for ucspi-tcp sotware. We just need to disable a line and introduce another line, exactly as before. So here it goes:

cd /package/admin/daemontools-0.76/src/

# vi error.h
extern int errno;

Comment out "extern int errno;" , and add another line "#include <errno.h>" , after that.

/* extern int errno; */
#include <errno.h>

(save exit).

Change directory one step back (out of src directory):

cd .. 
cd /package/admin/daemontools-0.76/

And run "package/install":

# package/install
grep sysdep hasmkffo.h >> sysdeps
grep sysdep hasflock.h >> sysdeps
grep sysdep hasshsgr.h >> sysdeps
Copying commands into ./command...
Creating symlink daemontools -> daemontools-0.76...
Making command links in /command...
Making compatibility links in /usr/local/bin...
Creating /service...
Adding svscanboot to inittab...
init should start svscan now.

At this point your daemontools installation is complete. You should be able to see the “svscan” process running on your machine. Use "ps -ef | grep svscan" , “ps aux | grep svscan” or "ps waux | grep svscan" to verify that svscan is running. See the important note below, if you cannot see the svscan process running.

# ps waux | grep svscan
root     25206  0.0  0.1   2452  1044 ?        Ss   15:06   0:00 /bin/sh /command/svscanboot
root     25208  0.0  0.0   1684   356 ?        S    15:06   0:00 svscan /service
root     25213  0.0  0.0   3912   676 pts/0    S+   15:09   0:00 grep svscan

You will also see the following line added to your /etc/inittab file :

# cat /etc/inittab 
. . . 
. . . 

IMPORTANT NOTE: (CentOS 6 specific)

The installer added a line to /etc/inittab to start svscan automatically, however that is obsolete in CentOS 6.x.

Remove the "SV" or "svscanboot" line from /etc/inittab:


Create a new file /etc/init/svscan.conf, with the startup code in it:

cd /etc/init
echo "start on runlevel [12345]" > svscan.conf
echo "respawn" >> svscan.conf
echo "exec /command/svscanboot" >> svscan.conf

Tell init to re-read its configuration files and start svscanboot:

initctl reload-configuration
initctl start svscan
# initctl reload-configuration

# initctl start svscan
svscan start/running, process 5167

# ps aux | grep svscan
root      5167  0.0  0.1  11296  1316 ?        Ss   11:44   0:00 /bin/sh /command/svscanboot
root      5169  0.0  0.0   4100   444 ?        S    11:44   0:00 svscan /service
root      5172  0.0  0.0 103236   836 pts/0    S+   11:44   0:00 grep svscan

This step is done.

Note: This process check (shown above, just now) is what QMR performs at the end of it's “Part 2 - Installing Qmail Itself”. But as soon as QMR does this, it goes on an “Install spree” (same as shopping spree) and installs all sorts of software until it reaches it's “Part-9” where is decides to have mercy on the person doing the install, and finalize the qmail installation. Why did'nt that guy just start the qmail software first and made sure that it worked before moving on to installing bells and whistles ? It is a mystery to me.

Install CDB package from DJB's site

DJB's cdb library and tools contains the cdbmake-12 program, which converts a text file into a cdb file. The cdbmake program will help creating CDB files later on. This package should be installed using the directions on djb's web site.

cd /downloads
wget http://cr.yp.to/cdb/cdb-0.75.tar.gz
tar xzf cdb-0.75.tar.gz

Fix the error in error.h file. Find:

 extern int errno;

, and change it to:

/* extern int errno; */
#include <errno.h>

Now compile and install:

make setup check

Also install perl-CDB_File either from yum repository or from CPAN:

yum -y install perl-CDB_File

Configure qmail

Qmail log directories

LWQ creates a directory (/var/log/qmail) at this point. This will be used in various log-run scripts created below. The qmail log-run scripts (from LWQ) will create files in this directory, “directly”. QMR deviated a little from this and created three sub directories inside /var/log/qmail: qmail-send, qmail-smtpd, and qmail-pop3d . JMS acknowledges (http://qmail.jms1.net/logfiles.shtml), that is is not standard, however it is not wrong as well. So we will use the QMR log directories.

mkdir /var/log/qmail # LWQ

mkdir /var/log/qmail/qmail-send # QMR
mkdir /var/log/qmail/qmail-smtpd # QMR
mkdir /var/log/qmail/qmail-pop3d # QMR

QMR, also sets up certain ownership and permission to these log directories. Such as:

chown -R qmaill:root /var/log/qmail
chmod -R 750 /var/log/qmail

If you check the LWQ run script for qmail-send/log, and qmail-smtpd/log, you will notice that setuidgid program (provided with qmail source code itself), is called with an account name of qmaill. The setuidgid program takes care of the correct ownership of the files being written by the child program it calls. So setting the ownership for /var/log/qmail and it's sub directories , as the user qmaill, is okay. The same is verified/confirmed when LWQ also sets up the log directories and sets up ownership of these files as user qmaill . Also see http://cr.yp.to/qmail/faq/admin.html#multilog

The /var/qmail/rc file [LWQ] (recommended)

The /var/qmail/boot directory contains example qmail boot scripts for different configurations. However for our installation, we'll use the following script and save it as /var/qmail/rc :

# Using stdout for logging
# Using control/defaultdelivery from qmail-local to deliver messages by default
exec env - PATH="/var/qmail/bin:$PATH" \
qmail-start "`cat /var/qmail/control/defaultdelivery`"

Note 1: Notice the back ticks! Note 2: QMR does the same, but doesn't inform us anything about creating this script, or it's contents. Also, QMR places ./Maildir (without quotes) in the /var/qmail/control/defaultdelivery file. That is intended, but an extra slash (/) is what is mentioned in LWQ guide. Normally it doesn't matter; It works both ways.

Save the above code as above /var/qmail/rc, and make it executable:

chmod 755 /var/qmail/rc

At this point you need to decide the default delivery mode for messages that aren't delivered by a .qmail file. The options are Mailbox or Maildir formats. We are going to use the Maildir format as the default delivery mode, which is more efficient and manageable. For more information about mbox and maildir, see INSTALL.mbox, INSTALL.maildir files in /usr/local/src/qmail-1.03 directory.

echo './Maildir/' >/var/qmail/control/defaultdelivery

Note [from LWQ]: defaultdelivery isn't a standard qmail control file. It's a feature of the above /var/qmail/rc file. The defaultdelivery argument to qmail-start is the contents of a .qmail file that specifies delivery instructions to be followed when no actual .qmail is found. Putting these instructions in a separate control file eliminates the need to quote shell metacharacters in the delivery instructions and avoids messy multi-line command arguments.

System start-up files [from LWQ]

The qmailctl script (recommended)

If you were to manually execute the /var/qmail/rc script, qmail would be partially started. But we want qmail started up automatically every time the system is booted and we want it shut down cleanly when the system is halted. This is accomplished by creating a startup/shutdown script like the following in /var/qmail/bin/qmailctl. This script is available via http://lifewithqmail.org/qmailctl-script-dt70 . Create the script using your editor or by downloading it with your web browser (recommended):

cd /downloads
wget http://lifewithqmail.org/qmailctl-script-dt70
cp qmailctl-script-dt70 /var/qmail/bin/qmailctl 


# For Red Hat chkconfig
# chkconfig: - 80 30
# description: the qmail MTA control file

export PATH

QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`

case "$1" in
    echo "Starting qmail"
    if svok /service/qmail-send ; then
      svc -u /service/qmail-send /service/qmail-send/log
      echo "qmail-send supervise not running"
    if svok /service/qmail-smtpd ; then
      svc -u /service/qmail-smtpd /service/qmail-smtpd/log
      echo "qmail-smtpd supervise not running"
    if [ -d /var/lock/subsys ]; then
      touch /var/lock/subsys/qmail
    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
    svstat /service/qmail-send
    svstat /service/qmail-send/log
    svstat /service/qmail-smtpd
    svstat /service/qmail-smtpd/log
    echo "Flushing timeout table and sending ALRM signal to qmail-send."
    svc -a /service/qmail-send
    echo "Sending HUP signal to qmail-send."
    svc -h /service/qmail-send
    echo "Pausing qmail-send"
    svc -p /service/qmail-send
    echo "Pausing qmail-smtpd"
    svc -p /service/qmail-smtpd
    echo "Continuing qmail-send"
    svc -c /service/qmail-send
    echo "Continuing qmail-smtpd"
    svc -c /service/qmail-smtpd
    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
    tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
    chmod 644 /etc/tcp.smtp.cdb
    echo "Reloaded /etc/tcp.smtp."
    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
    echo "Usage: $0 {start|stop|restart|doqueue|flush|reload|stat|pause|cont|cdb|queue|help}"
    exit 1

exit 0

Make the qmailctl script executable and link it to a directory in your path:

chmod 755 /var/qmail/bin/qmailctl
ln -s /var/qmail/bin/qmailctl /usr/bin

Note: QMR also uses the same script which is copied from /downloads/qmailrocks/scripts/finalize location to /var/qmail/bin directory, as a result of running the the /downloads/qmailrocks/scripts/finalize/linux/finalize_linux.script script-file, in the start of QMR step 9. QMR also makes this executable , and link it in /usr/bin the same way.

The supervise scripts:

Note: QMR already has these scripts ready in a specific directory (/downloads/qmailrocks/scripts/finalize/freebsd/) . It just copies these files at the same locations where LWQ suggests. This was revealed from studying the script (finalize_linux.script), which QMR runs in part 9.

Supervise scripts need a location to be created/stored in. Create the supervise directories for the qmail services [LWQ]: (recommended)

mkdir -p /var/qmail/supervise/qmail-send/
mkdir -p /var/qmail/supervise/qmail-send/log

mkdir -p /var/qmail/supervise/qmail-smtpd
mkdir -p /var/qmail/supervise/qmail-smtpd/log


  • I did not create qmail-pop3 directories, because I will use POP3 and IMAP from dovecot.
  • QMR also creates these directories, when a script named qmr_install_linux-s1.script is run.
The qmail-send run script

The JMS qmail-send run script looks like this:

exec env - PATH="$VQ/bin:/usr/local/bin:/usr/bin:/bin" \
  qmail-start ./Maildir/

This is in-fact an almost the same as the /var/qmail/rc file. Apparently JMS doesn't want to use /var/qmail/rc in his scripts, and would rather (kind of) hard-code the same.

Create the /var/qmail/supervise/qmail-send/run file (recommended):

cat > /var/qmail/supervise/qmail-send/run << EOF
exec /var/qmail/rc

Note: QMR's send_run file has same contents, for this file.

The qmail-send/log/run script

The /var/qmail/supervise/qmail-send/log/run file by LWQ, which I will use with some variation (shown later):

exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t /var/log/qmail

Note: QMR also uses the same contents with a little variation. These variations are permitted / certified by DJB (http://cr.yp.to/daemontools/multilog.html) , and JMS ( http://qmail.jms1.net/scripts/ ). The QMR's send_log script, later copied in system as /var/qmail/supervise/qmail-send/log/run, looks like:

cat /downloads/qmailrocks/scripts/finalize/freebsd/send_log

export PATH
exec setuidgid qmaill multilog t s100000 n20 /var/log/qmail/qmail-send 2>&1

A little explanation: s1000000 means that the current logfile will be rotated when reached a size of 100KB. And 20 such rotated log files will be retained by qmail / multilog. Older log files will automatically get deleted. We will adopt this variation and use this. I just want to raise the size-limit of the logfile to 50MB and 20 generations of this file. This will result in a total of 1GB of disk consumed by my qmail logs. You can adjust as per your requirements. Note: I think there is no need to "export" the PATH.

JMS provides a script at (http://qmail.jms1.net/scripts/service-any-log-run) location. Which looks like:


exec env - PATH="$VQ/bin:/usr/local/bin:/usr/bin:/bin" \
  multilog t n1024 s1048576 ./main \
    '-*' '+*ver: status:*' =lstatus

JMS calls it “service-any-log-run” because you can use it to log any service you want to, such as qmail-send, qmail-smtpd, etc. He uses the technique of using relative file name instead of absolute file names. That means that if this script it run under qmail-send/log/ directory, it will create a file “main” over there. The cryptic line at the end of the script is basically removal of all log lines except the ones with “ver” and “status” in them. This indicated minimal logging being done. Default is that all lines will be logged. He is keeping 1024 generations of this log file (n1024) , each one being 1MB in size (s1048576).

For detailed explanation of multilog options, look here: http://cr.yp.to/daemontools/multilog.html

Alright, the final version of my /var/qmail/supervise/qmail-send/log/run file looks like: (recommended)

cat > /var/qmail/supervise/qmail-send/log/run << EOF
exec setuidgid qmaill multilog t s50000000 n20 /var/log/qmail/qmail-send 2>&1

Look here for more explanations:

The qmail-smtpd/run script

Here is the LWQ version of the qmail-smtpd/run file: (not recommended for our setup)

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 /var/qmail/supervise/qmail-smtpd/run
    exit 1

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

exec /usr/local/bin/softlimit -m 2000000 \
    /usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/tcp.smtp.cdb -c "$MAXSMTPD" \
        -u "$QMAILDUID" -g "$NOFILESGID" 0 smtp /var/qmail/bin/qmail-smtpd 2>&1

The QMR version of qmail-smtpd/run file is below. When the finalize_linux.script script is run in QMR part 9, this file gets copied as /var/qmail/supervise/qmail-smtpd/run file. (Not recommended for our setup).

[root@qmail freebsd]# cat /downloads/qmailrocks/scripts/finalize/freebsd/smtpd_run
QMAILDUID=`id -u vpopmail`
NOFILESGID=`id -g vpopmail`
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 /var/qmail/supervise/qmail-smtpd/run
exit 1
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
exec /usr/local/bin/softlimit -m 30000000 \
/usr/local/bin/tcpserver -v -R -l "$LOCAL" -x /etc/tcp.smtp.cdb -c "$MAXSMTPD" \
-u "$QMAILDUID" -g "$NOFILESGID" 0 smtp \
/var/qmail/bin/qmail-smtpd mail.example.com \
/usr/home/vpopmail/bin/vchkpw /usr/bin/true 2>&1
[root@qmail freebsd]#

Note: [LWQ]: concurrencyincoming isn't a standard qmail control file. It's a feature of the above script. Also, that's -1 (dash one) on the LOCAL line and -l (dash ell) on the tcpserver line.

Note: [LWQ]: The memory limit specified in the softlimit command may need to be raised depending upon your operating system and hardware platform. If attempts to connect to port 25 fail or remote systems are unable to send you mail, or you see a message like the following, then try raising softlimit to 3000000 or 4000000, or whatever you can afford, according to available system resources.

/usr/local/bin/tcpserver: error while loading shared libraries:
libc.so.6: failed to map segment from shared object: Cannot allocate memory 

Note: If you use above script, you will need to create the concurrencyincoming control file as used in the qmail-smtpd/run scripts above. (This is shown below). Since we are not using the qmail-smtpd/run script either from LWQ or QMR, so we don't need to create the concurrencyincoming file.

echo 20 > /var/qmail/control/concurrencyincoming
chmod 644 /var/qmail/control/concurrencyincoming

Why QMR's author is using vpopmail as QMAILUID and NOFILESGID variables in the qmail-smtpd/run script?

Answer: The following link ( http://qmail.jms1.net/upgrade-qmr.shtml) from JMS website clearly indicates the problems you may run into, IF you run qmail-smtpd as uid of vpopmail. Here is the explanation from the mentioned web-link:

The qmail-smtpd program handles the server end of an SMTP connection. It was originally designed to run as the userid qmaild. However, the /service/qmail-smtp/run script that qmailrocks installs sets things up so that qmail-smtpd runs as the vpopmail user, which means that a bug in qmail-smtpd (or in any of the programs that it calls, such as qmail-scanner, clamav, or spamassassin) could possibly erase all of your users' mailboxes. Not cool.

My (JMS) scripts set things up so that qmail-smtpd runs as the qmaild user, as djb originally designed it. Why did qmailrocks change from djb's standard? qmailrocks sets things up to use vpopmail's vchkpw program to validate any AUTH commands. And in order for vchkpw to read the files which actually contain the passwords (or the MD5 hashes of the passwords, or the file containing login information to a mysql server with that information) it needs to run as the vpopmail user.

At some point in the past, somebody decided that the best way to allow vchkpw to access these files was to run qmail-smtpd as the vpopmail user, and apparently qmailrocks blindly copied that person's "run" script when putting the qmailrocks package together.

There are two alternatives to this, both of which are more secure: You can give vchkpw the permissions it needs by making it "setuid", so that no matter which userid starts the program, it will always run as the vpopmail user. To me this option makes more sense than running qmail-smtpd itself as the vpopmail user, since a malicious email crafted to take advantage of some as-yet unknown security hole in clamav, spamassassin, or qmail-scanner, would not be able to access or delete any messages from your users' mailboxes- becasue only vchkpw runs as the vpopmail user (which owns all of the vpopmail mailbox directories) while the other programs continue to run as the qmaild user (or the qscand user, if you run qmail-scanner the same way I do.)

The vfixpermissions script will make your vchkpw binary setuid, along with fixing the ownership and permissions of everything else under the vpopmail user's home directory (which is where vpopmail stores everything.)

The AUTH_CDB patch, which is part of the newer versions of the combined patch, allows you to create a .cdb file (I use the name "auth.cdb" for this) which contains the valid userids and their encrypted passwords, and make that one file readable to the userid as which qmail-smtpd runs.

Either option is preferable to making qmail-smtpd, plus all of the child processes it runs, run as the vpopmail user.

The qmail-smtpd/run script by JMS

The qmail-smtpd/run file by JMS is quite big. It is download-able from (http://qmail.jms1.net/scripts/service-qmail-smtpd-run) . It has a lot of options in it, which are explained at: http://qmail.jms1.net/scripts/service-qmail-smtpd-run.shtml

cd /downlaods
wget http://qmail.jms1.net/scripts/service-qmail-smtpd-run

Let's have a look at the script:

[root@qmail downloads]# cat service-qmail-smtpd-run 
# service-qmail-smtpd-run (formerly "run.smtp" and "run.smtp.sslserver")
# John Simpson <jms1@jms1.net> 2003-07-05 to 2008-03-24                 
# Generic daemontools "run" script for qmail "smtp" or "smtpssl" service.
# Documentation: http://qmail.jms1.net/scripts/service-qmail-smtpd-run.shtml

exec 2>&1
LOCAL=`head -1 $VQ/control/me`             

# options for tcpserver/sslserver                                              


# these require the "tcpserver limits" patch for ucspi-tcp, available here:
# http://linux.voyager.hr/ucspi-tcp/                                       

#DIEMSG="421 $LOCAL Service temporarily unavailable"

# my newer version of the tcpserver limits patch allows you to specify
# individual DIEMSG values for each policy.                           
# http://qmail.jms1.net/ucspi-tcp/                                    

#DIEMSG_MAXLOAD="421 $LOCAL Server busy, try again later."
#DIEMSG_MAXCONNIP="421 $LOCAL Too many connections from your IP."
#DIEMSG_MAXCONNC="421 $LOCAL Too many connections from your network."

# options for programs which run before qmail-smtpd                            

#RBL_BAD="zen.spamhaus.org dnsbl.njabl.org dnsbl.sorbs.net bl.spamcop.net"




# options for qmail-smtpd itself                                               


# options pertaining to the AUTH command.                                      


# if using the AUTH_CDB method

# if using the CHECKPW method
TRUE=`which true`            

# to change the environment whenever somebody authenticates

# options pertaining to the "validrcptto.cdb" mechanism.                       
# see http://qmail.jms1.net/patches/validrcptto.cdb.shtml for details.         


# options pertaining to the SPF mechanism.                                     


# options pertaining to the Domainkeys mechanism.                              
# this requires an add-on patch.                                               


# options for programs which run after qmail-smtpd                             

# if you are using simscan...


# if you are using qmail-scanner, un-comment ONE of these lines.


# if you're using some other qmail-queue replacement, add your own line here
# with the correct value.                                                   

# script is on your system and you're free to edit it however you want, but    
# changing things below this point may cause strange things to happen. make    
# sure you understand what you're doing if you change anything below...        

QDUID=`id -u $QUSER`
QDGID=`id -g $QUSER`

if [ -z "$IP" -o "$IP" = "unset" ]
        echo "The IP variable is not set in the run script. Cannot start."
        sleep 5                                                           
        exit 1                                                            

if [ -z "$QDUID" -o -z "$QDGID" -o -z "$MAX" -o -z "$LOCAL" \
        -o -z "$SSL" -o -z "$AUTH" ]                         
        echo "One of the variables QDUID, QDGID, MAX, LOCAL, SSL, or AUTH"
        echo "is not set in the run script. Cannot start."                
        sleep 5                                                           
        exit 1                                                            

if [ ! -f $VQ/control/rcpthosts ]
        echo Creating emtpy $VQ/control/rcpthosts file to prevent open relay.
        touch $VQ/control/rcpthosts                                          
        chmod 644 $VQ/control/rcpthosts                                      

if [ "$SSL" = "1" ]
        if ! which sslserver > /dev/null 2>&1
                echo ERROR: sslserver not found in PATH [$PATH]
                exit 1                                         

        if [ ! -f $SSL_CERT ]
                echo ERROR: $SSL_CERT does not exist
                exit 1                              

        export CERTFILE=${SSL_CERT}
        export KEYFILE=""          
        export DHFILE=""           

        SCMD="sslserver -e"
        if [ -n "$SSL_CERT" ]
                export TLS_SERVER_CERT=${SSL_CERT}


if [ "$IP" = "" ]
        export RELAYCLIENT=""
elif [ -z "${SMTP_CDB:-}" ]  
        if [ ! -f "$SMTP_CDB" ]
                echo ERROR: $SMTP_CDB does not exist
                exit 1                              

        ACMD="-x $SMTP_CDB"

if [ "$AUTH" = "1" ]
        if [ -n "$AUTH_CDB" ]
                if [ ! -f $AUTH_CDB ]
                        echo ERROR: AUTH_CDB file [$AUTH_CDB] does not exist
                        exit 1                                              

                export AUTH_CDB
        elif [ -n "$CHECKPW" ] 
                if [ ! -f $CHECKPW ]
                        echo ERROR: $CHECKPW [$CHECKPW] program does not exist
                        exit 1                                                

                if [ -z "$LOCAL" ]
                        echo ERROR: LOCAL is not set in the run script
                        exit 1                                        
                elif [ -z "$TRUE" ]                                   
                        echo ERROR: TRUE is not set in the run script 
                        exit 1                                        
                elif [ ! -e $TRUE ]                                   
                        echo ERROR: $TRUE [$TRUE] is not an executable
                        exit 1                                        

                ARGS=" $LOCAL $CHECKPW $TRUE"
                echo ERROR: AUTH=1 but no AUTH_CDB or CHECKPW
                exit 1                                       

# make RBL command (if needed)          


if [ -n "$RBLSMTPD_PROG" ]
        if [ -n "$RBL_GOOD" ]
                for name in $RBL_GOOD
                        RBLCMD="$RBLCMD -a $name"

        if [ -n "$RBL_BAD" ]
                for name in $RBL_BAD
                        RBLCMD="$RBLCMD -r $name"

        if [ -n "$RBLCMD" ]
                RBLCMD="$RBLSMTPD_PROG -t0 $RBLCMD"

# make domainkeys command (if needed)   

if [ "$DOMAINKEYS" = "1" ]
        if [ -f "$VQ/bin/qmail-dk" ]
                if [ -n "$QMAILQUEUE" ]
                        export DKQUEUE="$QMAILQUEUE"
                export AUTH_UNSET_DKVERIFY=1        
                export QMAILQUEUE="$VQ/bin/qmail-dk"

                if [ -n "$DKVERIFY" ] ; then export DKVERIFY ; fi
                if [ -n "$DKSIGN"   ] ; then export DKSIGN   ; fi
                echo ERROR: $VQ/bin/qmail-dk not found, cannot use domainkeys

# handle variables which may not have been set, but need to exist even
# if they contain blank values                                        

if [ -z "$RECORDIO" ]

if [ -z "$GREYLIST" ]

# do the deed                           

for n in SSL                                    \
        MAXLOAD                                 \
        MAXCONNIP                               \
        MAXCONNC                                \
        DIEMSG                                  \
        DIEMSG_MAXLOAD                          \
        DIEMSG_MAXCONNIP                        \
        DIEMSG_MAXCONNC                         \
        JGREYLIST_DIR                           \
        JGREYLIST_NOREV                         \
        JGREYLIST_BY_IP                         \
        JGREYLIST_HOLDTIME                      \
        JGREYLIST_LOG                           \
        JGREYLIST_LOG_PID                       \
        JGREYLIST_LOG_SMTP                      \
        JGREYLIST_TIMEOUT                       \
        JGREYLIST_LIMIT                         \
        SMTPGREETING                            \
        GREETDELAY                              \
        DROP_PRE_GREET                          \
        FORCE_TLS                               \
        DENY_TLS                                \
        MFCHECK                                 \
        MAXRCPT                                 \
        RELAYREJ                                \
        QMAILSMTPD_LOG_MAIL                     \
        QMAILSMTPD_LOG_RCPT                     \
        QMAILSMTPD_HELP_VERSION                 \
        REQUIRE_AUTH                            \
        ALLOW_INSECURE_AUTH                     \
        AUTH_CDB                                \
        AUTH_SET_MFCHECK                        \
        AUTH_SET_MAXRCPT                        \
        AUTH_SET_DATABYTES                      \
        AUTH_SET_SPFBEHAVIOR                    \
        AUTH_SET_VALIDRCPTTO_LIMIT              \
        AUTH_SET_VALIDRCPTTO_LOG                \
        AUTH_SET_SPF_LOG                        \
        AUTH_SET_RELAYREJ                       \
        AUTH_SET_VALIDRCPTTO_CDB                \
        AUTH_SET_QMAILSMTPD_LOG_MAIL            \
        AUTH_SET_QMAILSMTPD_LOG_RCPT            \
        VALIDRCPTTO_CDB                         \
        VALIDRCPTTO_LIMIT                       \
        VALIDRCPTTO_LOG                         \
        SPFBEHAVIOR                             \
        SPF_LOG                                 \
        SPF_BLOCK_PLUS_ALL                      \
        DKVERIFY                                \
        AUTH_SET_DKSIGN                         \
        QMAILQUEUE                              \
        NOP0FCHECK                              \
        SIMSCAN_DEBUG                           \
        # note: not 100% sure "eval" works under old-school /bin/sh
        eval "if [ -n \"\$$n\" ];then echo \"$n=\\\"\$$n\\\"\";export $n;fi"


echo "command-line: exec $CMD 2>&1"
exec $CMD 2>&1

# this will only be reached if the exec fails

echo ERROR: command did not run correctly
exit 1
[root@qmail downloads]#

Below is the short summary of the tcpserver options used in the qmail-smtpd/run scripts shown above. Details about the tcpserver command/program can be studied from the following location: http://cr.yp.to/ucspi-tcp/tcpserver.html .

Summary of tcpserver options

Syntax: tcpserver opts host port prog

Where: opts is a series of getopt-style options; host is one argument; port is one argument; prog consists of one or more arguments. tcpserver waits for connections from TCP clients. For each connection, it runs prog, with descriptor 0 reading from the network and descriptor 1 writing to the network. It also sets up several environment variables.

General options:

  • -v: Verbose. Print error messages and status messages.

Connection options:

  • -c n: Do not handle more than n simultaneous connections. If there are n simultaneous copies of prog running, defer acceptance of a new connection until one copy finishes. n must be a positive integer. Default: 40.
  • -x cdb: Follow the rules compiled into cdb by tcprules. These rules may specify setting environment variables or rejecting connections from bad sources. You can rerun tcprules to change the rules while tcpserver is running.
  • -g gid: Switch group ID to gid after preparing to receive connections. gid must be a positive integer.
  • -u uid: Switch user ID to uid after preparing to receive connections. uid must be a positive integer.
  • -1: After preparing to receive connections, print the local port number to standard output.

Data-gathering options:

  • -l localname: Do not look up the local host name in DNS; use localname for the environment variable $TCPLOCALHOST. A common choice for localname is 0. To avoid loops, you must use this option for servers on TCP port 53.
  • -R: Do not attempt to obtain $TCPREMOTEINFO from the remote host. To avoid loops, you must use this option for servers on TCP ports 53 and 113.
The softlimit program

A little explanation of the softlimit program (by DJB), from the link: http://cr.yp.to/daemontools/softlimit.html

softlimit runs another program with new resource limits. Syntax: softlimit opts child Where: opts is a series of getopt-style options; child consists of one or more arguments. softlimit sets soft resource limits as specified by opts. It then runs child program.

Options In each of the following options, n may be =, indicating that the soft limit should be set equal to the hard limit.

Options controlling memory use:

  • -m n: Same as -d n -s n -l n -a n.
  • -d n: Limit the data segment per process to n bytes.
  • -s n: Limit the stack segment per process to n bytes.
  • -l n: Limit the locked physical pages per process to n bytes. This option has no effect on some operating systems.
  • -a n: Limit the total of all segments per process to n bytes. This option has no effect on some operating systems.
  • -o n: Limit the number of open file descriptors per process to n. This option has no effect on some operating systems.
  • -p n: Limit the number of processes per uid to n.

Options controlling file sizes:

  • -f n: Limit output file sizes to n bytes.
  • -c n: Limit core file sizes to n bytes.

Efficiency options:

  • -r n: Limit the resident set size to n bytes. This limit is not enforced unless physical memory is full.
  • -t n: Limit the CPU time to n seconds. This limit is not enforced except that the process receives a SIGXCPU signal after n seconds.

Notice that JMS does not use softlimit in his qmail-smtpd/run script. The reason I see is that in modern linux systems the limits can be controlled through /etc/security/limits.conf file. And more importantly most of the limits are now controlled by the kernel itself, in the shape of better resource management. So apparently there is no need to run your programs as a softlimit's child. [Anyone with a better explanation?]

Conclusion for qmail-smtpd/run script

Copy the downloaded JMS smtpd-run file to /var/qmail/supervise/qmail-smtpd/run .

cp /downloads/service-qmail-smtpd-run /var/qmail/supervise/qmail-smtpd/run

Before we move on and edit the file, I would advise you to check the list/details of patches, on this location (http://qmail.jms1.net/patches/combined-details.shtml), which JMS has included in his combined patch. It is a 22 page document , if you print it on A4 size paper! This would definitely help us what to select / enable, and what not in the qmail-smtpd/run script.

It mainly contains vpopmail passwd program, Grey Listing checks, Delays, SPF, RBL, AUTH mechanism, DomainKeys QmailScanner, etc.

I have still not installed the grey-listing software, Qmail-Scanner, and domain keys. So I have not enabled them yet in this qmail-smtpd/run script. I will enable them one by one when the related software is installed and configured.

Edit this file and adjust values according to your needs. (The SMTP_CDB variable needs to be updated. Its value should not be /etc/tcp/smtp.cdb , rather, it's value should be /etc/tcp.smtp.cdb. This is updated after encountering an error a little later.)

The most important variables to fix for the time-being are: IP and SMTP_CDB. You should also disable anything related to VALIDRCPTTO at this moment.

vi /var/qmail/supervise/qmail-smtpd/run
#(my configuration part of the script looks like this):
exec 2>&1                                                                   
LOCAL=`head -1 $VQ/control/me`                                              
DIEMSG="421 $LOCAL Service temporarily unavailable"                         
DIEMSG_MAXLOAD="421 $LOCAL Server busy, try again later."                   
DIEMSG_MAXCONNIP="421 $LOCAL Too many connections from your IP."            
DIEMSG_MAXCONNC="421 $LOCAL Too many connections from your network."        
RBL_BAD="zen.spamhaus.org bl.spamcop.net"                                   
SMTPGREETING="$LOCAL NO UCE"                                                
#TRUE=`which true`                                                           
. . . 
. . . 

Note: If you do not select the options correctly in the run file, your qmail instance may start, but when you try to connect to it, it will show you an error. Such as the following:

[kamran@kworkbee ~]$ telnet qmail.example.com 25
Connected to qmail.example.com.
Escape character is '^]'.
421 unable to read controls (#4.3.0)
Connection closed by foreign host.
[kamran@kworkbee ~]$

So, the tip is to enable few things at a time and work gradually to a securer system.

The qmail-smtpd/log/run script

Now is the time to create the /var/qmail/supervise/qmail-smtpd/log/run file:

cat > /var/qmail/supervise/qmail-smtpd/log/run << EOF
exec setuidgid qmaill multilog t s50000000 n20 /var/log/qmail/qmail-smtpd 2>&1

Note: I don't see a need to export the PATH, so I have removed that from the script above.

Set correct permissions for supervise scripts

Make the run files executable [LWQ]: (recommended)

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

Note: Add qmail-pop3/* files to the list above, if you are using qmail-pop3 for POP3 service.

Then set up the log directories[LWQ]: Note: We have already setup log directories a while ago, with a slight modification. So we will NOT execute the following three lines.

# mkdir /var/log/qmail
# mkdir -p /var/log/qmail/smtpd
# chown qmaill /var/log/qmail /var/log/qmail/smtpd

Finally, link the supervise directories into /service. However, please note that the qmail system will start automatically shortly after these links are created. So at this point you should make sure that no service is running on port 25, such as sendmail, postfix or exim. If they are running, it is time to stop them:

service sendmail stop
service exim stop
service postfix stop

# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 :::22                       :::*                        LISTEN      1799/sshd
tcp        0      0 ::ffff:    ::ffff:  ESTABLISHED 2031/0

Good! No SMTP software/service running. So lets link the qmail supervise directories into /service .

ln -s /var/qmail/supervise/qmail-send   /var/qmail/supervise/qmail-smtpd   /service

Note: The /service directory is created when daemontools software is installed.

Note: Without linking, the qmailctl command will give the following error:

/service/qmail-send: unable to chdir: file does not exist
/service/qmail-send/log: unable to chdir: file does not exist
/service/qmail-smtpd: unable to chdir: file does not exist
/service/qmail-smtpd/log: unable to chdir: file does not exist
messages in queue: 0
messages in queue but not yet preprocessed: 0

After the linking, the qmail system would now be running automatically. Lets check:

# qmailctl stat
/service/qmail-send: up (pid 17094) 3 seconds
/service/qmail-send/log: up (pid 17095) 3 seconds
/service/qmail-smtpd: up (pid 17116) 1 seconds
/service/qmail-smtpd/log: up (pid 17098) 3 seconds
messages in queue: 0
messages in queue but not yet preprocessed: 0

The qmail-smtpd service seems to be stuck at 1 second. This is not good. Lets try to find the reason:

# tail /var/log/qmail/qmail-smtpd/current
@400000004a8d955234dccc7c ERROR: /etc/tcp.smtp.cdb does not exist
@400000004a8d955336fb5b04 ERROR: /etc/tcp.smtp.cdb does not exist
@400000004a8d9554382e1a5c ERROR: /etc/tcp.smtp.cdb does not exist
@400000004a8d95553982bd7c ERROR: /etc/tcp.smtp.cdb does not exist

We know that in qmailctl file, we have a section as :-

    tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
    chmod 644 /etc/tcp.smtp.cdb
    echo "Reloaded /etc/tcp.smtp."

Create the tcprules file. In LWQ, it is titled as SMTP Access Control.

Allow the local host to inject mail via SMTP:

echo '127.:allow,RELAYCLIENT=""' >>/etc/tcp.smtp

Now execute the following command to compile CDB rules: [LWQ]

qmailctl cdb

Now lets stop qmail and start it again.

qmailctl stop
qmailctl start

# wait for a few seconds here, then
qmail stat

# qmailctl stat
/service/qmail-send: up (pid 18357) 17 seconds
/service/qmail-send/log: up (pid 18356) 17 seconds
/service/qmail-smtpd: up (pid 18360) 17 seconds
/service/qmail-smtpd/log: up (pid 18361) 17 seconds
messages in queue: 0
messages in queue but not yet preprocessed: 0

As you can see, now your qmail-send and qmail-smtpd (and their respective log services) are all up for the same amount of time. This indicates all is well. Check through netstat as well:

# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 *                   LISTEN      2012/tcpserver
tcp        0      0 :::22                       :::*                        LISTEN      1799/sshd
tcp        0      0 ::ffff:    ::ffff:  ESTABLISHED 2031/0
[root@qmail ~]#

If you don't see tcpserver running on port 25 listed here, then check the log file (/var/log/qmail/qmail-smtpd/current).

# tail /var/log/qmail/qmail-smtpd/current

@400000005152d5fe2e586eec The IP variable is not set in the run script. Cannot start.
@400000005152d6032eec4a7c The IP variable is not set in the run script. Cannot start.

As per the error in the log file, we need to set the IP in the qmail-smtpd/run file.

# vi /var/qmail/supervise/qmail-smtpd/run

When things are good, you will see something similar to the following in the log files.

# tail -f /var/log/qmail/qmail-smtpd/current 
. . . 
. . . 
@400000004a8d98fc2ced80c4 SSL="0"
@400000004a8d98fc2cf06adc MAXLOAD="150"
@400000004a8d98fc2cf1f564 MAXCONNIP="2"
@400000004a8d98fc2cf43784 MAXCONNC="5"
@400000004a8d98fc2cf5cdc4 DIEMSG="421 qmail.example.com Service temporarily unavailable"
@400000004a8d98fc2cf8a06c DIEMSG_MAXLOAD="421 qmail.example.com Server busy, try again later."
@400000004a8d98fc2cfa59d4 DIEMSG_MAXCONNIP="421 qmail.example.com Too many connections from your IP."
@400000004a8d98fc2cfca7ac DIEMSG_MAXCONNC="421 qmail.example.com Too many connections from your network."
@400000004a8d98fc2d09135c SMTPGREETING="qmail.example.com NO UCE"
@400000004a8d98fc2d0cd064 DROP_PRE_GREET="1"
@400000004a8d98fc2d0e7a2c FORCE_TLS="0"
@400000004a8d98fc2d10106c DENY_TLS="0"
@400000004a8d98fc2d123f04 MFCHECK="3"
@400000004a8d98fc2d13bdd4 MAXRCPT="100"
@400000004a8d98fc2d16425c RELAYREJ="1"
@400000004a8d98fc2d180f4c QMAILSMTPD_LOG_MAIL="1"
@400000004a8d98fc2d1a3de4 QMAILSMTPD_LOG_RCPT="1"
@400000004a8d98fc2d1c0ad4 QMAILSMTPD_HELP_VERSION="1"
@400000004a8d98fc2d1e5c94 REQUIRE_AUTH="0"
@400000004a8d98fc2d20065c ALLOW_INSECURE_AUTH="0"
@400000004a8d98fc2d37cc4c SPFBEHAVIOR="3"
@400000004a8d98fc2d394f04 SPF_LOG="1"
@400000004a8d98fc2d3bbc1c SPF_BLOCK_PLUS_ALL="1"
@400000004a8d98fc2d40964c NOP0FCHECK="1"
@400000004a8d98fc2d45aefc command-line: exec tcpserver -vR -l qmail.example.com -c 30 -u 702 -g 700 -x /etc/tcp.smtp.cdb 25 rblsmtpd -t0  -r zen.spamhaus.org -r bl.spamcop.net   qmail-smtpd  2>&1
@400000004a8d98fc2d6d0d1c tcpserver: status: 0/30

At this point, qmail-smtpd seems good!

And, qmail-send also seems good! (see log below).

# tail -f /var/log/qmail/qmail-send/current
. . . 
@400000004a8d949a05aab4dc status: local 0/10 remote 0/20
@400000004a8d94ab0fdfdd9c status: qmail-todo stop processing asap
@400000004a8d94ab0fe000c4 status: exiting
@400000004a8d94b32d8c87b4 status: local 0/10 remote 0/20
@400000004a8d95411e2ad8d4 status: qmail-todo stop processing asap
@400000004a8d95411e2afbfc status: exiting
@400000004a8d95411f7f1664 status: local 0/10 remote 0/20
@400000004a8d98fc29f2ea1c status: qmail-todo stop processing asap
@400000004a8d98fc29f3865c status: exiting
@400000004a8d98fc2c477cc4 status: local 0/10 remote 0/20

Note: You will not find any log entry for qmail related errors (qmail start / stop /error events) in /var/log/messages , or in /var/log/maillog .

Starting qmail at system startup

Linking of the supervise script directories in /service directory and the following line added in the /etc/inittab file (or the file in /etc/init/ in CENTOS 6+) ensures that the qmail service will be started at system boot time.

tail /etc/inittab

Remove other SMTP software from your system

At this point, you can remove any other SMTP software from your system.

# rpm -e sendmail sendmail-cf exim postfix --nodeps
error: package sendmail is not installed
error: package sendmail-cf is not installed
error: package postfix is not installed

Note [LWQ]: If you're using an RPM-based Linux distribution like Red Hat, removing the MTA package might cause problems down the road. Utilities that update the system might try to reinstall Sendmail, or MUA packages might not install because they can't tell an MTA is installed. Mate Wierdl provides a stub package called "fake_mta" that can be installed to prevent these problems. Simply install the RPM available from ftp://ftp.csi.hu/mw/fake_mta-1-10memphis.noarch.rpm . However, I could not find the fake_mta on the net. (Purely my fault). So I am skipping it.

[LWQ]: Lastly, replace any existing /usr/lib/sendmail with the qmail version: (recommended)

mv /usr/lib/sendmail /usr/lib/sendmail.old                  # ignore any errors
mv /usr/sbin/sendmail /usr/sbin/sendmail.old                # ignore any errors
chmod 000 /usr/lib/sendmail.old /usr/sbin/sendmail.old        # ignore any errors
ln -s /var/qmail/bin/sendmail /usr/lib
ln -s /var/qmail/bin/sendmail /usr/sbin

Note: QMR does the same in Part-10.

Note[LWQ]: It's important to create the sendmail links, regardless of the previous MTA, if any. The sendmail command is invoked by many applications for sending mail.

Note: QMR , when it runs the finalize_linux.script in part 9, copies the smtpd_ssl and pop3d run scripts as well to the supervise directory. Also sets up links in the /service directory for them. We are not going to use QMR's smtpd_ssl , at least for now. And about pop3, though it seems very tempting, I would not install qmail's own pop3 daemon, because, I will be using the pop3 and IMAP services of either Courier or DoveCot. So no need to go into hassle of installing qmail-pop3 in the first place.

Create System Email Aliases [LWQ]

There are three system aliases that should be created on all qmail installations:

  • Alias Purpose
  • postmaster RFC 2821 required, points to the mail adminstrator (you)
  • mailer-daemon de facto standard recipient for some bounces
  • root redirects mail from privileged account to the system administrator
  • abuse de facto standard recipient for abuse complaints

To create these aliases, decide where you want each of them to go (a local user or a remote address) and create and populate the appropriate .qmail files. For example, say local user dave is both the system and mail administrator:

#echo dave > /var/qmail/alias/.qmail-root
#echo dave > /var/qmail/alias/.qmail-postmaster
#ln -s .qmail-postmaster /var/qmail/alias/.qmail-mailer-daemon
#ln -s .qmail-postmaster /var/qmail/alias/.qmail-abuse
#chmod 644 /var/qmail/alias/.qmail-root /var/qmail/alias/.qmail-postmaster

What I want to do is setup these links to point to postmaster@yourdomain.com . I will set them to postmaster@example.com . Note that QMR also creates these aliases.

echo postmaster@example.com  > /var/qmail/alias/.qmail-root
echo postmaster@example.com  > /var/qmail/alias/.qmail-postmaster
ln -s /var/qmail/alias/.qmail-postmaster /var/qmail/alias/.qmail-mailer-daemon
ln -s /var/qmail/alias/.qmail-postmaster /var/qmail/alias/.qmail-abuse
chmod 644 /var/qmail/alias/.qmail-root /var/qmail/alias/.qmail-postmaster

Check the qmail installation [LWQ]

qmail should now be running. First run qmailctl stat to verify that the services are up and running:

# qmailctl stat
/service/qmail-send: up (pid 30303) 187 seconds
/service/qmail-send/log: up (pid 30304) 187 seconds
/service/qmail-smtpd: up (pid 30305) 187 seconds
/service/qmail-smtpd/log: up (pid 30308) 187 seconds
messages in queue: 0
messages in queue but not yet preprocessed: 0

All four services should be "up" for more than one second. If they're not, you've probably got a typo in the associated run script or you skipped one or more steps in creating the necessary files, directories, or links. Go back through the installation step-by-step and double check your work. You can also download and run the inst_check script, available from http://lifewithqmail.org/inst_check. For example:

# sh inst_check
! /var/log/qmail has wrong owner, should be qmaill
...try: chown qmaill /var/log/qmail

If inst_check finds problems, fix them and re-run it. When everything looks right, inst_check will report:
    Congratulations, your LWQ installation looks good!

So let's do that:

cd /downloads/
wget http://lifewithqmail.org/inst_check
chmod +x inst_check

[root@qmail downloads]# ./inst_check
! /var/qmail/control/concurrencyincoming is missing
...try: echo 20 >/var/qmail/control/concurrencyincoming
! /var/log/qmail has wrong mode, should be 755
...try: chmod 755 /var/log/qmail
! /var/log/qmail/smtpd is missing
...try: mkdir -p /var/log/qmail/smtpd
! Alias for root is missing
...try: echo me >/var/qmail/alias/.qmail-root
! Alias for postmaster is missing
...try: echo me >/var/qmail/alias/.qmail-postmaster
! Alias for mailer-daemon is missing
...try: echo me >/var/qmail/alias/.qmail-mailer-daemon
[root@qmail downloads]#

Wow! So many errors!

Actually I forgot to create the email aliases mentioned above. I created them now. Also created the concurrencyincoming file, and fixed the permissions of /var/log/qmail directory.

echo 20 >/var/qmail/control/concurrencyincoming
chmod 755 /var/log/qmail -R

The last check left is about missing /var/log/qmail/smtpd directory:

[root@qmail downloads]# ./inst_check
! /var/log/qmail/smtpd is missing
...try: mkdir -p /var/log/qmail/smtpd
[root@qmail downloads]#

You can ignore this error, as we are using a different name (/var/log/qmail/qmail-smtpd) for it. As shown below:

# ls /var/log/qmail/ -l
total 12
drwxr-xr-x 2 qmaill root 4096 Aug 18 15:32 qmail-pop3d
drwxr-xr-x 2 qmaill root 4096 Aug 21 12:11 qmail-send
drwxr-xr-x 2 qmaill root 4096 Aug 21 12:11 qmail-smtpd

If you want to fee happy, and desperate to see the congratulations message, you can create this directory and run the test script. You will only be fooling the script! (Not needed)

# mkdir /var/log/qmail/smtpd
# chown qmaill /var/log/qmail/smtpd/

# ./inst_check
Congratulations, your LWQ installation looks good!

Note: A little about QMR's qmr_isnt_check script: QMR runs the qmr_inst_check script in part 11. QMR has modified the default inst_check script from LWQ. The following differences exist. The “diff” is shown below with little summary in simple words explained further below:

[root@qmail downloads]# diff /downloads/qmailrocks/scripts/util/qmr_inst_check ./inst_check 
< # Qmailrocks, 2003-12-05                                                                  
< # I've modified Dave Sill's script slightly to accomodate                                 
< # the variations between his Qmail installation and the                                   
< # Qmailrocks.org installation. The Qmailrocks version                                     
< # of this script takes into account the slightly different                                
< # loggind directory setup and a few permissions differences.                              
< # http://www.qmailrocks.org/downloads/scripts/qmr_inst_check                              
< CHECKPOP=y                                                                                
< CHECKSEND=y                                                                               
> CHECKPOP=n                                                                                
<   echo "...try: echo 30 >$QMHOME/control/concurrencyincoming"
>   echo "...try: echo 20 >$QMHOME/control/concurrencyincoming"
< LOGDIRS="/var/log/qmail /var/log/qmail/qmail-smtpd"
> LOGDIRS="/var/log/qmail /var/log/qmail/smtpd"
<   LOGDIRS="$LOGDIRS /var/log/qmail/qmail-pop3d"
< fi
< if [ "$CHECKSEND" = "y" ]; then
<   LOGDIRS="$LOGDIRS /var/log/qmail/qmail-send"
>   LOGDIRS="$LOGDIRS /var/log/qmail/pop3d"
<   elif [ "`ls -ld $i|awk '{print $1}'`" != "drwxr-x---" ]; then
<     echo "! $i has wrong mode, should be 750"
<     echo "...try: chmod 750 $i"
>   elif [ "`ls -ld $i|awk '{print $1}'`" != "drwxr-xr-x" ]; then
>     echo "! $i has wrong mode, should be 755"
>     echo "...try: chmod 755 $i"
<   echo "Congratulations, your Qmailrocks.org Qmail installation looks good!"
>   echo "Congratulations, your LWQ installation looks good!"

Summary of differences between QMR and LWQ's installation check scripts in plain words:

  • QMR's script has CHECKPOP and CHECKSEND enabled as “y”. Whereas LWQ's script uses CHECKPOP=n .
  • QMR sets concurrencyincoming as 30, whereas LWQ sets it as 20.
  • QMR checks LOGDIRS="/var/log/qmail /var/log/qmail/qmail-smtpd", whereas LWQ checks LOGDIRS="/var/log/qmail /var/log/qmail/smtpd" .
  • QMR additionally checks for LOGDIRS="$LOGDIRS /var/log/qmail/qmail-pop3d" .
  • QMR also checks for LOGDIRS="$LOGDIRS /var/log/qmail/qmail-send" if CHECKSEND=y
  • QMR wants permissions of log directories as “750”, whereas LWQ expects them to be “755”. Not an issue.

LWQ: The readproctitle program keeps a log of error messages generated by services managed by svscan. To see these messages, use ps or some other process listing command. For example, you might see something like:

# ps -efl | grep "service errors" | grep -v grep
000 S root      1006  1001  0  76   0    -   334 pipe_w Mar31 ?        00:00:00
readproctitle service errors: ...unable to start qmail-smtpd/run: exec format error

In this case, the problem is that there is an error in the first line of the /service/qmail-smtpd/run script--most likely caused by the file being is DOS format (CR-LF line endings instead of Unix's LF-only). It sometimes helps to run a service manually in order to find configuration problems. For example, if your qmail-smtpd/log service isn't running, do:

cd /service/qmail-smtpd/log
svc -d .
if no errors, enter a line of text and press ENTER
if still no errors, enter CTRL-D (end of file)

At this point, you should be able to identify the problem and fix it. Once that's done, return to the service's directory, if necessary, and do:

svc -u .

If there are no problems, your readproctitle will show you dots only, instead of errors:

[root@qmail downloads]# ps -efl | grep "service errors" | grep -v grep
0 S root      2001  1963  0  84   0 -   378 pipe_w 12:11 ?        00:00:00 readproctitle service errors: ................................................................................................................................................................................................................................................................................................................................................................................................................

Test the qmail installation

Once the services are all up with >1 second uptime, follow the instructions in TEST.deliver and TEST.receive to verify that they're working correctly. Note that using these instructions, logging will be accomplished by multilog to /var/log/qmail, not splogger to something like /var/log/maillog.

Note: If you chose maildir mailbox format as the default delivery method, you will need to create a Maildir directory in your home directory and alias's home directory before trying these instructions. See the maildir section to see how to properly create this directory.

If I am to follow these instructions in TEST.deliver and TEST.receive files (inside the /usr/local/src/qmail-1.03/ directory), I need to have some OS accounts to send mail back and forth. Also I need to have a special directory named Maildir in home directories of those users. This is because vpopmail and mysql are not yet installed. So all testing will be done through OS users.

useradd kamran
useradd feysal

echo 'redhat' | passwd --stdin kamran
echo 'redhat'| passwd --stdin feysal

mkdir /home/kamran/Maildir
mkdir /home/feysal/Maildir
chown kamran:kamran /home/kamran/* -R
chown kamran:kamran /home/feysal/* -R

Try the tests and check logs in parallel:

[root@qmail qmail-1.03]# echo to: kamran | /var/qmail/bin/qmail-inject

Check the log:-

# tail -F  /var/log/qmail/qmail-send/current
@400000004a8e95f405201c64 new msg 65096
@400000004a8e95f4052471c4 info msg 65096: bytes 217 from <root@qmail.example.com> qp 3059 uid 0
@400000004a8e95f4052bf78c starting delivery 1: msg 65096 to local kamran@qmail.example.com
@400000004a8e95f4053970c4 status: local 1/10 remote 0/20
@400000004a8e95f40be3154c delivery 1: deferral: Temporary_error_on_maildir_delivery._(#4.3.0)/
@400000004a8e95f40be36f24 status: local 0/10 remote 0/20

A note from LWQ , explains the possible reason: Note: qmail-local can deliver mail to maildir mailboxes, but it can't create them. Maildir mailboxes should be created with the maildirmake program that comes with qmail. E.g., "maildirmake ~/Maildir". Be sure to run maildirmake as the owner of the maildir, not as root. Your useradd or adduser command might support a "skeleton" directory, e.g. /etc/skel, where you can create a maildir that will be copied for all new users.

A maildir mailbox is a directory containing three subdirectories, new, cur, and tmp. Each message in a maildir mailbox is in a separate file in one of the subdirectories, depending upon its status: new is for unread messages, cur is for messages that have been seen, and tmp is for messages in the process of being delivered.

So I deleted the old directories and created new ones using the following commands:

[root@qmail qmail-1.03]# rm -fr /home/kamran/Maildir
[root@qmail qmail-1.03]# rm -fr /home/feysal/Maildir

[root@qmail qmail-1.03]# su -c "/var/qmail/bin/maildirmake ~/Maildir" kamran
[root@qmail qmail-1.03]# su -c "/var/qmail/bin/maildirmake ~/Maildir" feysal

The following shows that this command has created the Maildir in the user's home directory with desired permssions, as well as directories cur, new and tmp in the ~/Maildir .

[root@qmail qmail-1.03]# ls -l /home/kamran/
total 4
drwx------ 5 kamran kamran 4096 Aug 21 16:29 Maildir

[root@qmail qmail-1.03]# ls -l /home/kamran/Maildir/
total 12
drwx------ 2 kamran kamran 4096 Aug 21 16:29 cur
drwx------ 2 kamran kamran 4096 Aug 21 16:29 new
drwx------ 2 kamran kamran 4096 Aug 21 16:29 tmp
[root@qmail qmail-1.03]#

Try the mail test again:

[root@qmail qmail-1.03]# echo to: kamran | /var/qmail/bin/qmail-inject

This time, it succeeded:

[root@qmail qmail-1.03]# tail -F /var/log/qmail/qmail-send/current
@400000004a8ea2321cf0b6dc new msg 65097
@400000004a8ea2321cf206cc info msg 65097: bytes 217 from <root@qmail.example.com> qp 3155 uid 0
@400000004a8ea2321cf3f2fc starting delivery 7: msg 65097 to local kamran@qmail.example.com
@400000004a8ea2321cfa05ac status: local 1/10 remote 0/20
@400000004a8ea2321dc3d1d4 delivery 7: success: did_1+0+0/
@400000004a8ea2321dc698c4 status: local 0/10 remote 0/20
@400000004a8ea2321dd1a0fc end msg 65097

If you check inside user kamran's Maildir/new directory, you will find a file:

[root@qmail qmail-1.03]# ls ~kamran/Maildir/new/ -lh
total 4.0K
-rw------- 1 kamran kamran 294 Aug 21 16:33 1250861608.3158.qmail.example.com
[root@qmail qmail-1.03]#

This mail file, has the following contents:

[root@qmail qmail-1.03]# cat ~kamran/Maildir/new/1250861608.3158.qmail.example.com
Return-Path: <root@qmail.example.com>
Delivered-To: kamran@qmail.example.com
Received: (qmail 3155 invoked by uid 0); 21 Aug 2009 16:33:28 +0300
Date: 21 Aug 2009 16:33:28 +0300
Message-ID: <20090821133328.3154.qmail@qmail.example.com>
From: root@qmail.example.com
to: kamran@qmail.example.com
[root@qmail qmail-1.03]#

Try these tests too, and check logs in parallel:

echo to: nonexistantaccount | /var/qmail/bin/qmail-inject

echo to: kamran@wbitt.com | /var/qmail/bin/qmail-inject # Will reach your SPAM folder if not in INBOX.

echo to: POSTmaster | /var/qmail/bin/qmail-inject

So this seems to be working properly. I can try from another machine, using smtpd protocol and send another mail to the same user. Here is how:

I log on to another machine and try sending the mail:

[kamran@kworkbee ~]$ telnet qmail.example.com 25
Connected to qmail.example.com.
Escape character is '^]'.
220 qmail.example.com NO UCE ESMTP
ehlo kworkbee.homedomain.com
250-qmail.example.com NO UCE
250-SIZE 0
mail from: kamranazeem@homedomain.com
250 ok
rcpt to: kamran@example.com
553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)
rcpt to: kamran@qmail.example.com
250 ok
354 go ahead
Subject: test message
This is a test over SMTP from another machine.
250 ok 1250862005 qp 3166
221 qmail.example.com NO UCE
Connection closed by foreign host.
[kamran@kworkbee ~]$

I have the following in my qmail-send log file:

@400000004a8ea3bf06373d6c new msg 65097
@400000004a8ea3bf0637935c info msg 65097: bytes 379 from <kamranazeem@homedomain.com> qp 3166 uid 702
@400000004a8ea3bf0649a844 starting delivery 8: msg 65097 to local kamran@qmail.example.com
@400000004a8ea3bf0649c784 status: local 1/10 remote 0/20
@400000004a8ea3bf071368b4 delivery 8: success: did_1+0+0/
@400000004a8ea3bf07164afc status: local 0/10 remote 0/20
@400000004a8ea3bf071bc16c end msg 65097
@400000004a8ea40502fa17b4 starting delivery 9: msg 65096 to local kamran@qmail.example.com
@400000004a8ea4050309dec4 status: local 1/10 remote 0/20
@400000004a8ea40504f5860c delivery 9: success: did_1+0+0/
@400000004a8ea40504f5d814 status: local 0/10 remote 0/20
@400000004a8ea40504f61e64 end msg 65096

And I should have a new file in the Maildir/new of user kamran:

[root@qmail qmail-1.03]# ls ~kamran/Maildir/new/ -lh
total 12K
-rw------- 1 kamran kamran 294 Aug 21 16:33 1250861608.3158.qmail.example.com
-rw------- 1 kamran kamran 460 Aug 21 16:40 1250862005.3171.qmail.example.com

[root@qmail qmail-1.03]# cat ~kamran/Maildir/new/1250862005.3171.qmail.example.com
Return-Path: <kamranazeem@homedomain.com>
Delivered-To: kamran@qmail.example.com
Received: (qmail 3166 invoked from network); 21 Aug 2009 16:39:48 +0300
Received: from unknown (HELO kworkbee.homedomain.com) (
  by qmail.example.com with SMTP; 21 Aug 2009 16:39:48 +0300
Received-SPF: none (qmail.example.com: domain at homedomain.com does not designate permitted sender hosts)
Subject: test message
This is a test over SMTP from another machine.
[root@qmail qmail-1.03]#

Great! So this seems to be working. There are a lot of tests explained in the TEST.deliver file in the Qmail source directory, which you can (should) perform , before moving on.

EZMLM (Easy Mailing List Management)

From: http://untroubled.org/ezmlm/faq/What-is-the-difference-between-ezmlm-and-ezmlm_002didx_003f.html#What-is-the-difference-between-ezmlm-and-ezmlm_002didx_003f

1.4 What is the difference between ezmlm and ezmlm-idx?

ezmlm-0.53 is a qmail-based mailing list manager written by Dan J. Bernstein. It has all the basic functionality of a mailing list manager, such as subscriber address management including automated bounce handling as well as message distribution and archiving.

ezmlm-idx originated as an add-on to ezmlm. It now exists as a complete package on its own, but can still be considered essentially as an extension to ezmlm. It adds multi-message threaded message retrieval from the archive, digests, message and subscription moderation, and a number of remote administration function. It modifies the configuration program ezmlm-make(1) so that it uses a text file template rather than compiled-in texts in list creation. In this manner, ezmlm-idx allows easy setup of lists in different languages and customization of default list setup. ezmlm-idx also adds MIME handling, and other support to streamline use with languages other than English. Prior to version 7, ezmlm-idx existed as an ezmlm source add-on, and as such did not work without ezmlm. ezmlm-idx tries to be compatible with ezmlm as much as possible in its usage, though the internal structure has changed considerably. ezmlm-idx also modifies the ezmlm subscriber database to be case insensitive to avoid many unsubscribe problems.

This document is a FAQ for ezmlm-idx. However, many of the basic items that are discussed also apply to ezmlm per se.

Install EZMLM-IDX without installing EZMLM 0.53

Install mysql-devel

Download latest version of EZMLM-IDX (ezmlm-idx-7.1.1) from http://www.inter7.com/?page=software . Extract the tarball in a separate directory under /downloads.

Edit conf-cc and conf-ld:

# cat conf-cc
gcc -O -g -I/usr/include/mysql -I/usr/include/pgsql

# cat conf-ld
cc -g -B /usr/lib64/mysql/


make clean; make; make man; make mysql 

make install

EZMLM will get installed under /usr/local/bin/ezmlm/ .


ln  -s  /downloads/ezmlm-idx-7.1.1/lang/en_US   /downloads/ezmlm-idx-7.1.1/lang/default



mysql> create database ezmlm;
mysql> grant all on ezmlm.* to ezmlmdbuser@localhost identified by 'redhat';
mysql> flush privileges;

./ezmlm-test -s mysql -d ezmlm -u ezmlmdbuser -p redhat -h localhost


Latest version 2.0.5 is at http://www.inter7.com/index.php?page=development , released in 2003. Download and extract it in /downloads.

# make
gcc -O2 -Wall -g  autorespond.c -o autorespond

# make install
install -d /usr/bin /usr/share/man/man1
install autorespond /usr/bin
install autorespond.1 /usr/share/man/man1

Mail Drop

maildrop is the mail filter/mail delivery agent that's used by the Courier Mail Server. You do not need to download maildrop if you already have the Courier mail server installed. The standalone build of the maildrop mail filter can be used with other mail servers.

maildrop is a replacement for your local mail delivery agent. maildrop reads a mail message from standard input, then delivers the message to your mailbox. maildrop knows how to deliver mail to mbox-style mailboxes, and maildirs. "maildir" is a mailbox format used by the Courier mail server and Qmail.

maildrop optionally reads instructions from a file, which describe how to filter incoming mail. These instructions can direct maildrop to deliver the message to an alternate mailbox, or forward it somewhere else. Unlike procmail, maildrop uses a structured filtering language.

maildrop is written in C++, and is significantly larger than procmail. However, it uses resources much more efficiently. Unlike procmail, maildrop will not read a 10 megabyte mail message into memory. Large messages are saved in a temporary file, and are filtered from the temporary file. If the standard input to maildrop is a file, and not a pipe, a temporary file will not be necessary.

maildrop checks the mail delivery instruction syntax from the filter file, before attempting to deliver a message. Unlike procmail, if the filter file contains syntax errors, maildrop terminates without delivering the message. The user can fix the typo without causing any mail to be lost.

Latest version is 2.6 , released in Sep 2012, available from here: http://www.courier-mta.org/download.php#maildrop

# yum install pcre-devel gcc-c++
groupadd  vchkpw
useradd  -g vchkpw -d /home/vpopmail -s /sbin/nologin  vpopmail
./configure --with-devel --enable-userdb --enable-maildirquota --enable-syslog=1 \
  --enable-trusted-users='root mail daemon postmaster qmaild mmdf' \
  --enable-restrict-trusted=0 --enable-maildrop-uid=root --enable-maildrop-gid=vchkpw

make install-strip
make install-man

Mail drop gets installed in /usr/local/bin/ :

# ls /usr/local/bin/maildrop -l
-rwxr-xr-x 1 root vchkpw 186624 Apr  4 12:58 /usr/local/bin/maildrop

VPOPMail with MySQL Support

Latest version is 5.4.33 , released in Feb 2011, available from Inter7 website http://www.inter7.com/index.php?page=vpopmail . Download a d extract under /downloads.

Download from: http://sourceforge.net/projects/vpopmail/files/vpopmail-stable/5.4.33/

mkdir ~vpopmail/etc
chown vpopmail:vchkpw ~vpopmail/etc
echo "localhost|0|vpopmaildbuser|redhat|vpopmail" > ~vpopmail/etc/vpopmail.mysql
mysql -u root
mysql> create database vpopmail;
mysql> grant all on vpopmail.* to vpopmaildbuser@localhost identified by 'redhat';
mysql> flush privileges;
./configure --enable-logging=p --enable-auth-module=mysql --disable-clear-passwd \
  --disable-many-domains --enable-sql-logging --enable-mysql-replication --enable-valias \
  --enable-roaming-users --enable-spamassassin --enable-mysql-limits --enable-libdir=/usr/lib64/mysql/

Need to work on "onchange-script" later. Don't use it at the moment.


WARNING : DO NOT USE the --enable-maildrop switch in vpopmail otherwise you will not get any mails and will get "Unable to open mailbox" errors in the maillog.

           vpopmail 5.4.33           
            Current settings

vpopmail directory = /home/vpopmail
 domains directory = /home/vpopmail/domains
               uid = 710
               gid = 702
     roaming users = ON  --enable-roaming-users
    tcpserver file = /etc/tcp.smtp
    open_smtp file = /home/vpopmail/etc/open-smtp
rebuild tcpserver file = ON  --enable-rebuild-tcpserver-file (default)
 password learning = OFF --disable-learn-passwords (default)
     md5 passwords = ON  --enable-md5-passwords (default)
      file locking = ON  --enable-file-locking (default)
vdelivermail fsync = OFF --disable-file-sync (default)
     make seekable = ON  --enable-make-seekable (default)
      clear passwd = OFF --disable-clear-passwd
 user dir hashing  = ON  --enable-users-big-dir (default)
address extensions = OFF --disable-qmail-ext (default)
          ip alias = OFF --disable-ip-alias-domains (default)
   onchange script = ON  --enable-onchange-script
     domain quotas = OFF --disable-domainquotas (default)
       auth module = mysql --enable-auth-module=mysql
 mysql replication = ON  --enable-mysql-replication
       sql logging = ON  --enable-sql-logging
      mysql limits = ON  --enable-mysql-limits
  SQL valias table = ON  --enable-valias
          auth inc = -I/usr/include/mysql
          auth lib = -Xlinker -R -Xlinker /usr/lib64/mysql/ -L/usr/lib64/mysql/  -lmysqlclient -lz -lm
  system passwords = OFF --disable-passwd (default)
        pop syslog = show failed attempts with clear text password --enable-logging=p
      auth logging = ON  --enable-auth-logging (default)
one domain per SQL table     = --disable-many-domains
      spamassassin = ON  --enable-spamassassin
          maildrop = OFF --disable-maildrop (default)

*** You have MySQL limits enabled.  If you are upgrading, please see doc/UPGRADE for information on database schema changes ****
make && make install-strip


Need to check the following:


     If you plan to use the vpopmail usage daemon
     run the following commands:

   cd vusaged && ./configure && make

     Then follow the INSTALL file to install


Based on discussion about vchkpw at http://qmail.jms1.net/upgrade-qmr.shtml , I will set the vchkpw program as setuid.

# ls -l /home/vpopmail/bin/vchkpw 
-rwx--x--x 1 vpopmail vchkpw 133608 Apr  4 13:17 /home/vpopmail/bin/vchkpw

# chmod u+s /home/vpopmail/bin/vchkpw 

# ls -l /home/vpopmail/bin/vchkpw 
-rws--x--x 1 vpopmail vchkpw 133608 Apr  4 13:17 /home/vpopmail/bin/vchkpw

Important from Qmailadmin INSTALL file:

Please note that any time you reconfigure and install vpopmail
you will need to rebuild and install QmailAdmin.  QmailAdmin
statically links libvpopmail, so you need to recompile it
whenever libvpopmail changes.

This means that if you are recompiling vpopmail for any reason, you should also recompile qmailadmin.


Latest version in 2.3.7 , released in Nov 2005 , available at http://www.inter7.com/index.php?page=vqadmin . Download and extract it under /downloads.

mkdir /var/www/vqadmin
chown apache:apache /var/www/vqadmin -R
./configure --enable-cgibindir=/var/www/cgi-bin --enable-htmldir=/var/www/vqadmin

If you see this error:

checking build system type... Invalid configuration `x86_64-unknown-linuxoldld': machine `x86_64-unknown' not recognized
configure: error: /bin/sh ./config.sub x86_64-unknown-linuxoldld failed

, then add the following in config.sub in the base_machine section

case $basic_machine in
        # We use `pc' rather than `unknown'
        # because (1) that's what they normally are, and
        # (2) the word "unknown" tends to confuse beginning users.
        i*86 | x86_64)

, and also add the following in vax section of config.sub:

 x86_64-* | 

You can compare the config.sub from the vpopmail source code to figure this out.


Note: Instead of doing the manual editing above, you can also run "libtoolize --force" in vqadmin's source-root directory, as mentioned at http://wbitt.com/knowledge/how-to-tutorials/127-vqadmin-for-qmail.html

Successful configuration will result in the following:

            Current settings
vpopmail directory = /home/vpopmail
               uid = 710
               gid = 702
       cgi-bin dir = /var/www/cgi-bin
       vqadmin dir = /var/www/cgi-bin/vqadmin

Now do:

make && make install-strip
chown              vpopmail /var/www/cgi-bin/vqadmin
chgrp              vchkpw /var/www/cgi-bin/vqadmin
chown -R           vpopmail /var/www/cgi-bin/vqadmin/*
chgrp -R           vchkpw /var/www/cgi-bin/vqadmin/*
chown root         /var/www/cgi-bin/vqadmin/vqadmin.cgi
chgrp `id -g root` /var/www/cgi-bin/vqadmin/vqadmin.cgi
chmod u+s,g+s      /var/www/cgi-bin/vqadmin/vqadmin.cgi 
chmod 755          /var/www/cgi-bin/vqadmin
chmod 755          /var/www/cgi-bin/vqadmin/html
chown              vpopmail /var/www/html/images/vqadmin
chgrp              vchkpw /var/www/html/images/vqadmin
chown -R           vpopmail /var/www/html/images/vqadmin/*
chgrp -R           vchkpw /var/www/html/images/vqadmin/*
chmod 755          /var/www/html/images/vqadmin
if test -f /var/www/cgi-bin/vqadmin/.htaccess ; then \
		echo ".htaccess file already in place" ; \
	else \
		cp htaccess        /var/www/cgi-bin/vqadmin/.htaccess ; \
chown nobody       /var/www/cgi-bin/vqadmin/.htaccess
chmod u+rw         /var/www/cgi-bin/vqadmin/.htaccess
chmod go+r         /var/www/cgi-bin/vqadmin/.htaccess
make[3]: Leaving directory `/downloads/vqadmin-2.3.7'
make[2]: Leaving directory `/downloads/vqadmin-2.3.7'
make[1]: Leaving directory `/downloads/vqadmin-2.3.7

Setup a .conf file in /etc/httpd/conf.d/ as vqadmin.conf

cat >> /etc/httpd/conf.d/vqadmin.conf << EOF

Alias /vqadmin /var/www/vqadmin

<Directory /var/www/vqadmin>
  Order deny,allow
  Allow from all

<Directory "/var/www/cgi-bin/vqadmin">
  Allow from all
  Options ExecCGI
  AllowOverride AuthConfig
  Order deny,allow

The make process will place a .htaccess file in /var/www/cgi-bin/vqadmin directory. Secure the config file, as well as create a password file. Also remove / comment the "satisfy any" line from it.

chown apache:root /var/www/cgi-bin/vqadmin/.htaccess 
chmod 0640 /var/www/cgi-bin/vqadmin/.htaccess
htpasswd  -bc  /etc/httpd/conf/vqadmin.passwd  admin  redhat

chown apache:root /etc/httpd/conf/vqadmin.passwd
chmod 0640 /etc/httpd/conf/vqadmin.passwd

Note: Try keeping the username as admin, otherwise you will get problems.

Restart Apache web server

service httpd restart

Try to load vqadmin page using the URL: http://mail.example.com/cgi-bin/vqadmin/vqadmin.cgi .

If you see a page saying "invalid language file", then make a couple of copies of your language file as shown:

cd  /var/www/cgi-bin/vqadmin/html
cp  en-us  en-US
cp  en-us  en_US

Reload the page, and you should be able to see the distinctive blue screen of vqadmin.

If you see a white page with Vqadmin menu on it. Check your apache error log.

Apache error log:-

[Thu May 08 16:51:18 2008] [error] [client] File does not exist:

The solution is :-

ln -s /var/www/html/images /var/www/vhosts/example.com/httpdocs/

chown apache:apache /var/www/html -R

When vqadmin works, try adding a domain through it's menu system.

You should be able to successfully add/create a domain, say "example.com" through vqadmin.


QmailAdmin 1.2.15 has some problem during make. So I used qmailadmin-1.2.16, which is the development version, and it compiles properly. Download and extract under /downloads.

mkdir /var/www/qmailadmin/images -p
chown apache:apache /var/www/qmailadmin -R

cat >> /etc/httpd/conf.d/qmailadmin.conf << EOF

Alias /qmailadmin /var/www/qmailadmin

<Directory /var/www/qmailadmin>
  Order deny,allow
  Allow from all
./configure --enable-cgibindir=/var/www/cgi-bin --enable-htmldir=/var/www/qmailadmin \
 --enable-imagedir=/var/www/qmailadmin/images --enable-imageurl=/qmailadmin/images \


            qmailadmin 1.2.16
            Current settings
       cgi-bin dir = /var/www/cgi-bin
          html dir = /var/www/qmailadmin
         image dir = /var/www/qmailadmin/images
         image URL = /qmailadmin/images
      template dir = /usr/local/share/qmailadmin
         qmail dir = /var/qmail
      vpopmail dir = /home/vpopmail
   autorespond dir = /usr/bin
         ezmlm dir = /usr/local/bin/ezmlm
         ezmlm idx = yes
   mysql for ezmlm = yes
              help = no
      modify quota = yes
   domain autofill = no
 catchall accounts = yes
 trivial passwords = yes
 modify spam check = no
make install-strip

service httpd restart

You should be able to load the qmailadmin page using http://mail.example.com/cgi-bin/qmailadmin .

You should also be able to successfully create a new email address, such as kamran@example.com .

Important from qmailadmin INSTALL file:

Please note that any time you reconfigure and install vpopmail
you will need to rebuild and install QmailAdmin.  QmailAdmin
statically links libvpopmail, so you need to recompile it
whenever libvpopmail changes.

This means, if you make any changes to vpopmail binaries (recompile, etc), then you must recompile qmailadmin after that.

Final Checks

Make sure that the final files exist, and have the proper content:

  • /var/qmail/supervise/qmail-send/run
  • /var/qmail/supervise/qmail-smtpd/run
  • /var/qmail/supervise/qmail-pop3d/run # only if you are using qmail's pop3d
  • /var/qmail/supervise/qmail-send/log/run
  • /var/qmail/supervise/qmail-smtpd/log/run
  • /var/qmail/supervise/qmail-pop3d/log/run # only if you are using qmail's pop3d
  • /var/qmail/rc
  • /var/qmail/bin/qmailctl

Setup needed permissions:

chmod 755 /var/qmail/rc  
chmod 755 /var/qmail/bin/qmailctl
chmod 751 /var/qmail/supervise/qmail-pop3d/run
chmod 751 /var/qmail/supervise/qmail-pop3d/log/run
chmod 751 /var/qmail/supervise/qmail-smtpd/run
chmod 751 /var/qmail/supervise/qmail-smtpd/log/run
chmod 751 /var/qmail/supervise/qmail-send/run
chmod 751 /var/qmail/supervise/qmail-send/log/run

Setup default values to various control files. You may want to check the files contents first, before executing the code below:

echo ./Maildir > /var/qmail/control/defaultdelivery
echo 30 > /var/qmail/control/concurrencyincoming   # The default value is 10
chmod 644 /var/qmail/control/concurrencyincoming
echo 255 > /var/qmail/control/concurrencyremote    # The default value is 20
chmod 644 /var/qmail/control/concurrencyremote

Create symbolic links. Ideally these links should already be in-place by this time:

ln -s /var/qmail/bin/qmailctl /usr/bin
ln -s /var/qmail/supervise/qmail-send /var/qmail/supervise/qmail-smtpd /var/qmail/supervise/qmail-pop3d /service

The /etc/tcp.smtp file should contain the following at the moment:

echo '127.:allow,RELAYCLIENT=""'

You will need to compile the cdb file, each time you make any changes in the /etc/tcp.smtp .

qmailctl stop
qmailctl cdb
qmailctl start

Also make sure that the necessary system aliases exist. These aliases are going to tell Qmail what to do with common server-generated mails.

echo postmaster@example.com  > /var/qmail/alias/.qmail-root
echo postmaster@example.com  > /var/qmail/alias/.qmail-postmaster
echo postmaster@example.com  > /var/qmail/alias/.qmail-mailer-daemon
ln  -s  /var/qmail/alias/.qmail-root  /var/qmail/alias/.qmail-anonymous
chmod 644 /var/qmail/alias/.qmail*

Remove SENDMAIL, POSTFIX , EXIM from System:

If you have not removed these packages before, you can do it now.

service sendmail stop
service postfix stop
service exim stop
service dovecot stop
rpm -e sendmail --nodeps
rpm -e sendmail-cf --nodeps
rpm -e postfix --nodeps
rpm -e exim --nodeps

Create artificial sendmail path:

rm -f /usr/lib/sendmail
rm -f /usr/sbin/sendmail

ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail

Create Qmail SSL certificate

Reference: http://qmail.jms1.net/smtp-service.shtml

cd /var/qmail/control
openssl req -newkey rsa:1024 -x509 -nodes -days 3650 -out servercert.pem -keyout servercert.pem

Note: "Common Name" MUST MATCH the name your clients will put into their mail program as their SMTP server name.

chown root:nofiles servercert.pem 
chmod 640 servercert.pem

The "nofiles" group is the group which "qmaild" belongs to. This combination of ownership and permissions allows qmail-smtpd to read the key, but not change or delete it.

Once you have verified that the certificate is valid, you can copy the "servercert.pem" file to "clientcert.pem", and qmail-remote will be able to use it to encrypt the connection when sending outbound messages to remote SMTP servers which advertise the ability to support TLS.

cp servercert.pem clientcert.pem
chown root:qmail clientcert.pem    
# The "qmail" group is the group with the "qmailr" user belongs to. This user should be able to read, but not write, the "clientcert.pem" file.
chmod 640 clientcert.pem 

Refer to the the "Permissions on SSL .pem files" section at http://qmail.jms1.net/upgrade-qmr.shtml for more information on Qmail certificate permissions.

Courier IMAP

In May 2008 the developer of courier-imap decided to drop support for vpopmail. This support is not there in courier-authlib-0.60.4 and newer.Similarly courier-imap-4.5.0 does not work, if it doesn't find at least courier-authlib-0.60.6 .

Dovecot as POP3 and IMAP server

Since I cannot use courier, I have to use dovecot. I am using the following as reference, on how to use dovecot with qmail.

Download the latest stable version of Dovecot is 2.1.15 , available from http://www.dovecot.org/download.html. You need to compile the source code because the rpm package, which comes with redhat and family is not compiled with vpopmail option.

./configure \
	--without-shadow \
	--without-pam \
	--without-bsdauth \
	--without-gssapi \
	--without-sia \
	--without-ldap \
	--with-vpopmail \
	--without-sql \
	--without-pgsql \
	--without-mysql \
	--without-sqlite \


config.status: executing libtool commands

Install prefix . : /usr/local
File offsets ... : 64bit
I/O polling .... : epoll
I/O notifys .... : inotify
SSL ............ : yes (OpenSSL)
GSSAPI ......... : no
passdbs ........ : static passwd passwd-file checkpassword vpopmail
                 : -shadow -pam -bsdauth -sia -ldap -sql
userdbs ........ : static prefetch passwd passwd-file checkpassword vpopmail nss
                 : -ldap -sql
SQL drivers .... :
                 : -pgsql -mysql -sqlite
Full text search : squat
                 : -lucene -solr

Make sure that the configure script detects SSL and the output contains "vpopmail" in passdbs and userdbs modules/lines.

make install
mkdir -m 0755 /usr/local/var \
        /usr/local/var/run \

You will need to create a user to run the dovecot services.

groupadd dovecot
useradd -g dovecot -M  -s /sbin/nologin -c 'Dovecot user' dovecot
useradd -g dovecot -M  -s /sbin/nologin -c 'Dovecot Null user' dovenull

# -d /nohome ???

Dovecot configuration


You can use the config file provided by JMS. Be mindful, that there are few items which are changed in the config file. You will need to adjust that. Use "dovecot -n" to generate a clean version of current configuration. Please note that dovecot does not use a single config file any more. You have to make use of (and adjust) the files in the conf.d directory too.

cp -rp /usr/local/share/doc/dovecot/example-config/*   /usr/local/etc/dovecot/ 

If you get error messages in /var/log/maillog , about the size mismatch of emails, or something about the "S" size of the email file, the you will need to set an extra variable in 10-mail.conf .


maildir_broken_filename_sizes = yes

Dovecot SSL certificate

cd /downloads/dovecot-2.1.15/doc/
cp dovecot-openssl.cnf dovecot-openssl.cnf.orig
vi dovecot-openssl.cnf
mkdir /etc/ssl/private

# sh mkcert.sh 
Generating a 1024 bit RSA private key
writing new private key to '/etc/ssl/private/dovecot.pem'

subject= /C=NO/L=Okern/O=FineDistribution/OU=IMAP server/CN=mail.example.com/emailAddress=postmaster@example.com
SHA1 Fingerprint=B2:D8:64:85:5D:BA:28:0D:93:60:FE:71:63:52:26:2D:A1:6D:03:6E

The files /etc/ssl/private/dovecot.pem and /etc/ssl/certs/dovecot.pem must have been created.

Copy dovecot init script to /etc/init.d/ and adjust if necessary.

cp   /downloads/dovecot-2.1.15/doc/dovecot-initd.sh   /etc/init.d/dovecot
chmod +x /etc/init.d/dovecot

Start the dovecot service and verify that dovecot process is listening on the ports.

chkconfig --level dovecot on
service dovecot restart

# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0       *                   LISTEN      8791/dovecot        
tcp        0      0       *                   LISTEN      8791/dovecot        
tcp        0      0*                   LISTEN      1122/named          
tcp        0      0      *                   LISTEN      1122/named          
tcp        0      0        *                   LISTEN      1159/sshd           
tcp        0      0*                   LISTEN      2005/tcpserver      
tcp        0      0       *                   LISTEN      8791/dovecot        
tcp        0      0       *                   LISTEN      8791/dovecot        
tcp        0      0      *                   LISTEN      1297/mysqld         
tcp        0      0         ESTABLISHED 1378/sshd           
tcp        0      0 :::80                       :::*                        LISTEN      1320/httpd          
tcp        0      0 ::1:53                      :::*                        LISTEN      1122/named          
tcp        0      0 :::22                       :::*                        LISTEN      1159/sshd           
tcp        0      0 :::443                      :::*                        LISTEN      1320/httpd          


Download SquirrelMail from http://squirrelmail.org , and extract it in /downloads.

mkdir   /var/www/squirrelmail
rsync -av   /downloads/squirrelmail-webmail-1.4.22/   /var/www/squirrelmail/
chown apache:apache /var/www/squirrelmail -R
cd /var/www/squirrelmail/config/

Create the apache configuration file for squirrelmail and restart apache web service.

cat > /etc/httpd/conf.d/squirrelmail.conf << EOF
Alias /squirrelmail /var/www/squirrelmail

service httpd restart

Run the squirrelmail configuration script inside the config directory.


Set the correct values for the following options:

Organization Preferences
1. Organization Name : Example website
4. Organization Title : Example webmail service, powered by SquirrelMail $version

Server Settings

1. Domain : example.com
2. Invert Time : false
3. Sendmail or SMTP : SMTP
(Remember, we are using Dovecot in this setup. Still the following IMAP settings should be ok with most of IMAP software/servers)

IMAP Settings
4. IMAP Server : localhost
5. IMAP Port : 143
6. Authentication type : login
7. Secure IMAP (TLS) : false
8. Server software : other
9. Delimiter : detect

SMTP Settings
4. SMTP Server : localhost
5. SMTP Port : 25
6. POP before SMTP : false
7. SMTP Authentication : none
8. Secure SMTP (TLS) : false
9. Header encryption key :

General Options
1. Data Directory : /var/www/squirrelmail/data/
2. Attachment Directory : /var/www/squirrelmail/attach/

Make sure to create data and attach directories. Make sure that they are readable by the web server process. Ideally these directories should be outside your webroot directory.

mkdir -m 733 /var/www/squirrelmail/{attach,data} -p
chown apache:apache /var/www/squirrelmail/{attach,data}

RoundCube WebMail

Download roundcube from http://roundcube.net , and extract it in /var/www/roundcube .

chown apache:apache /var/www/roundcube -R

Create apache config file for roundcube:

cat > /etc/httpd/conf.d/roundcube.conf << EOF
Alias /roundcube /var/www/roundcube

# service httpd restart

Create a mysql database for roundcube.

mysql -u root

mysql> create database roundcubemail;
mysql> grant all on roundcubemail.* to roundcubeuser@localhost identified by 'redhat';
mysql> flush privileges;

Execute the installer script located at http://mail.example.com/roundcube/installer/

When saving the main.inc.php file, make sure to set the auth type to PLAIN.

$rcmail_config['imap_auth_type'] = plain;

ClamAV Antivirus

Install EPEL repository using : http://fedoraproject.org/wiki/EPEL/FAQ#howtouse

rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

Install CLAMAV from EPEL repository:

yum install clamd

Freshen up virus database:


Create user qscand:

groupadd qscand
useradd  -g qscand -c "Qmail-Scanner Account" -s /bin/false qscand

Question: Why do we need to run clamav as qscand? Why create a separate account? If qmail-scanner needs it's helper programs to run as qscand, then why spamassassin is being run as spamd? Answe: ClamAV needs access to the MIME parts of the mails, separated by Qmail-Scanner. For this, it needs to run as the qmail-scanner user qscand. In newer versions of qmail-scanner, it is supported that ClamAV runs as it's independant user clamav, and it just needs to be a member of the qmail scanner group qscand. See http://qmail.jms1.net/clamav/qmail-scanner.shtml

Edit configuration files.


LogFile /var/log/clamav/clamd.log
LogFileMaxSize 0
LogTime yes
LogSyslog yes
PidFile /var/run/clamav/clamd.pid
TemporaryDirectory /var/tmp
DatabaseDirectory /var/lib/clamav
LocalSocket /var/run/clamav/clamd.sock
LocalSocketGroup qscand
LocalSocketMode 660
FixStaleSocket yes
TCPSocket 3310
MaxConnectionQueueLength 30
MaxThreads 50
ReadTimeout 300
User qscand
AllowSupplementaryGroups yes
ScanPE yes
ScanELF yes
DetectBrokenExecutables yes
ScanOLE2 yes
ScanMail yes
ScanArchive yes
ArchiveBlockEncrypted no


DatabaseDirectory /var/lib/clamav
UpdateLogFile /var/log/clamav/freshclam.log
LogSyslog yes
DatabaseOwner qscand
AllowSupplementaryGroups yes
DatabaseMirror db.sa.clamav.net
DatabaseMirror db.local.clamav.net
NotifyClamd /etc/clamd.conf

Create necessary directories and set correct ownerships.

chown qscand:qscand /var/run/clamav -R
chown qscand:qscand /var/lib/clamav -R
chown qscand:qscand /var/log/clamav -R
service clamd restart
chkconfig --level 35 clamd on

Test clamav:

cd /usr/share/doc/clamav-0.97.7/test

# clamscan -r -l /tmp/scan.txt
----------- SCAN SUMMARY -----------
Known viruses: 2007060
Engine version: 0.97.7
Scanned directories: 2
Scanned files: 142
Infected files: 46
Data scanned: 19.51 MB
Data read: 12.40 MB (ratio 1.57:1)
Time: 5.243 sec (0 m 5 s)

Check the file /tmp/scan.txt. It should contain the name of files which are infected with test virus signatures.

You don't need to run freshclam as a daemon, as it will consume memory. Better run it as cronjob.

# crontab -e
* * */2 * * /usr/bin/freshclam --quiet

Make sure to edit the /etc/logrotate.d/freshclam and /etc/logrotate.d/clamav files, and update the user (change to qscand).

SpamAssassin AntiSpam

SpamAssassin is installed through the available RPM in redhat/centos base repo.

Add a user:

useradd -s /bin/false spamd

Edit the configuration files:


SPAMDOPTIONS="-x -u spamd -H /home/spamd -d"

spamd Options explained

-c, --create-prefs Create user preferences files (we don't need it)
-x, --nouser-config Disable user config files
-d, --daemonize Daemonize
-m num, --max-children=num Allow maximum num children
-u username, --username=username Run as username
-g groupname, --groupname=groupname Run as groupname (should we use this too? ) ???
-v, --vpopmail Enable vpopmail config (we "should" need it, but not using at the moment) ???
-x, --nouser-config Disable user config files 
-H [dir], --helper-home-dir[=dir] Specify a different HOME directory


required_hits 5
report_safe 0
rewrite_header Subject [SPAM]
service spamassassin restart
chkconfig --level 35 spamassassin on

A lot of modules can be enabled , which I will enable later.


  • SpamAssassin modules
  • Razor
  • DCC


Download latest version of QmailScanner from http://qmail-scanner.sourceforge.net/ .

Create a pre-install config file:

# vi qms-config

## File: qms-config
## Purpose: Provide a file to save personal qmail-scanner configuration
## options. This file should be edited for your server and
## saved somewhere so that it survives qmail-scanner and
## qms-analog upgrades.

# Was the "install" option given?
if [ "$1" != "install" ]; then

# Qmail Scanner 2.11 configuration:

./configure --domain example.com \
--admin postmaster \
--local-domains "example.com,example.net" \
--add-dscr-hdrs yes \
--ignore-eol-check yes \
--notify admin \
--redundant yes \
chmod +x qms-config

Now run the configuration script.


If all ends well, you will need to run the script with install option.

# ./qms-config install


Finished installation of initial directory structure for Qmail-Scanner
under /var/spool/qscan and qmail-scanner-queue.pl under /var/qmail/bin.

Finished. Please read README(.html) and then go over the script
(/var/qmail/bin/qmail-scanner-queue.pl) to check paths/etc. 

"/var/qmail/bin/qmail-scanner-queue.pl -r" should return some well-known virus
definitions to show that the internal perlscanner component is working.

That's it!

Note: QMS will get installed in /var/spool/qscan. NOT /var/spool/qmailscan .

Run a couple of self tests:

$ /var/qmail/bin/qmail-scanner-queue.pl -g
perlscanner: generate new DB file from /var/spool/qscan/quarantine-events.txt
perlscanner: total of 12 entries.

You should not see any permission error in the output above.

Note: Any SMTP sessions that are dropped (due to network outages/etc) may lead to files lying around in /var/spool/qscan . Running "/var/qmail/bin/qmail-scanner-queue.pl -z", at least once daily, will ensure such files are deleted when they're over 30 hours old.

Now run the following:

setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -z # also setup a cronjob to do this once a day.
setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -g # also setup a cronjob to do this once a day.

Set them up to run through cron as well:-

# crontab -e
0 1 * * * /usr/local/bin/setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -z
0 1 * * * /usr/local/bin/setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -g

Stop qmail at this point.

qmailctl stop

In the qmail SMTPD run file (from JMS), uncomment the line which deals with Qmail scanner.

# vi /var/qmail/supervise/qmail-smtpd/run

Now start qmail and check status.

qmailctl start
sleep 5
qmailctl stat

You should try to run a test script in the qmail-scanner contrib directory. It is the famous "do it" script!

cd /downloads/qmail-scanner-2.11/contrib
# ./test_installation.sh -doit

Sending standard test message - no viruses...

Sending eicar test virus - should be caught by perlscanner module...

Sending eicar test virus with altered filename - should only be caught by commercial anti-virus modules (if you have any)...

Sending bad spam message for anti-spam testing - In case you are using SpamAssassin...

Finished test. Now go and check Email sent to postmaster@example.com

You should have two emails in the postmaster's inbox. One in-offensive and the other marked with SPAM.

From     	        Date      	  	Subject 
sb55sb55@yahoo.com 	1:54 pm 	  	[SPAM] Qmail-Scanner anti-spam test (4/4): checkin...
Qmail-Scanner Test 	1:54 pm 	  	Qmail-Scanner test (1/4): inoffensive message

In this case, two out of four mails (second and third) were (and should be) successfully eliminated.

You should have a log similar to the following in the /var/log/maillog when you run the test_installation.sh script.

Apr  2 14:54:44 mail qmail-scanner[7987]: Clear:RC:1( 0.005206 323 postmaster@example.com postmaster@example.com Qmail-Scanner_test_(1/4):_inoffensive_message <20130402115444.7986.qmail@mail.example.com> orig-mail.example.com13649036845887987:323 1364903684.7989-0.mail.example.com:68
Apr  2 14:54:45 mail spamd[3139]: spamd: connection from localhost [] at port 47094
Apr  2 14:54:45 mail spamd[3139]: spamd: processing message <20130402115444.7986.qmail@mail.example.com> for postmaster@example.com:712
Apr  2 14:54:45 mail qmail-scanner[7996]: Perlscan:EICAR_Test_Virus:RC:1( 0.450453 971 postmaster@example.com postmaster@example.com Qmail-Scanner_viral_test_(2/4):_checking_perlscanner... <20130402115444.7995.qmail@mail.example.com> 1364903684.7998-0.mail.example.com:300 Eicar.com:69 orig-mail.example.com13649036845887996:971
Apr  2 14:54:45 mail qmail-scanner[8009]: MHR:MHR-82:RC:1( 0.031313 1246 postmaster@example.com postmaster@example.com Qmail-Scanner_viral_test_(3/4):_checking_non-perlscanner_AV... <20130402115445.8008.qmail@mail.example.com> sneaky.txt
Apr  2 14:54:45 mail spamd[3140]: spamd: connection from localhost [] at port 47095
Apr  2 14:54:45 mail spamd[3140]: spamd: checking message <9PS291LhupY> for postmaster@example.com:712
Apr  2 14:54:53 mail spamd[3140]: spamd: identified spam (1005.5/5.0) for postmaster@example.com:712 in 8.0 seconds, 1986 bytes.
Apr  2 14:54:53 mail spamd[3140]: spamd: result: Y 1005 - DATE_IN_FUTURE_Q_PLUS,EXCUSE_4,FORGED_YAHOO_RCVD,FREEMAIL_FROM,GTUBE,INVALID_MSGID,MSGID_SHORT,NORMAL_HTTP_TO_IP,T_TO_NO_BRKTS_FREEMAIL scantime=8.0,size=1986,user=postmaster@example.com,uid=712,required_score=5.0,rhost=localhost,raddr=,rport=47095,mid=<9PS291LhupY>,autolearn=no
Apr  2 14:54:53 mail spamd[3137]: prefork: child states: BI
Apr  2 14:54:53 mail qmail-scanner[8015]: Clear:RC:1( 8.058205 1881 postmaster@example.com postmaster@example.com Qmail-Scanner_anti-spam_test_(4/4):_checking_SpamAssassin_[if_present]_(There_yo <9PS291LhupY> orig-mail.example.com13649036855888015:1881 1364903685.8017-0.mail.example.com:818
Apr  2 14:54:53 mail spamd[3140]: spamd: connection from localhost [] at port 47096
Apr  2 14:54:53 mail spamd[3140]: spamd: processing message <9PS291LhupY> for postmaster@example.com:712
Apr  2 14:54:53 mail spamd[3140]: spamd: identified spam (1004.2/5.0) for postmaster@example.com:712 in 0.1 seconds, 2421 bytes.
Apr  2 14:54:53 mail spamd[3140]: spamd: result: Y 1004 - EXCUSE_4,FORGED_YAHOO_RCVD,FREEMAIL_FROM,GTUBE,INVALID_MSGID,MSGID_SHORT,NORMAL_HTTP_TO_IP,T_TO_NO_BRKTS_FREEMAIL scantime=0.1,size=2421,user=postmaster@example.com,uid=712,required_score=5.0,rhost=localhost,raddr=,rport=47096,mid=<9PS291LhupY>,autolearn=no
Apr  2 14:54:53 mail spamd[3137]: prefork: child states: BI
Apr  2 14:54:54 mail spamd[3139]: spamd: clean message (-0.0/5.0) for postmaster@example.com:712 in 9.3 seconds, 663 bytes.
Apr  2 14:54:54 mail spamd[3139]: spamd: result: . 0 - NO_RELAYS scantime=9.3,size=663,user=postmaster@example.com,uid=712,required_score=5.0,rhost=localhost,raddr=,rport=47094,mid=<20130402115444.7986.qmail@mail.example.com>,autolearn=ham
Apr  2 14:54:54 mail spamd[3137]: prefork: child states: II

Final adjustments

The /etc/tcp.smtp file needs to be updated, with the settings you want for your server and network. You may want to (will need to) change the file /etc/tcp.smtp , from a simple:


, to:

# No Qmail-Scanner at all for mail from
# Use Qmail-Scanner without SpamAssassin on any mail from the local network
# [it triggers SpamAssassin via the presence of the RELAYCLIENT var]
# 10.:allow,RELAYCLIENT="",RBLSMTPD="",QMAILQUEUE="/var/qmail/bin/qmail-scanner-queue.pl"
# Use Qmail-Scanner with SpamAssassin on any mail from the rest of the world

I have setup my /etc/tcp.smtp as follows:-


# my users loggin on to the web interface may be uploading virus infeced files.
# No Qmail-Scanner at all for mail from
## 127.:allow,RELAYCLIENT="",RBLSMTPD="",QMAILQUEUE="/var/qmail/bin/qmail-queue"
# Use Qmail-Scanner without SpamAssassin on any mail from the local network
# [it triggers SpamAssassin via the presence of the RELAYCLIENT var]
# 10.:allow,RELAYCLIENT="",RBLSMTPD="",QMAILQUEUE="/var/qmail/bin/qmail-scanner-queue.pl"
# Use Qmail-Scanner with SpamAssassin on any mail from the rest of the world

Restart Qmail

qmailctl stop ; qmailctl cdb ; qmailctl start


This is already included in qmail, you just need to enable it in the qmail-smtpd/run script.

Valid RCPT To

Valid RCPT To is an excellent tool in fighting against email floods and deny of service attacks. Normally a mail server receives / accepts mails from anyone, for the domains it is hosting. However it is often the case that it receives emails for email accounts which do not exist in the system even if the domain exists. Te email server then generate a bounce email and tries to send it to the sender, informing that the email server does not exist. This is a waste of system resources. Why receive emails for email accounts which do not exist on our server in the first place. Valid RCPT To patch protects against such abuse.

Valid RCPT To is part of the JMS combined patch. In order to enable it, you need a CDB file of all the email accounts on your system. This CDB file can be created using the mkvalidrcptto script from the JMS website.

cd /usr/local/bin
wget http://qmail.jms1.net/scripts/mkvalidrcptto
chmod 755 mkvalidrcptto

Make sure that your installation of perl is happy with the script and can find the modules. You can do this by running this command as a non-root user:

$ perl -c /usr/local/bin/mkvalidrcptto
/usr/local/bin/mkvalidrcptto syntax OK

You should then run it once as root and make sure the output makes sense for your system. The output should be a list of every valid email address on your system, one on each line.

# mkvalidrcptto


Generate the CDB file to be used with qmail smtpd:

mkvalidrcptto -c /var/qmail/control/validrcptto.cdb

Note: You must re-create the CDB file, and restart qmail to reload the new CDB, as soon as you create or remove a user (including mailing lists) from any of the domains managed on this system.

Ideally there should be some sort of "onchange" trigger, which could re-create the CDB file and restart qmail to load new CDB. vpopmail provides such a functionality (through the use of an external script), but is kind of useless, because qmailadmin - which we use most of the time to create user accounts, does not work well with this functionality.

I have created a script which runs every minute and sees if there are any changes in the CDB. If there are, it re-creates CDB files and restarts qmail. For me this is the most robust and affordable solution, without getting into any trouble of patching vpopmail or qmailadmin, and chasing developers on various mailing list. The script can be found further below, and is titled update-qmail.sh.

Enable the VALIDRCPTTO options in the qmail-smtpd/run file:


Restart Qmail:

qmailctl restart

Grey Listing using jgreylist [JMS]

Download jgreylist from http://qmail.jms1.net/scripts/jgreylist.shtml .

cd /downloads
wget http://qmail.jms1.net/scripts/jgreylist
wget http://qmail.jms1.net/scripts/jgreylist-clean
wget http://qmail.jms1.net/scripts/jgreylist.c

Create necessary directories:

cd /var/qmail
mkdir -m 0700 jgreylist
chown qmaild:nofiles jgreylist

Before configuring the jgreylist script, identify the "grey list user". The phrase "greylist user" means the user that your SMTP service runs as. This user will need to own the directory containing the greylist files, so that it will have permission to create files and directories as needed.

You will need to know the userid, as well as their primary login group (the group assigned to them in their /etc/passwd entry. Normally the user is qmaild, which is the member of the group nofiles.

The jgreylist script should be installed in a directory listed in the PATH of your SMTP service's "run" script, which is normally /var/qmail/bin. It should be owned by root, have the same group ID as the greylist user's group ID, and have permissions 0750.

The jgreylist-clean script can be installed anywhere on the system, such as /usr/local/sbin. It should be owned by root and have permissions 0755.

Install the jgreylist script by copying the files to proper locations:

cp /downloads/jgreylist /var/qmail/bin/ 
cp /downloads/jgreylist-clean /usr/local/sbin

chown root:nofiles /var/qmail/bin/jgreylist
chown root:root /usr/local/sbin/jgreylist-clean

chmod 0750 /var/qmail/bin/jgreylist
chmod 0755 /usr/local/sbin/jgreylist-clean

Enable the GREYLIST variable in the qmail-smtpd/run file. It needs to be after tcpserver and before qmail-smtpd in the command which actually runs the qmail-smtpd daemon.



Restart Qmail SMTPD service:

svc -t /service/qmail-smtpd
qmailctl restart

The jgreylist script also has a configuration section, which you may change as per your setup. Most of the time, the default values are good enough.

The jgreylist-clean script can be run as the greylist user, or as root; and can be run at any time, and once a day is good enough. It has also has a configuration section, which can be used to adjust the clean-up timing.

Setup a cronjob to run jgreylist-clean script every day.

# crontab -e
* * */1 * * /usr/local/sbin/jgreylist-clean

The final crontab

# crontab -l

# Run at 0 min (zero minute) of every hour.
0 * * * * /usr/bin/freshclam --quiet
# Run at 1 AM every day
0 1 * * * /usr/local/bin/setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -z
0 1 * * * /usr/local/bin/setuidgid qscand /var/qmail/bin/qmail-scanner-queue.pl -g
# Run the following every 1 minute. It updates CDB files and restarts qmail.
# The ">/dev/null 2>&1" prevents the script to generate any output when run through cron.
# If it is not used, the script causes cron to generate an email every minute and
# fills postmaster's Inbox.
*/1 * * * * /usr/local/bin/update-qmail.sh >/dev/null 2>&1
# Run at 2 AM everyday:
0 2 * * * /usr/local/sbin/jgreylist-clean


Discussion about SMTP auth and two available methods, as per the following links:  ????

Download the JMS mkauth script:

cd /usr/local/bin
wget http://qmail.jms1.net/scripts/mkauth
chmod 755 mkauth

Run the mkauth program to see if it can pull the users and password information:

# perl -c /usr/local/bin/mkauth
/usr/local/bin/mkauth syntax OK

# mkauth 
alias *
alias@mail.example.com *
dovecot !!
dovecot@mail.example.com !!
dovenull !!
dovenull@mail.example.com !!
kamran $6$67BILXM0$653j3t0ZqGviMOrq17j3ym4/8D8v6cRkCmEyy7MrwkiprS0jZYD9ff3jJ8xexLP23uAhkbvfCZFpcDc.UCq4UrNJ.
kamran@example.com $1$QkAyMD5Gfuv$csa3tunpQIaCkZaJzE4fK.
kamran@mail.example.com $6$67BIH5SLXM0$j3t0ZqGfviMOrq17j3ym4/8D8v6cRkCmEyy7MrwkiprS0jZYD9ff3jJ8xexLP23uAhkbvfCZFpcDc.UCq4UrNJ.
postmaster@example.com $1$AMP3vlrE$8CRAE2qVLb4k9LfN2oUgrKiu.
qmaild *
qmaild@mail.example.com *
qmaill *
qmaill@mail.example.com *
qmailp *
qmailp@mail.example.com *
qmailq *
qmailq@mail.example.com *
qmailr *
qmailr@mail.example.com *
qmails *
qmails@mail.example.com *
qscand !!
qscand@mail.example.com !!
spamd !!
spamd@mail.example.com !!
vpopmail !!
vpopmail@mail.example.com !!

Now create the auth.cdb file:

mkauth -c /tmp/new.cdb
chown root:nofiles /tmp/new.cdb
chmod 0640 /tmp/new.cdb
mv /tmp/new.cdb /var/qmail/control/auth.cdb

Note: If there are any active qmail-smtpd services using the auth.cdb file, you should not build the file directly - you should build it using a different name, then set the ownership and permissions correctly, and then rename the new file over the old one. The example above assumes that qmail-smtpd is running as the "qmaild" user (which is the standard way to do it). Still, you can directly create the auth.cdb if you wish so:

mkauth -c  /var/qmail/control/auth.cdb
chown root:nofiles /var/qmail/control/auth.cdb
chmod 0640 /var/qmail/control/auth.cdb

Note: You will need to recreate the CDB file each time you add,remove, or change password of an email account. Check the update-qmail.sh script further below in this document.

Enable the following options in the qmail-smtpd/run file:

# options pertaining to the AUTH command.


# if using the AUTH_CDB method

# if using the CHECKPW method
# CHECKPW=~vpopmail/bin/vchkpw
# TRUE=`which true`

Now restart qmail:

qmailctl restart

Now setup your mail client (Thunderbird, Outlook, etc) to send mail using SMTP-AUTH (LOGIN). Send a test mail, and check logs.

==> /var/log/qmail/qmail-smtpd/current <==
@40000000515fed62244466a4 tcpserver: status: 1/30
@40000000515fed62244be0b4 tcpserver: pid 4043 from 195.xxx.yyy.zzz
@40000000515fed622453cff4 tcpserver: ok 4043 mail.example.com.com: :
@40000000515fed652f8fcddc qmail-smtpd[4043]: AUTH successful [195.xxx.yyy.zzz] kamran@example.com
@40000000515fed66058eb8cc qmail-smtpd[4043]: MFCHECK pass [195.xxx.yyy.zzz] example.com
@40000000515fed66058fed64 qmail-smtpd[4043]: MAIL FROM:<kamran@example.com>
@40000000515fed6612e2fbdc qmail-smtpd[4043]: RCPT TO:<kamran@wbitt.com>
@40000000515fed682157b694 tcpserver: end 4043 status 0
@40000000515fed682157ba7c tcpserver: status: 0/30


The update-qmail.sh script must be set in cron to run every minute. It checks for changes in the CDB files by generating temporary ones and comparing them to the older ones. If it detects any difference, it re-creates new CDB files and restarts qmail to load the new values.


# Author: Muhammad Kamran Azeem
# Licence: GPL v2
# Created: 20130409
# Updated: 20130409
# Description: Checks the difference of auth.cdb and validrcptto.cdb with earlier versions of same files
#              If changes are found, the script restarts qmail service to load new values.
# Changelog: At the end of the script.

######## Start - User configurable Section
# Qmail directory. Adjust of your qmail lives somewhere else.

# Path to qmailctl program

# Path to ValidRCPTTo CDB file

# Path to Auth CDB file

# Path to mkauth program

# Path to mkvalidrcptto program

######## End - User configurable Section

# You don't need to modify anything in the section below.

######## Start - Actual program code

# mkauth -c /tmp/auth.cdb.new 
# mkvalidrcptto -c /tmp/validrcptto.cdb.new
# SUM1=$(md5sum auth.cdb | cut -f 1 -d ' ')
# SUM2=$(md5sum auth.cdb.new | cut -f 1 -d ' ')
# if [ "$SUM1" == "$SUM2" ] ; then echo "Equal" ; else echo "Different"; fi

# Check if the files exist
if [ -d $VQ ] && [ -x $QMAILCTL ] && [ -r $VRCPTFILE ] && [ -r $AUTHFILE ] && [ -x $MKAUTH ] && [ -x $MKVRCPT ] ; then 
  echo "Files specified in configuration section exist. Good! Proceeding to run the script" 
  echo "One of the files or programs is missing. Please check and re-run the script."
  exit 9

# Generate a new auth cdb file in /tmp:
$MKAUTH -c /tmp/auth.cdb.new

# Generate a new validrcptto file in /tmp:
$MKVRCPT -c /tmp/validrcptto.cdb.new

# Generate SUM of old and new auth file:
AUTHSUMOLD=$(md5sum $AUTHFILE | cut -f1 -d ' ')
AUTHSUMNEW=$(md5sum /tmp/auth.cdb.new | cut -f1 -d ' ')

# Generate SUM of old and new validrcptto file:
VRCPTSUMOLD=$(md5sum $VRCPTFILE | cut -f1 -d ' ')
VRCPTSUMNEW=$(md5sum /tmp/validrcptto.cdb.new | cut -f1 -d ' ')

if [ "$AUTHSUMOLD" != "$AUTHSUMNEW" ] || [ "$VRCPTSUMOLD" != "$VRCPTSUMNEW" ] ; then 
  logger -s "qmail - Auth CDB or ValidRCPTTo CDB has changed. Need to restart qmail with new CDBs."
  logger -s "qmail - Creating CDB $VRCPTFILE ..."
  logger -s "qmail - Creating CDB $AUTHFILE ..."
  $QMAILCTL restart
  logger -s "qmail - CDBs recreated. qmail service restarted."
  echo  "qmail - No changes found in Auth CDB or ValidRCPTTo CDB."
  # No need to fill the log with "No changes found" message
  # logger -s "qmail - No changes found in Auth CDB or ValidRCPTTo CDB."

exit $?

######## End - Actual program code


Personal tools