From PMWH2 - PHPMyWebHosting's official wiki
Jump to: navigation, search

Needed Packages

On Debian GNU/Linux you need to install the following packages:

  • postfix
  • postfix-mysql
  • postfix-tls (for Auth-SMTP)
  • libsasl-digestmd5-des
  • libsasl-gssapi-mit or libsasl-gssapi-heimdal
  • libsasl-modules-plain
  • libpam-mysql
  • sasl-bin
  • amavisd-new, zoo, lha, unarj, unzip (if you want virus scanning)


On Debian GNU/Linux use this command to install all at once:

apt-get install postfix postfix-mysql postfix-tls courier-authdaemon courier-authmysql \
    courier-base courier-maildrop courier-pop courier-pop-ssl courier-imap courier-imap-ssl \
    libsasl-digestmd5-des libsasl-gssapi-mit libsasl-modules-plain libpam-mysql sasl-bin

If you want to use amavisd-new, put this line to your /etc/apt/sources.list:

deb hmh/amavisd-new/
deb hmh/misc/

and do a

apt-get update && apt-get install amavisd-new unzip

Please create a new systemuser named "vmail" to your system.

On Debian GNU/Linux use this command:

adduser --disabled-password --disabled-login --gecos "Mail user" vmail

After the creation, type this to get uid and gid of the vmail user, we will need those values in Postfix'

bash #> id vmail
uid=1005(vmail) gid=1005(vmail) Gruppen=1005(vmail)
bash #>

Postfix configuration

First, you have to configure postfix to connect to mysql database which is installed via phpmywebhosting.

First of all an important note: Remember that Postfix runs in a chroot environment. Thus it would not be able to access MySQL's socket file at /var/run/mysqld/mysqld.sock. There are two ways to work around this: Mario Duve (et al) propose an addition to the start section of /etc/init.d/postfix like this:

if [ -e /var/spool/postfix/var/run/mysqld/mysqld.sock ]; then
      rm /var/spool/postfix/var/run/mysqld/mysqld.sock

mkdir -p /var/spool/postfix/var/run/mysqld
chown mysql /var/spool/postfix/var/run/mysqld
ln /var/run/mysqld/mysqld.sock /var/spool/postfix/var/run/mysqld/mysqld.sock

In case of power failure postfix won't start working because the socket of mysql is not valid. To prevent this edit /etc/init.d/postfix and add the above lines in the start section under echo -n "Starting mail transport agent: Postfix". Result is:

case "$1" in
      echo -n "Starting mail transport agent: Postfix"
      if [ -e /var/spool/postfix/var/run/mysqld/mysqld.sock ]; then
            rm /var/spool/postfix/var/run/mysqld/mysqld.sock
      mkdir -p /var/spool/postfix/var/run/mysqld
      chown mysql /var/spool/postfix/var/run/mysqld
      ln /var/run/mysqld/mysqld.sock /var/spool/postfix/var/run/mysqld/mysqld.sock
      # see if anything is running chrooted.

Also take care, that postfix is started after mysql. Check it with the following command:

bash #> ls /etc/rc$(grep initdefault /etc/inittab | cut -d: -f2).d/ | grep -E "(postfix|mysql)"
bash #>

If it is like above, everything is alright. If not, delete the links in /etc/rc{runlevel}.d/ and create them new.

Or use the real hostname of the MySQL server instead of "localhost". This way database lookups will be done via UDP instead of UNIX sockets. In this case you will need to comment out the line "skip-networking" in the /etc/mysql/my.cnf file to enable the UDP listener.

Debian: /etc/postfix/

# basic settings
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix
program_directory = /usr/lib/postfix 
myorigin = [your domain] 
mydomain = [your domain]
myhostname = [hostname.your domain]
mydestination = $myhostname, localhost.$mydomain, $mydomain, $transport_maps
mynetworks = [local IP address range]

# virtual domain and delivery settings
virtual_mailbox_limit = 10000000
virtual_mailbox_base = /home/vmail/
virtual_uid_maps = static:[VMAIL-UID]
virtual_gid_maps = static:[VMAIL-UID]
virtual_mailbox_maps = mysql:/etc/postfix/
alias_maps = mysql:/etc/postfix/
transport_maps = mysql:/etc/postfix/
virtual_maps = mysql:/etc/postfix/

