There is a new version of this tutorial available for Ubuntu 18.04 (Bionic Beaver).

Postfix Virtual Hosting With LDAP Backend And With Dovecot As IMAP/POP3 Server On Ubuntu Kamic Koala 9.10

This how to will allow you step by to configure a Postfix mail server with with virtual hosting. Virtual hosting means that you can add as many mail domains as you want and sub sequentially as many mailboxes for these domains as you want.

Here we we use an LDAP backend for both the MTA (Postfix) and POP3/IMAP server (Dovecot), and a web based management interface.

Optional in this how to is the use of Roundcube webmail and proftpd.

The new version of Roundcube 0.3 allows a webmail user to change his / her (ldap) password, integrate an ldap address book and vacation using ldap (plugin). These features align nicely with the setup used in this how to and also provides an easy interface for the mail user.

This how to is an upgraded and enhanced version of the Ubuntu Intrepid version. Please note that the configuration of Roundcube and Proftpd can be added to a Intrepid / Jaunty setup.

Software to be used in this how to:

Postfix MTA, Dovecot IMAP / POP3, OpenLDAP, Gnarwl as autoresponder (vacation), Proftpd as ftp server, Phamm as management interface, MySQL as database backend for the webmail and Roundcube as webmail.

This worked for me, but I cannot guarantee that this set up will work for you so this how to comes without any guarantee.

Assumptions:

This how to assumes the following configurations, if your installation differs from this, then replace the entries below with your actual configuration.

Mail delivery (mailboxes) path:

/home/vmail/domains

User vmail:

UID:1000, GID:1000

User postfix:

UID: 108, GID:108

OpenLDAP base dn:

dc=example,dc=tld

OpenLDAP admin account:

cn=admin,dc=example,dc=tld

Phamm search dn:

o=hosting,dc=example,dc=tld

A read only account for the o=hosting,dc=example,dc=tld tree:

cn=phamm,o=hosting,dc=example,dc=tld

You're using root as the user during this guide. 

If you want for example o=maildomains or ou=domains, please make sure to replace o=hosting with what you  want, especially in the acl.ldif. This acl file is strict, phamm will not work correctly if it is not exactly as it should be. If you want a different read only user than phamm than replace cn=phamm with cn=wat-you everywhere in this how to.

 

Step 1: Install And Configure Ubuntu Server

I recommend following the guide below for this (I do not need to rewrite or reinvent what others did better than me) :

The Perfect Server - Ubuntu 9.10 [ISPConfig 3]

Replace the following on page 4:

aptitude install postfix postfix-mysql postfix-doc mysql-client mysql-server courier-authdaemon courier-authlib-mysql courier-pop courier-pop-ssl courier-imap courier-imap-ssl libsasl2-2 libsasl2-modules libsasl2-modules-sql sasl2-bin libpam-mysql openssl getmail4 rkhunter binutils

by

aptitude install postfix postfix-ldap mysql-client mysql-server dovecot-imapd dovecot-pop3d libsasl2-2 libsasl2-modules libsasl2-modules-sql sasl2-bin libpam-mysql openssl getmail4 rkhunter binutils

and skip the configuration of postfix. We will install and configure Postfix and Dovecot further on in this guide.

Note: all of the URLs and package names are valid at the time of writing of this how to. Best practice is to check if there are new versions available.
The directory names for the downloaded and extracted packages need to be changed to ver version number of the respective packages downloaded eg: phamm-0.5.17 to phamm-0.5.xx

So let's get started with the rest.

Download some packages and openldap schema's we will need:

cd /usr/src

Get the latest version of phamm:

wget http://open.rhx.it/phamm/phamm-0.5.17.tar.gz

Unpack the archive:

tar xvzf phamm-0.5.17.tar.gz

 

Step2: Install and configure openldap

The configuration of OpenLDAP got a bit (more) complicated. cn=config is still used, but when installing the packages from the repositories only a skeleton configuration of openldap is installed.

You're not asked anymore to provide a password when the package is installed and issuing the "dpkg-reconfigure slapd" only resest openldap to the skeleton configuration. You will have to setup the openldap database, root dn and acl's your self using the root account (or sudo) in order to configure openldap.

Install openldap and ldap-utils:

aptitude install slapd ldap-utils

Change into the /etc/ldap directory:

cd /etc/ldap

Copy the phamm.schema and perversia.net.schema from the phamm package to the schema directory:

cp /usr/src/phamm-0.5.17/schema/phamm.schema /etc/ldap/schema.
cp /usr/src/phamm-0.5.17/schema/contrib/perversia.net.schema /etc/ldap/schema.

Get some more schema's we need.

cd schema
wget http://open.rhx.it/phamm/schema/ISPEnv2.schema
wget http://open.rhx.it/phamm/schema/amavis.schema
wget http://open.rhx.it/phamm/schema/pureftpd.schema
cd ../

Now we need to convert the schema's to ldif format.

Create a file called convert and paste the text below in to it.

vi convert

Contents of convert:

