Setup (production)

From d00d3
Revision as of 18:49, 16 October 2013 by Andrenarchy (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This document covers the setup of this server with the following components:

  • OpenLDAP for administration of users
  • lighttpd
  • Dovecot as IMAPs server
  • Postfix as SMTP server
  • Mail filtering with:
    • AMaViS with clamav
    • SpamAssassin
    • DKIM
    • SPF
    • roundcube


Initial Configuration on 2010/10/24


We had a plain Debian 5.0 "Lenny" installed. For general purposes we use the domain and for the mail server we use

First step: become root!

sudo su
cd ~


This is a handy tool for good passwords (and we are going to need lots of them):

apt-get install makepasswd

Disable portmap

We do not want portmap to listen on the public interface. Edit /etc/default/portmap such that it contains


SSL certificates

We created a certificate signed by like this:

groupadd --system sslusers
apt-get install openssl
sh csr

Fill in the forms, e.g. like this

Short Hostname (ie. imap big_srv www2): d00d3
FQDN/CommonName (ie. :
Type SubjectAltNames for the certificate, one per line. Enter a blank line to finish
SubjectAltName: DNS:

Then paste the CSR to or sign it as you like and paste the public server certificate into a file d00d3_publiccert.pem. Now we'll do some corrections and move the files

cat d00d3_publiccert.pem d00d3_privatekey.pem > d00d3_pubpriv.pem
chmod 640 d00d3_pubpriv.pem d00d3_privatekey.pem
chown root:sslusers d00d3_pubpriv.pem d00d3_privatekey.pem
mv d00d3_* /etc/


apt-get install slapd
dpkg-reconfigure slapd

Our choice was:

  • DNS:
  • Orga Name:
  • Password: (we will call it LDAPADMINPASS later on)
  • Backend: HDB
  • Purge: No
  • Allow v2: No

We only want ldaps on the public interface and users have to authenticate themselves even for reading. Everyone can read on the local interface. Open /etc/ldap/slapd.conf and change or add the following lines

include         /etc/ldap/schema/misc.schema

access to attrs=userPassword,shadowLastChange
        by dn="cn=admin,dc=d00d3,dc=net" write
        by dn="cn=dovecot,dc=d00d3,dc=net" read
        by anonymous auth
        by self write
        by * none

# change "access to *" part to:
access to *
        by dn="cn=admin,dc=d00d3,dc=net" write
        by peername.ip= read
        by users read

# add at end
index   cn                      pres,sub,eq
index   sn                      pres,sub,eq
index   uid                     pres,sub,eq
index   displayName             pres,sub,eq
index   default                 sub
index   uidNumber               eq
index   gidNumber               eq
index   memberUid               eq
index   uniqueMember            eq
index   mail,givenName          eq,subinitial
index   dc                      eq
index   associatedDomain        eq
index   mailLocalAddress        eq

TLSCertificateFile /etc/d00d3_publiccert.pem
TLSCertificateKeyFile /etc/d00d3_privatekey.pem

Edit /etc/default/slapd:

SLAPD_SERVICES="ldap:// ldaps:///"

Then restart slapd:

/etc/init.d/slapd stop
chown openldap:openldap -R /var/lib/ldap
sudo -u openldap slapindex
/etc/init.d/slapd start


This is a simple one:

apt-get install lighttpd php5-cgi
lighty-enable-mod fastcgi 
lighty-enable-mod ssl
mkdir /var/wwws

Change /etc/lighttpd/conf-available/10-ssl.conf

ssl.pemfile                 = "/etc/d00d3_pubpriv.pem"
server.document-root        = "/var/wwws"

and issue

/etc/init.d/lighttpd force-reload


apt-get install phpldapadmin
ln -s /usr/share/phpldapadmin /var/wwws/

Now you can browse to (username: cn=admin,dc=d00d3,dc=net, password LDAPADMINPASS) and populate the LDAP tree. Our structure is like this:

++ dc=d00d3,dc=net
 + cn=admin
 + cn=dovecot (organizationalRole,simpleSecurityObject)
 +-+ ou=domains
 | + (organizationalUnit,domainRelatedObject)
 | +
 | (...)
 +-+ ou=people
   + cn=Test Person (inetOrgPerson)
   + (...)

Be sure you also create a cn=dovecot,dc=d00d3,dc=net entry with a password (LDAPDOVECOTPASS). Do not add as a domain since this will be the "local" postfix domain.


Buggy dovecot-antispam in Debian Lenny

Unfortunately, the dovecot-antispam package has a bug in Debian Lenny. We compiled our own and for this we used another machine since dpkg pulls in a lot of dependencies only for building the package. This is what we did:

echo "deb-src lenny-backports main" >> /etc/apt/sources.list
apt-get update
mkdir /usr/local/src/dovecot-antispam
cd /usr/local/src/dovecot-antispam
apt-get build-dep -t lenny-backports dovecot-antispam
apt-get -b source -t lenny-backports dovecot-antispam

Then we copied the file dovecot-antispam_1.2+20090702-1~bpo50+3_amd64.deb to the server and issued

dpkg -i dovecot-antispam_1.2+20090702-1~bpo50+3_amd64.deb

Put the package on hold:

echo dovecot-antispam hold | dpkg --set-selections

To undo this replace "hold" with "install".

IMAP and Local Delivery Agent

We use virtual users and all mailboxes are owned by the user 'vmail' and group 'vmail':

addgroup --system --gid 900 vmail
adduser --system --home /var/vmail --uid 900 --ingroup vmail vmail

These are the changes to /etc/dovecot/dovecot.conf

protocols = imaps managesieve
ssl = yes
ssl_cert_file = /etc/d00d3_publiccert.pem
ssl_key_file = /etc/d00d3_privatekey.pem
mail_location = maildir:~/Maildir
protocol imap {
  mail_plugins = autocreate antispam
protocol managesieve {
  listen =
protocol lda {
  postmaster_address =
  hostname =
  mail_plugins = sieve autocreate
  sendmail_path = /usr/sbin/sendmail
  auth_socket_path = /var/run/dovecot/auth-master
auth default {
  mechanisms = plain
  #passdb pam {
  passdb ldap {
    args = /etc/dovecot/dovecot-ldap.conf
  #userdb passwd {
  userdb static {
    # id of vmail user/group (should be above 500!)
    args = uid=900 gid=900 home=/var/vmail/%d/%n
  user = nobody
  socket listen {
    master {
      # Master socket provides access to userdb information. It's typically
      # used to give Dovecot's local delivery agent access to userdb so it
      # can find mailbox locations.
      path = /var/run/dovecot/auth-master
      mode = 0600
      # Default user/group is the one who started dovecot-auth (root)
      user = vmail
      group = vmail
    client {
      # The client socket is generally safe to export to everyone. Typical use
      # is to export it to your SMTP server so it can do SMTP AUTH lookups
      # using it.
      #path = /var/run/dovecot/auth-client
      path = /var/spool/postfix/private/auth-client
      mode = 0660
      user = postfix
      group = postfix
plugin {
  sieve_after = /etc/dovecot/sieve_after/
  # Autocreate plugin
  autocreate = Trash
  autocreate2 = Sent
  autocreate3 = Spam
  autocreate4 = Drafts
  autosubscribe = Trash
  autosubscribe2 = Sent
  autosubscribe3 = Spam
  autosubscribe4 = Drafts
  # dovecot-antispam plugin
  antispam_spam = Spam
  antispam_trash = Trash
  antispam_mail_sendmail = /usr/bin/sa-learn
  antispam_mail_sendmail_args = -u;%u
  antispam_mail_spam = --spam
  antispam_mail_notspam = --ham

We need to configure Dovecot's LDAP passwd functionality by making these changes to /etc/dovecot/dovecot-ldap.conf:

hosts = localhost
dn = cn=dovecot,dc=d00d3,dc=net
base = ou=people,dc=d00d3,dc=net
ldap_version = 3
scope = subtree
pass_attrs = mail=user,userPassword=password
pass_filter = (&(objectClass=inetOrgPerson)(mail=%u))
default_pass_scheme = PLAIN

Furthermore, we want to install a default sieve script which filters Spam into the 'Spam' IMAP folder.

mkdir /etc/dovecot/sieve_after

Create a file /etc/dovecot/sieve_after/spam.sieve with the following content:

require ["fileinto"];

# rule:[DKIM]
if anyof (header :contains "Authentication-Results" "dkim-adsp=discard")
        fileinto "Spam";
# rule:[SPAM]
if anyof (header :contains "X-Spam-Flag" "YES")
        fileinto "Spam";

The script has to be compiled by runnind

sievec /etc/dovecot/sieve_after


Restart Dovecot now:

/etc/init.d/dovecot restart

The IMAP server should run now and you should be able to login using your LDAP credentials with your favorite mail client via IMAPs.


apt-get install mysql-server phpmyadmin

Choose a password for MySQL's root account (MYSQLROOTPASS) and don't let the phpmyadmin package configure anything. Do it yourself with

ln -s /usr/share/phpmyadmin /var/wwws/
/etc/init.d/lighttpd restart

Then browse to and login with root and MYSQLROOTPASS. Head for privileges => new user and create a user spamass for localhost, choose a password (SPAMASSPASS) and tick Create database with same name and grant all privileges.


We need to add the volatile archive to apt's sources and add a user for spamd:

echo "deb lenny/volatile main" >> /etc/apt/sources.list
apt-get update
adduser --system --home /var/spamd --group spamd
apt-get install spamassassin pyzor razor libmail-dkim-perl libmail-spf-query-perl

Change the following lines in /etc/default/spamassassin:

OPTIONS="--max-children 5 --nouser-config --sql-config -u spamd -g spamd -D"

Now use the password SPAMASSPASS to initialize the database for SpamAssassin

mysql -h localhost -u spamass -p spamass < /usr/share/doc/spamassassin/sql/bayes_mysql.sql
mysql -h localhost -u spamass -p spamass < /usr/share/doc/spamassassin/sql/bayes_mysql.sql
mysql -h localhost -u spamass -p spamass < /usr/share/doc/spamassassin/sql/userpref_mysql.sql

and teach SpamAssassin how to reach it by creating a file /etc/spamassassin/ with the contents

bayes_store_module               Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn                    DBI:mysql:spamass:localhost
bayes_sql_username               spamass
bayes_sql_password               SPAMASSPASS # <-- CHANGE!!!

user_scores_dsn                  DBI:mysql:spamass:localhost
user_scores_sql_username         spamass
user_scores_sql_password         SPAMASSPASS # <-- CHANGE!!!
user_scores_sql_custom_query     SELECT preference, value FROM _TABLE_ WHERE username = _USERNAME_ OR username = '$GLOBAL' OR username = CONCAT('%',_DOMAIN_) ORDER BY username ASC

Then issue

chmod 440 /etc/spamassassin/
chown vmail:spamd /etc/spamassassin/
/etc/init.d/spamassassin restart
chown -R spamd:spamd /var/spamd/.pyzor

AMaViS + ClamAV

apt-get install amavisd-new clamav-daemon
usermod -G amavis -a clamav
usermod -G clamav -a amavis
usermod -G spamd -a amavis # we'll let amavis cronjobs maintenance spamassassin

Uncomment virus checks in /etc/amavis/conf.d/15-content_filter_mode so that it reads

@bypass_virus_checks_maps = (
   \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);

In /etc/amavis/conf.d/50-user add

$myhostname = '';
$final_virus_destiny = D_DISCARD;

Unfortunately, there's a bug in lenny's amavisd-new, so we fix it by editing /usr/sbin/amavisd-new. Search for 'postmaster'. The line

$hdrfrom_notify_sender = "$pname <postmaster\@\${myhostname}>"

should read

$hdrfrom_notify_sender = "$pname <postmaster\@${myhostname}>"

Then restart amavis and clamav:

/etc/init.d/clamav-daemon restart
/etc/init.d/amavis restart


apt-get install -t lenny-backports dkim-filter

Add this line to /etc/default/dkim-filter:


And edit /etc/dkim-filter.conf:

Syslog			yes
UMask			002
Background		yes
DNSTimeout		5
Mode			sv
SubDomains		yes
ADSPDiscard		yes
X-Header		yes
Statistics		/var/run/dkim-filter/dkim-stats
KeyList			/etc/dkim-keys.conf

Create a directory for DKIM keys:

mkdir /etc/mail/dkim
cd /etc/mail/dkim

For each domain you wish a separate key you have to run the next commands. This is what I did for the domain '':

dkim-genkey -b 1024 -d -s alpha #create key with selector 'alpha' (you may choose another name of course)
chown dkim-filter:dkim-filter alpha.private
chmod 600 alpha.private
echo "*" >> /etc/dkim-keys.conf #add domain
echo "*@*" >> /etc/dkim-keys.conf #add subdomains of
/etc/init.d/dkim-filter restart

Then you have to update the DNS record of each domain. Add the contents of /etc/mail/dkim/ to your record. Also add a ADSP entry for each subdomain you sign mails for:

_adsp._domainkey IN TXT "dkim=discardable" #for domain
_adsp._domainkey.sub IN TXT "dkim=discardable" #for domain

Note: wildcards are NOT allowed for subdomain ADSP records.


Make sure that /etc/aliases contains redirects for postmaster, clamav, amavis and root. On it contains:

postmaster: root
clamav: root
amavis: root
root: andre

'andre' is a 'real' user on and has a file '.forward' in his home with several email addresses (one per line) to redirect mails to. This will work once Postfix has been setup. Do not forget to run


after you changed /etc/aliases.



apt-get install postfix postfix-ldap postfix-policyd-spf-python postgrey

without doing any configuration.


Add these entries to's DNS record:

 IN TXT "v=spf1 mx ~all" #for
* IN TXT "v=spf1 mx ~all" #for subdomains of

Add these entries to all other domains (with appropriate MX records pointing to

 IN TXT "v=spf1"
* IN TXT "v=spf1"


Edit /etc/default/postgrey:

POSTGREY_OPTS="--inet= --max-age=70"

Then issue

/etc/init.d/postgrey restart

echo -e "\n" > /etc/mailname

This is the /etc/postfix/ file:

myorigin = /etc/mailname
myhostname =
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h 

readme_directory = no

# TLS parameters
smtpd_tls_auth_only = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = localhost
mynetworks =
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth-client
smtpd_sasl_auth_enable = yes

spamassassindeliver_destination_recipient_limit = 1
mailbox_transport = spamassassindeliver
virtual_transport = spamassassindeliver
virtual_mailbox_maps = ldap:/etc/postfix/
virtual_mailbox_domains = ldap:/etc/postfix/
virtual_alias_maps = ldap:/etc/postfix/
smtpd_sender_login_maps = ldap:/etc/postfix/


        check_policy_service inet:,
        check_policy_service unix:private/policy-spf,


milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
spf-policyd_time_limit = 3600s

# amavis clamav
content_filter = smtp-amavis:[]:10024


Create the file /etc/postfix/ and add

server_host = ldap://localhost
search_base = ou=domains,dc=d00d3,dc=net
bind = no
query_filter = (&(objectclass=domainRelatedObject)(associatedDomain=%s))
result_attribute = associatedDomain

Create the file /etc/postfix/ and add

server_host = ldap://localhost
search_base = ou=people,dc=d00d3,dc=net
bind = no
query_filter = (&(objectclass=inetOrgPerson)(mail=%s))
result_attribute = mail

Create the file /etc/postfix/ and add

server_host = ldap://localhost
search_base = ou=people,dc=d00d3,dc=net
bind = no
query_filter = (&(objectclass=inetLocalMailRecipient)(mailLocalAddress=%s))
result_attribute = mailRoutingAddress

Edit the file /etc/postfix/ according to this:

smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
pickup    fifo  n       -       -       60      1       pickup
         -o content_filter=
         -o receive_override_options=no_header_body_checks
policy-spf  unix  -       n       n       -       -       spawn
  user=nobody argv=/usr/bin/policyd-spf
spamassassindeliver unix -     n       n       -       -       pipe
        user=vmail:vmail argv=/usr/bin/spamc -u ${recipient} -t 30 -e
        /usr/lib/dovecot/deliver -f ${sender} -d ${recipient}
smtp-amavis     unix    -       -       -       -       2       smtp
        -o smtp_data_done_timeout=1200
        -o smtp_send_xforward_command=yes
        -o disable_dns_lookups=yes
        -o max_use=20 inet    n       -       -       -       -       smtpd
        -o content_filter=
        -o local_recipient_maps=
        -o relay_recipient_maps=
        -o smtpd_restriction_classes=
        -o smtpd_delay_reject=no
        -o smtpd_client_restrictions=permit_mynetworks,reject
        -o smtpd_helo_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o smtpd_data_restrictions=reject_unauth_pipelining
        -o smtpd_end_of_data_restrictions=
        -o mynetworks=
        -o smtpd_error_sleep_time=0
        -o smtpd_soft_error_limit=1001
        -o smtpd_hard_error_limit=1000
        -o smtpd_client_connection_count_limit=0
        -o smtpd_client_connection_rate_limit=0
        -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters

Restart postfix:

/etc/init.d/postfix restart


apt-get install -t lenny-backports roundcube roundcube-mysql

Choose auto-config and choose a password for roundcube (ROUNDPASS). Then edit the following lines in /etc/roundcube/

$rcmail_config['force_https'] = TRUE;
$rcmail_config['default_host'] = 'ssl://localhost';
$rcmail_config['default_port'] = 993;
$rcmail_config['preview_pane'] = TRUE;

We then download the plugins 'managesieve' and 'sauserprefs' (SpamAssassin user preferences):

cd /usr/share/roundcube/plugins

wget "" -O
mv branches/release-0.3.1/plugins/managesieve .
rm -rf branches
mv managesieve/ managesieve/

wget "" -O sauser.tar.gz
tar -zxf sauser.tar.gz
mv JohnDoh-Roundcube-Plugin-SpamAssassin-User-Prefs-SQL-620cb74/ sauserprefs
rm sauser.tar.gz 
mv sauserprefs/ sauserprefs/

Edit /usr/share/roundcube/plugins/sauserprefs/

$sauserprefs_config['db_dsnw'] = 'mysql://spamass:SPAMASSPASS/spamass';
$sauserprefs_config['bayes_delete_query'] = array(
                'DELETE FROM bayes_seen WHERE id IN (SELECT id FROM bayes_vars WHERE username = %u);',
                'DELETE FROM bayes_token WHERE id IN (SELECT id FROM bayes_vars WHERE username = %u);',
                'DELETE FROM bayes_vars WHERE username = %u;',

Edit /usr/share/roundcube/config/

$rcmail_config['plugins'] = array('sauserprefs','managesieve');

Mail check

Try to login to and try to send mails. Check DKIM by sending a mail to The result should read like this:

  DKIM Signature validation: pass (1024-bit key)
  DKIM Author Domain Signing Practices: "dkim=discardable"


apt-get install -t lenny-backports mediawiki mediawiki-extensions-ldapauth

Go to => 'privileges' => 'new user' and add user 'mediawiki' for 'localhost', choose password (WIKISQLPASS) and tick 'Create database with same name and grant all privileges'. Then create this symlink:

ln -s /var/lib/mediawiki /var/www/w

Add this to /etc/lighttpd/lighttpd.conf

# mediawiki stuff:
$HTTP["scheme"] == "http" {
        $HTTP["host"] =~ "(.*)" {
                url.redirect = ( "^/$" => "https://%1/" )

$HTTP["scheme"] == "https" {
        url.rewrite-once = (
                     "^/wiki/upload/(.+)" => "/wiki/upload/$1",
                     "^/$" => "/w/index.php",
                     "^/wiki/([^?]*)(?:\?(.*))?" => "/w/index.php?title=$1&$2"

Reload lighttpd:

/etc/init.d/lighttpd force-reload

Go to (or, if this does not work for whatever reason, goto and configure your wiki:

  • name: d00d3
  • email:
  • lang: en
  • license: public domain
  • admin name: WikiSysop
  • pass: WIKIPASS (choose one!)
  • no caching
  • email glob: enabled
  • email u2u: enabled
  • email notify: enable discussion+watchlist
  • email auth: enabled
  • db: mysql
  • db host: localhost
  • db name: mediawiki
  • db user: mediawiki
  • db pass: WIKISQLPASS
  • db superuser: no
  • no specific options

Enable LDAP plugin:

ln -s /etc/mediawiki-extensions/extensions-available/LdapAuthentication.php /etc/mediawiki-extensions/extensions-enabled/
chown root:www-data /etc/mediawiki/LocalSettings.php
chmod 640 /etc/mediawiki/LocalSettings.php

Edit /etc/mediawiki/LocalSettings.php and append

$wgAuth = new LdapAuthenticationPlugin();
$wgLDAPDomainNames = array(
$wgLDAPServerNames = array(
  '' => 'localhost'
$wgLDAPEncryptionType = array(
  '' => 'clear'
$wgLDAPSearchAttributes = array(
  '' => 'uid'
$wgLDAPBaseDNs = array(
  '' => 'ou=people,dc=d00d3,dc=net'
$wgLDAPPreferences = array(
  '' => array(
$wgGroupPermissions['*']['createaccount']    = false;
$wgGroupPermissions['*']['read']             = true;
$wgGroupPermissions['*']['edit']             = false;
$wgGroupPermissions['*']['createpage']       = false;
$wgGroupPermissions['*']['createtalk']       = false;
$wgGroupPermissions['*']['writeapi']         = false;
$wgLogo = '/wikistar.png';

Log in with your account at and (if you want to be the admin) add yourself to sysop and bureaucrat group by fiddling in phpmyadmin => mediawiki => user_groups. ;)


  • Pay attention when updating roundcube (>3.1 => update plugins!)
  • Do NOT update dovecot-antispam unless there is a newer version (sounds stupid but thats the way it is)

Later Changes

2010-12-01: apticron

apt-get install apticron

2011-04-26: roundcube/php login issue

In /etc/php5/conf.d/suhosin.ini, set

suhosin.session.encrypt = off

Thanks to ID's blog! This issue drove me nuts! Without this you'll see the login screen over and over again after a (successful!) login.

2011-07-11: dovecot-antispam update

change in configuration /etc/dovecot/dovecot.conf

 # dovecot-antispam plugin
 antispam_backend = pipe
 antispam_spam = Spam
 antispam_trash = Trash
 #antispam_mail_sendmail = /etc/spamassassin/
 #antispam_mail_sendmail_args = -u;%u
 #antispam_mail_spam = --spam
 #antispam_mail_notspam = --ham
 antispam_pipe_program = /etc/spamassassin/
 antispam_pipe_program_args = -u;%u
 antispam_pipe_program_spam_arg = --spam
 antispam_pipe_program_notspam_arg = --ham

2011-08-07: ipv6

I changed the kvm network from a libvirt-managed bridge to kvmbr0 (see Setup (r00t)).

Here's /etc/network/interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

iface eth0 inet6 static
	address 2a01:4f8:120:4024:1337::3
	netmask 80
	up ip -6 route add default via 2a01:4f8:120:4024:1337::2

2013-10-16: upgrade Debian to 7.2 wheezy


deb  wheezy main contrib non-free
deb  wheezy/updates  main contrib non-free

Remove content of /etc/apt/preferences.

apt-get update
apt-get upgrade
apt-get dist-upgrade
Personal tools