# define which email is accepted for delivery only 
# alias_maps means: addresses in the MySQL aliase table
# virtual_mailbox_maps: addresses in the MySQL virtual table
# unix:passwd.byname: addresses of local users in /etc/passwd (optional)
local_recipient_maps = $alias_maps $virtual_mailbox_maps unix:passwd.byname

# settings for authenticated SMTP
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, check_relay_domains
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes

# some other options
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) # or FreeBSD ;-)
setgid_group = postdrop
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no
relayhost =  
mailbox_size_limit = 0
recipient_delimiter = +

message_size_limit = 10000000

As you can see we will not be querying the MySQL database according to definitions in /etc/postfix/

For this to work you need to create these files first. They define how Postfix accesses the database and how you named the fields. Just copy and paste them:

Here is the following set of files in a tar.gz. A little perl script called will ask you for the needed values and fill the files with the correct values. To use it, just do

bash #> tar xvfz portfix-mysql.tar.gz
bash #> cd postfix
bash #> ./
What is the name of the database admin user? [postfix database name, e.g. vmail]
What is his password?                        [another secret from above]
What is the database hostname?               localhost [or IP address]
bash #> cp mysql-* /etc/postfix/
bash #>

It creates the files following in the next chapter. Just copy them to your /etc/postfix/ directory and restart postfix using

/etc/init.d/postfix restart

Debian: /etc/postfix/

user = [database admin user]
password = [database admin password]
dbname = provider
table = users
select_field = maildir
where_field = email
additional_conditions = and postfix = 'y'
hosts = [database server hostname]

Debian: /etc/postfix/

user = [database admin user]
password = [database admin password]
dbname = provider
table = alias
select_field = destination
where_field = alias
hosts = [database server hostname]

Debian: /etc/postfix/

user = [database admin user]
password = [database admin password]
dbname = provider
table = transport
select_field = destination
where_field = domain
hosts = [database server hostname]

Debian: /etc/postfix/

user = [database admin user]
password = [database admin password]
dbname = provider
table = virtual
select_field = destination
where_field = email
hosts = [database server hostname]

Now we configure postfix to understand authenticated SMTP.