include         /etc/ldap/schema/core.schema
include         /etc/ldap/schema/cosine.schema
include         /etc/ldap/schema/nis.schema
include         /etc/ldap/schema/inetorgperson.schema
include         /etc/ldap/schema/phamm.schema
include         /etc/ldap/schema/ISPEnv2.schema
include         /etc/ldap/schema/amavis.schema
include         /etc/ldap/schema/pureftpd.schema
include         /etc/ldap/schema/perversia.net.schema

Now we will convert the shemas:

mkdir ldif
slaptest -f convert -F ldif

Now we change in to the directory that contains the converted schemas:

cd ldif/cn\=config/cn\=schema

The directory should contain the following files:

cn={0}core.ldif    cn={3}inetorgperson.ldif  cn={6}amavis.ldif
cn={1}cosine.ldif  cn={4}phamm.ldif          cn={7}pureftpd.ldif
cn={2}nis.ldif     cn={5}ISPEnv2.ldif        cn={8}perversia.ldif

We will need to edit the phamm, amavis, pureftpd, ISPEnv2 and perversia schemas. For each you need to do the following (example for the phamm schema):

Change:

dn: cn={4}phamm
objectClass: olcSchemaConfig
cn: {4}phamm

to

dn: cn=phamm,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: phamm

And delete:

structuralObjectClass: olcSchemaConfig
entryUUID: c27532b2-6a27-102e-88a5-e92372c94d84
creatorsName: cn=config
createTimestamp: 20091120135300Z
entryCSN: 20091120135300.238121Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20091120135300Z

So for each of these do repectively and make the changes like above:

vi cn\=\{4\}phamm.ldif
vi cn\=\{5\}ISPEnv2.ldif
vi cn\=\{6\}amavis.ldif
vi cn\=\{7\}pureftpd.ldif
vi cn\=\{8\}perversia.ldif

Note: the enry phamm in the example is ISPEnv2, amavis, pureftpd and pervisia in the  other ldif's.