Imagine your users fetch their email using POP3. Now they need a way to send mails via your mail server. For security reasons Postfix allows users in the local (=trusted) network to send emails. (You obviously want this security if you don't want to work as an open relay for spammers.) My suggestion is that your users use authenticated SMTP. This means that their email clients provide a username and password to the mail server to make them "trusted". Most email cilents have this feature included.

Setting authenticated SMTP isn't that complicated. The only pitfall is that Debian runs Postfix in a chroot'ed environment by default. ("chroot'ed" means that Debian pretends that the "/var/spool/postfix" directory is pretended to be "/" - so Postfix cannot move out of this secure subdirectory. Anyone who finds a security problem and messes with Postfix can only do harm to that very subdirectory and not screw your whole system)

There may be different ways to use authentication with SMTP. I decided to use PAM (pluggable authentication modules). The authentication configuration is usually stored in "/etc/pam.d". As Postfix runs chroot'ed the configuration is searched for in "/var/spool/postfix/etc/pam.d". Just go there and put the following lines in a file called "smtp" (the backslashes show that all declarations are to be written in a single line):

Debian: /var/spool/postfix/etc/pam.d/smtp

auth required user=[database admin user] passwd=[database admin password] host=[database server hostname] \
       db=provider table=users usercolumn=email passwdcolumn=password crypt=1
account sufficient user=[database admin user] passwd=[database admin password] host=[database server hostname] \
       db=provider table=users usercolumn=email passwdcolumn=password crypt=1

For security reasons we change the ownership and permissions of that file. So it only can be read by root and postfix:

bash #> chown root:postfix /var/spool/postfix/etc/pam.d/smtp
bash #> chmod 660 /var/spool/postfix/etc/pam.d/smtp
bash #>

The second step is to make SASL use the PAM for authentication. This is easy. Just put the following line in


pwcheck_method: pam

Third and last step: copy the /lib/security/ to /var/spool/postfix/lib/security to make it accessible for Postfix (chroot'ed, remember?):

bash #> mkdir -p /var/spool/postfix/lib/security/
bash #> cp /lib/security/ /var/spool/postfix/lib/security/

(I'm not a PAM guru. If the PAM configuration can be simplified please tell me. I was glad they worked the way they are.)

Virus filtering

Now that you operate your own professional email server you may offer your users a little more service: scanning incoming emails for viruses. Good news: it is perfectly easy to install. Bad news: it will most likely mean you have to spend money because good antivirus software does not come for free (yet).

Let's get to work. First make sure you installed the amavisd-new package. Amavis is a piece of software that gets all emails forwarded, scans them using an external virus scanner and re-injects them into your mail system. It is important to note that Amavis does not include a virus scanner - it is just an interface between Postfix and your favorite virus scanner. So make sure you have a virus scanner already installed and keep it up-to-date.

Now please take a look at the /etc/amavis/amavisd.conf (FreeBSD 5.3: /usr/local/etc/amavisdconf) file. All configuration you find here are well explained and should be easy to edit. Make sure that you configure at least one virus scanner in the first part of the file and have the following options set:

Debian: /etc/amavis/amavisd.conf

$forward_method = 'smtp:'
$inet_socket_port = 10024;

To get clamav, a free virus scanner, run the following command on

bash #> apt-get install clamav clamav-freshclam clamav-testfiles clamav-daemon clamav-base spamassassin

Restart the amavisd:

bash #> /etc/init.d/amavis restart

amavisd-new has a lot, lot more options to use other scanners. Most of them are "free" for uncommercial use, but closed source. So make up your mind if you want them or not. If you decide to install another, you just have to uncomment the designated part in amavisd.conf - in most cases.

The last thing to do is tell Postfix that you want to have all your emails scanned. First edit /etc/postfix/ and add the following line:

content_filter = smtp-amavis:[]:10024

Now you have told Postfix that you want to use the "content_filter = smtp-amavis:[]:10024" transport method on all emails. You may also consider setting "soft_bounce = yes" temporarily to make sure emails do not get bounced when something goes wrong. Emails will stay in your mail queue if you set this to true.

Finally tell Postfix what you mean by "content_filter = smtp-amavis:[]:10024". Add this section to the

Debian: /etc/postfix/

smtp-amavis unix -      -       n     -       2  smtp
   -o smtp_data_done_timeout=1200
   -o disable_dns_lookups=yes inet n  -       n     -       -         smtpd
   -o content_filter=
   -o local_recipient_maps=
   -o relay_recipient_maps=
   -o smtpd_restriction_classes=
   -o smtpd_client_restrictions=
   -o smtpd_helo_restrictions=
   -o smtpd_sender_restrictions=
   -o smtpd_recipient_restrictions=permit_mynetworks,reject
   -o mynetworks=
   -o strict_rfc821_envelopes=yes

Restart Postfix (postfix reload) and you are set. You may want to watch the log file (tail -f /var/log/mail.log) and send a test mail. You should find a line like "X-Virus-Scanned: by AMaViS snapshot-20020222" in the email header. To make sure that your system handles infected emails correctly you may want to attach ''. Eicar is not a virus but a well-known (to virus scanners) signature to test if your virus recognition works. When you send this "infected" email to yourself you will receive an alert from Amavis with subject "VIRUS IN YOUR MAIL".

A last word to explain how Amavis works. Postfix receives your mail and pipes the email as defined in your "vscan" transport method into the amavis tool. Amavis will then use the configured virus scanner to scan all attachments. If the attachments are archives it will even use the archive tools to unpack them. After scanning the files it will contact Postfix on localhost's port 10025 and re-inject the email into the delivery process. But this time it sets the "content_filter" option to nothing and thus bypasses the content scanning this time. You will also see this behaviour in your /var/log/mail.log file. First you will see a "relay=vscan" and then a "relay=local".

Amavisd can also be quite intense. Don't shoot me, but after all it's "just" Perl. ;-) Keep in mind.

In case you are not authorized, make sure you tail the auth.log (probably to be found in /var/log) and also use "postfix check" to see if you have any issues open. So for example, if your permissions are screwed up and postfix can not find something, it will most likely tell you.

Open issues

If you have been successful in one of the following issues then please tell us so we can include the parts here:

  • Enforcing quotas during delivery
  • how to integrate postgrey with the Debian/GNU installation