Now we copy the ldifs to the /etc/ldap/schema directory (this is not needed, but is handy whenever the ldif's are needed).

cp cn\=\{4\}phamm.ldif /etc/ldap/schema/phamm.ldif
cp cn\=\{5\}ISPEnv2.ldif /etc/ldap/schema/ISPEnv2.ldif
cp cn\=\{6\}amavis.ldif /etc/ldap/schema/amavis.ldif
cp cn\=\{7\}pureftpd.ldif /etc/ldap/schema/pureftpd.ldif
cp cn\=\{8\}perversia.ldif /etc/ldap/schema/perversia.ldif

We can now delete the ldif directory since we don't need it anymore and also to avoid any confusion and change back to the /etc/ldapdirectory.

cd /etc/ldap
rm -R ldif

Now we add the schemas to openldap.

ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/inetorgperson.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/nis.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/phamm.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/ISPEnv2.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/amavis.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/pureftpd.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/perversia.ldif

We need load the database backend module and create the database.

Create a file called db.ldif and paste the text below in to it:

vi db.ldif

Content of the db.ldif:

# Load dynamic backend modules
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module
olcModulepath: /usr/lib/ldap
olcModuleload: {0}back_hdb

# Create the database
dn: olcDatabase={1}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {1}hdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example,dc=com
olcRootDN: cn=admin,dc=example,dc=com
olcRootPW: example
olcDbConfig: {0}set_cachesize 0 2097152 0
olcDbConfig: {1}set_lk_max_objects 1500
olcDbConfig: {2}set_lk_max_locks 1500
olcDbConfig: {3}set_lk_max_lockers 1500
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcDbIndex: cn,mail,givenname eq,subinitial
olcDbIndex: vd,delete eq,pres
olcDbIndex: accountActive,forwardActive eq,pres
olcDbIndex: smtpAuth eq,pres
olcDbIndex: sn,displayName eq,pres,sub
olcDbIndex: default sub
olcDbIndex: uid eq,pres
olcDbIndex: objectClass eq

Safe the file and issue the following command to load the module and initialize the database:

ldapadd -Y EXTERNAL -H ldapi:// -f db.ldif

Please note the olcRootPW: example which sets the RootPW to example. Replace example witch a password of your choice.

Now we create the base dn and the admin account for the openldap server as well as the o=hosting and phamm account.

Modify the text below to your needs and wants and generate a password for the admin account. The hash currently in this file sets the password to example. The crypt for the phamm account results in the password readonly.

To create crypt a password for the admin account  issue the following command:

slappasswd -h {MD5}

Type the wanted pasword twice and copy the result in to the text below.

Create the base.ldif:

vi base.ldif

Content of base.ldif.

dn: dc=example,dc=tld
objectClass: dcObject
objectclass: organization
o: example.tld
dc: example
description: My LDAP Root

dn: cn=admin,dc=example,dc=tld
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
userPassword: {MD5}Gnmk1g3mcY6OWzJuM4rlMw==
description: LDAP administrator

dn: o=hosting,dc=example,dc=tld
objectClass: organizationalUnit
objectClass: top
ou: domains
description: Hosting Organization

# Read only account
dn: cn=phamm,o=hosting,dc=example,dc=tld
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: phamm
userPassword: {MD5}M267sheb6qc0Ck8WIPOvQA==
description: Read only account

Load the base dn into the database with the following command:

ldapadd -Y EXTERNAL -H ldapi:// -f base.ldif

We modify the system acl's.

There are some acl's set in the openldap setup that prevent phpldapadmin to interface with the directory, so we will remove them now and set openldap to the default cn=admin,cn=config. From this moment on the openldap can be configured and manipulated as before, but no longer by issuing commands like ldapadd -Y EXTERNAL -H ldapi:// -f file but rather ldapadd -x -Y EXTERNAL -H ldapi:// -D cn=admin,cn=config -W -f file.

Create a file called config.ldif and paste the text below in to it. However do not forget to replace the olcRootPW hash with the hash you created above.

vi config.ldif

Content of config.ldif:

dn: cn=config
changetype: modify
delete: olcAuthzRegexp

dn: olcDatabase={-1}frontend,cn=config
changetype: modify
delete: olcAccess

dn: olcDatabase={0}config,cn=config
changetype: modify
delete: olcRootDN

dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootDN
olcRootDN: cn=admin,cn=config

dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {MD5}Gnmk1g3mcY6OWzJuM4rlMw==

dn: olcDatabase={0}config,cn=config
changetype: modify
delete: olcAccess

Load the config.ldif into the openldap server:

ldapadd -Y EXTERNAL -H ldapi:// -f config.ldif

Set the ldap acl's for phamm.

Create a file called acl.ldif and paste the text below into it:

vi acl.ldif

Content of acl.ldif:

dn: olcDatabase={1}hdb,cn=config
add: olcAccess
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=userPassword by dn="cn=admin,dc=example,dc=tld" write by self write by anonymous auth by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" write by set.expand="user/vd & [$1]" write
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=amavisBypassVirusChecks,quota,smtpAuth,accountActive by dn="cn=admin,dc=example,dc=tld" write by self read by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by set.expand="user/editAccounts & [TRUE]" write by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" read by set.expand="user/vd & [$1]" write
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=cn,sn,uid,forwardActive,vacationActive,vacationInfo,vacationStart,vacationEnd,vacationForward,amavisSpamTagLevel,amavisSpamTag2Level,amavisSpamKillLevel by dn="cn=admin,dc=example,dc=tld" write by self write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" write by set.expand="user/vd & [$1]" write
olcAccess: to dn.regex="^.*,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=editAccounts by dn="cn=admin,dc=example,dc=tld" write by self read by set.expand="user/editAccounts & [TRUE]" write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by * none
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=objectClass,entry by dn="cn=admin,dc=example,dc=tld" write by self write by anonymous read by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by set.expand="user/editAccounts & [TRUE]" write by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" read
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=amavisBypassSpamChecks,accountActive,delete by dn="cn=admin,dc=example,dc=tld" write by self read by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" write by set.expand="user/vd & [$1]" write
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=otherPath by dn="cn=admin,dc=example,dc=tld" write by anonymous read by self read by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by dn.exact,expand="cn=postmaster,vd=$1,o=hosting,dc=example,dc=tld" read by set.expand="user/vd & [$1]" write
olcAccess: to dn.regex=".+,vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=createMaildir,vdHome,mailbox,otherTransport by dn="cn=admin,dc=example,dc=tld" write by self read by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by set.expand="user/vd & [$1]" read
olcAccess: to dn.regex="^(.+,)?vd=([^,]+),o=hosting,dc=example,dc=tld$" attrs=vd by dn="cn=admin,dc=example,dc=tld" write by self write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by dn.exact,expand="cn=postmaster,vd=$2,o=hosting,dc=example,dc=tld" write by set.expand="user/vd & [$2]" write
olcAccess: to dn.regex="^(.+,)?vd=([^,]+),o=hosting,dc=example,dc=tld$" by dn="cn=admin,dc=example,dc=tld" write by self write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by set.expand="user/editAccounts & [FALSE]" read by dn.exact,expand="cn=postmaster,vd=$2,o=hosting,dc=example,dc=tld" write by set.expand="user/vd & [$2]" write
olcAccess: to dn.regex=".+,o=hosting,dc=example,dc=tld$" by dn="cn=admin,dc=example,dc=tld" write by self write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by anonymous auth
olcAccess: to dn.regex=".+,dc=tld$" by dn="cn=admin,dc=example,dc=tld" write by dn.exact="cn=phamm,o=hosting,dc=example,dc=tld" read by anonymous auth
olcAccess: to attrs=userPassword,shadowLastChange by dn="cn=admin,dc=example,dc=tld" write by anonymous auth by self write by * none
olcAccess: to dn.base="" by * read
olcAccess: to * by dn="cn=admin,dc=example,dc=tld" write by * read

Now load the acl into the openldap server:

ldapmodify -x -D cn=admin,cn=config -W -f acl.ldif

Now we have openldap configured and we can go to the next step.

Share this page:

3 Comment(s)