Postfix LDAP Howto v2.1

Last updated on 05th of August 2007

You can also check my newer page on Postfix MySQL over here


Full-fledged Postfix using LDAP HOWTO

Postfix, LDAP, IMAP, WebMail, Virus- and spamscanning/checking mail system

by Tom Scholten and authors of likewise documents


Special thanks to Richard from

Postfix OpenLDAP



This document tens to provide a description of how to set up a ‘full fledge’ mailserver using Postfix as it’s core. It will be extended using mailing list managers and webmail on the frontend side but will feature spam- and virus detection and avoidance software to handle the backend. There will also be a backend interface for mailhandling (web-based). All software used is available from the web and open source. The solution chosen will provide capabilities for multiple users and multiple domains, so you might use it for your small/average ISP solutions. This document can be used as a HOWTO document on FreeBSD or likewise systems, but will also apply to generic Unices like Solaris or Linuces (e.g. RedHat/Ubuntu/Debian/etc. There are other documents describing how to implement Postfix and LDAP, and this howto is based upon them, but is written as a ‘from-scratch’ to ‘fully-operational’ manual, as i found a great tutorial on Postfix-Mysql including a nice PHP admin frontend besides the great JAMM approach which has also a nice, but in java (server pages), admin tool. Plans for now are to deliver both a perl/cgi interface AND a PHP interface to administrate you’re very own Postfix-LDAP mail system.




This page describes how i installed and configured the solution on a FreeBSD 6.x system. There is no reason why this wouldn’t work on any other UN*X system, including all flavors of linux, *bsd, hpux, tru64, solaris or aix. Maybe on SCO but i don’t like either there OS nor their attitude. Ofcourse no responsibility or liability blah blah blah, to be continued




Installed software (on FreeBSD)

Today, 05th of August 2007, I started updating this howto by building a complete new mailsystem. It should be fairly up to date and fully usable for you! Installing using the order provided might ensure you have all the packages required (due to their dependencies).

  • lang/perl58 v5.8.8 (either as additional package during install or usinig ports)
  • mail/p5-Mail-ClamAV and security/p5-File-Scan-ClamAV
  • databases/p5-DBD-mysql50
  • net/openldap23-server v2.3.37 (check dependencies first, as v2.4 might become dependent instead of v2.3)
  • www/apache22 v2.2.4_2 (WITH_LDAP_MODULES=yes)
  • mail/postfix (using pcre, sasl, tls, mysql, openldap, vda and test) v2.54
  • lang/php5 v5.2.3 and lang/php5-extensions (at least enable openldap, mysql and imap)
  • databases/mysql50-server v5.0.45
  • mail/dovecot v1.0.2 (with LDAP and MYSQL enabled)
  • security/clamav v0.91.1
  • mail/mailscanner v4.61.7
  • net/phpldapadmin
  • mail/roundcube
  • Optionally you could install these packages:
  • procmail
  • phpldapadmin
  • phpmyadmin


Assumptions made for this howto

  • The example domain name will be ‘’
  • The virtual users (mail)directory store will live under /usr/virtual
  • The virtual user used is vmail (group vmail) uid/gid are both 2001
  • The scanner runs as vscan/vscan (2002/2002)

OpenLDAP configuration

First edit /usr/local/etc/openldap/slapd.conf, the example below does NOT have a safe password (secret), which you should create using slappasswd from the commandline. It asks you to type you’re password twice and then prints out the string to be used. You can use another (than SSHA, the default) algorythm, man slappasswd for more information!

Also in the example below there are no acl’s so anyone has access to you’re information, consider RTFM on (Open)LDAP to secure you’re LDAPtree some more./usr/local/etc/openldap/slapd.conf


# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.

pidfile /var/run/openldap/
argsfile /var/run/openldap/slapd.args

# Load dynamic backend modules:
modulepath /usr/local/libexec/openldap/
moduleload back_bdb

# Sample security restrictions
# Require integrity protection (prevent hijacking)
# Require 112-bit (3DES or better) encryption for updates
# Require 63-bit encryption for simple bind
# security ssf=1 update_ssf=112 simple_bind=64

# Sample access control policy:
# Root DSE: allow anyone to read it
# Subschema (sub)entry DSE: allow anyone to read it
# Other DSEs:
# Allow self write access
# Allow authenticated users read access
# Allow anonymous users to authenticate
# Directives needed to implement policy:
# access to dn.base=”” by * read
# access to dn.base=”cn=Subschema” by * read
# access to *
# by self write
# by users read
# by anonymous auth
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn. (e.g., “access to * by * read”)
# rootdn can always read and write EVERYTHING!

# BDB database definitions

# OpenLDAP configuration for

# Core schema’s delivered with OpenLDAP
include /usr/local/etc/openldap/schema/core.schema
include /usr/local/etc/openldap/schema/cosine.schema
include /usr/local/etc/openldap/schema/inetorgperson.schema
include /usr/local/etc/openldap/schema/nis.schema

# Mailserver schema used with postfix
include /usr/local/etc/openldap/schema/mailserver.schema


# ldbm database definitions

database bdb
suffix “dc=example,dc=org”

rootdn “cn=Manager,dc=example,dc=org”
rootpw secret

pidfile /var/run/openldap/

# The database directory MUST exist prior to running slapd AND
# should only be accessable by the slapd/tools. Mode 700 recommended.
directory /var/db/openldap-data

# Indices to maintain
index objectClass pres,eq
index mail,cn eq,sub

# logging
loglevel 256

access to attrs=userPassword
by self write
by anonymous auth
by peername.ip= read
by dn=”cn=dovecot,dc=example,dc=org” read
by * none

access to *
by dn=”cn=postfix,dc=example,dc=org” read
by dn=”cn=courier,dc=example,dc=org” read
by peername.ip= read
by * read


OpenLDAP scheme for mailserver

Next, create the LDAPscheme to be used for our mailserver in /usr/local/etc/openldap/schemaDownload mailserver.schema

Ready, Aim, OpenLDAP

Now start up you’re OpenLDAP server either by hand or set slapd_enable=”YES” in /etc/rc.conf and use /usr/local/etc/rc.d/slapd start and verify that slapd is running.If it’s running were ready to fill …

Initial LDIF

OpenLDAP tree layout and initial ldif. You can load this to your serverusing

[code] cat /home/user/ldap/initial.ldif | ldapadd -x -D “cn=Manager,dc=example,dc=org” -W



# example
dn: dc=example, dc=org
objectClass: top
objectClass: organization
objectClass: dcObject
o: example
dc: example

# MAnager
dn: cn=Manager,dc=example,dc=org
objectClass: top
objectClass: organizationalRole
cn: Manager

# mail, example
dn: dc=mail, dc=example, dc=org
objectClass: top
objectClass: organizationalunit
objectClass: dcObject
ou: mail
dc: mail

dn: cn=postfix,dc=example,dc=org
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole
userPassword:: secret
cn: postfix

dn: cn=dovecot,dc=example,dc=org
objectClass: top
objectClass: simpleSecurityObject
objectClass: organizationalRole
userPassword:: secret
cn: dovecot

#, mail, example
dn:, dc=mail, dc=example,dc=org
accountActive: TRUE
editPostmasters: TRUE
editAccounts: TRUE
objectClass: top
objectClass: mailDomain
delete: FALSE
lastChange: 111
postfixTransport: virtual:

#, mail, example
dn:, dc=mail, dc=example,dc=org
accountActive: TRUE
editPostmasters: TRUE
editAccounts: TRUE
objectClass: top
objectClass: mailDomain

delete: FALSE
lastChange: 111
postfixTransport: virtual:

#, mail, example
dn:, dc=mail, dc=example,dc=org
accountActive: TRUE
editPostmasters: TRUE
editAccounts: TRUE
objectClass: top
objectClass: mailDomain
delete: FALSE
lastChange: 111
postfixTransport: virtual:

#, mail, example
dn:, dc=mail, dc=example,dc=org
accountActive: TRUE
editPostmasters: TRUE
editAccounts: TRUE
objectClass: top
objectClass: mailDomain
delete: FALSE
lastChange: 111
postfixTransport: virtual:


Apache webserver

Add the following lines to your/usr/local/etc/apache22/httpd.conf and make sure ‘DirectoryIndex’ also contains index.php[code]AddType application/x-httpd-php .phpAddType application/x-httpd-php-source .phps[/code]


Since not all people are keen on adding/removing/changing users by hand using scripts there is also ‘phpldapadmin’ in the portstree that makes things a little easier, after installing the port (assuming apache+php are up and running!!!) add the following lines to a secure (https) instance of your webserver /usr/local/etc/apache22/extra/httpd-ssl.conf


Alias /phpldapadmin “/usr/local/www/phpldapadmin/”

AllowOverride AuthConfig

Allow from all


And put a .htaccess file in /usr/local/www/phpldapadmin containing


AuthUserFile /usr/local/etc/apache22/htpasswd.admin

AuthName “/admin auth”

AuthType Basicrequire valid-user[/code]

Next create the htpasswd file using “htpasswd -cm /usr/local/etc/apache22/htpasswd.admin {username}” and any further users leaving the -c out.Next edit the /usr/local/www/phpldapadmin/config/config.php and change/add the following lines







Restart your webserver and test if everything works The reason i use plain htpasswd authentication instead of ldap authentication (that is possible by using the example .htaccess below) is that any FU in your ldap would knock yourself out of it.Example .htaccess using ldap authentication

[code]AuthName “/auth required”

AuthType BasicAuthLDAPURL ldap://localhost/dc=employees,dc=example,dc=org?name??

require valid-user[/code]

ClamAV, virusscanner

You might want to run it by enabling clamav_clamd_enable and clamav_freshclam_enable in /etc/rc.conf

DOVECOT imap server

Copy /usr/local/etc/dovecot-example to dovecot.conf and adjust the
following lines

protocols = imaps pop3s
disable_plaintext_auth = no
syslog_facility = mail
ssl_disable = no
ssl_cert_file = /etc/ssl/certs/mailserver.pem
ssl_key_file = /etc/ssl/certs/mailserver.pem
login_user = dovecot
mail_location = maildir:/usr/virtual/%d/%n/Maildir
first_valid_uid = 500
last_valid_uid = 0
last_valid_gid = 0
valid_chroot_dirs = /usr/virtual
passdb ldap {
args = /usr/local/etc/dovecot-ldap.conf
userdb ldap {
args = /usr/local/etc/dovecot-ldap.conf
} [/code]
Copy /usr/local/etc/dovecot-ldap-example.conf to dovecot-ldap.conf and
adjust the following lines

hosts = localhost
tls = no
auth_bind = no
ldap_version = 3
base = dc=mail,dc=example,dc=org
user_filter =
user_attrs = mail,homeDirectory,,,,
pass_attrs = mail=user,userPassword=password
pass_attrs = mail,userPassword
pass_filter = (&(objectClass=MailAccount)(mail=%u))
default_pass_scheme = CRYPT
user_global_uid = 5000
user_global_gid = 5000[/code]

Next start dovecot using /usr/local/etc/rc.d/dovecot start

RoundCube Webmail

Make sure mysql is started (/usr/local/etc/rc.d/mysql_server start) next issue

mysql -uroot -p (depending on if you already secured your database)

[code]> create database roundcube;

> grant all on roundcube.* to roundcube@localhost identified by ’secret’;


Issue cat /usr/ports/mail/roundcube/work/roundcube*/SQL/mysql5.initial.sql | mysql -u roundcube -psecret roundcube and add the following lines to your /usr/local/etc/apache22/extra/httpd-ssl.conf

[code]Alias /webmail /usr/local/www/roundcube>

<Directory “/usr/local/www/roundcube”>

Allow from all

</Directory> [/code]

OpenLDAP scripts

Using the script provide below we can add users, according to the domain setup shown below using the following commands example· ./ vivian “Secret123? mailbox | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ alexander “Dog=Cr@zy” mailbox | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ roland_dg “a1zihw” mailbox | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ postmaster, alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ webmaster alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ “*” alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w· ./ ian “vivian” mailbox | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ postmaster alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ webmaster “w3bs1t3? mailbox | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w· ./ “*” alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w· ./ postmaster alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secret· ./ webmaster alias | ldapadd -x -D ‘cn=Manager,dc=example,dc=org’ -w secretAnd so, we should have created a tree looking like this, with receiving all ‘non-existing-mailbox’ mail from and all mail from ’’ delivered to (so you can email and still reach ian, whom (ofcourse) should have procmail or so set up to warn people about his changed domain name, but that’s outside the scope of this document). Also mind that is delivered to BOTH alexander AND vivian. The add-script just makes to ‘maildrop’s in the LDAP tree!


First of all, copy all .sample files to their respective realnames (and make changes if you like) in /usr/local/etc/MailScanner and subdirectories. Do the same for /usr/local/share/MailScanner/reports/en/ (or other languages). To enable ‘autofinding’ clamav, also issue acp /usr/local/libexec/MailScanner/clamav-wrapper.sample /usr/local/libexec/MailScanner/clamav-wrapper

I recommend using clamavmodule however, this will save you overhead and time of seperate clamav processes on your precious system!

Adjust /usr/local/etc/MailScanner/MailScanner.conf with at least the following lines.


%org-name% = YourOrg
%org-long-name% = Your Full Organisation Name
%web-site% =
Run As User = postfix
Run As Group = postfix
Incoming Queue Dir = /var/spool/postfix/hold
Outgoing Queue Dir = /var/spool/postfix/incoming
MTA = postfix
Quarantine User = postfix
Quarantine Group = www
Deliver Disinfected Files = yes
Quarantine Whole Message = yes
Information Header Value = See you’re providers webpage or for more information
Notify Senders = no
Required SpamAssassin Score = 3
High SpamAssassin Score = 5
Depending on your choice you might change :
#High Scoring Spam Actions = deliver header “X-Spam-Status: Yes”
High Scoring Spam Actions = store

Monitors for ClamAV Updates = /var/db/clamav/*.inc/* /var/db/clamav/*.cvd


MailWatch (MailScanner front-end)

Download the package from and untar in /tmp and change to the directory.

[code]$ mysql -uroot -p < create.sql[/code]

[code]$ mysql -uroot -p [/code]

[mysql]mysql> GRANT ALL ON mailscanner.* TO mailwatch@localhost IDENTIFIED BY ‘secret’;

mysql> GRANT FILE ON *.* TO mailwatch@localhost IDENTIFIED BY ‘secret’;


mysql > use mailscanner;

mysql > INSERT INTO users (username, password, fullname, type) VALUES (‘<username’>,md5(‘<password>’),'<name>’,’A’);[/mysql]

Edit the file and change the database configuration

[code]my($db_user) = ‘mailwatch’;

my($db_pass) = ‘secret’;[/code]

Next copy this file to the MailScanner directories : cp /usr/local/lib/MailScanner/MailScanner/CustomFunctions/Move the mailscanner directory to your webroot and “chown www:www” it (i.e. /usr/local/www/mailscanner) and add the following lines to your apache configuration

[code]Alias /mailscanner /usr/local/www/mailscanner

Directory “/usr/local/www/mailscanner”

Allow from all


Next restart your mailscanner (/usr/local/etc/rc.d/mailscanner restart) and watch the /var/log/maillog closely. If no obvious errors or warnings occur try to send yourself an email. If it all works (email received) just go ahead and browse to MailWatch to see some stats!

Configure postfix

Changes to postfix’, also change other settings to customize you’re requirements. Setting ’soft_bounce=yes’ for testing purposes while starting out using you’re new mailserver would be a wise decision!We also need to change some things regarding to sending Postfix’ incoming mail through MailScanner first in

hash_queue_depth = 1
hash_queue_names = incoming,hold,deferred,defer
inet_protocols = all
header_checks = regexp:/usr/local/etc/postfix/header_checks
And add the following line to /usr/local/etc/postfix/header_checks
/^Received:/ HOLD
Further, change your to contain the following ldap-related stuff.
queue_directory = /var/spool/postfix
command_directory = /usr/local/sbin
daemon_directory = /usr/local/libexec/postfix
mail_owner = postfix
myhostname =
mydomain =
myorigin = $mydomain
local_recipient_maps =
unknown_local_recipient_reject_code = 550

debug_peer_level = 2
debugger_command =
xxgdb $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/local/sbin/sendmail
newaliases_path = /usr/local/bin/newaliases
mailq_path = /usr/local/bin/mailq
setgid_group = maildrop
html_directory = no
manpage_directory = /usr/local/man
sample_directory = /usr/local/etc/postfix
readme_directory = no
domains_server_host =
domains_search_base = dc=mail,dc=example,dc=org
domains_query_filter =
domains_result_attribute = postfixTransport
domains_bind = no
domains_scope = one
aliases_server_host =
aliases_search_base = dc=mail,dc=example,dc=org
aliases_query_filter =
aliases_result_attribute = maildrop
aliases_bind = no
aliasalternates_server_host =
aliasalternates_search_base = dc=mail,dc=example,dc=org
aliasalternates_query_filter =
aliasalternates_result_attribute = maildrop
aliasalternates_bind = no
accounts_server_host =
accounts_search_base = dc=mail,dc=example,dc=org
accounts_query_filter =
accounts_result_attribute = mailbox
accounts_bind = no

accountsmap_server_host =
accountsmap_search_base = dc=mail,dc=example,dc=org
accountsmap_query_filter =
accountsmap_result_attribute = mail
accountsmap_bind = no
accountalternates_server_host =
accountalternates_search_base = dc=mail,dc=example,dc=org
accountalternates_query_filter =
accountalternates_result_attribute = mail
accountalternates_bind = no
transport_maps = ldap:domains
masquerade_domains = ldap:domains
virtual_maps = ldap:accountsmap, ldap:aliases, ldap:accountalternates,
virtual_transport = local
virtual_mailbox_base = /usr/virtual
virtual_mailbox_maps = ldap:accounts
virtual_mailbox_domains = ldap:domains
virtual_minimum_uid = 5000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
local_alias_maps = hash:/etc/mail/aliases
local_transport = local
mailbox_command = /usr/local/bin/procmail
mydestination =, localhost
relay_domains = localhost
mynetworks = localhost,

owner_request_special = no
recipient_delimiter = +
unknown_local_recipient_reject_code = 550
smtpd_client_restrictions = check_client_access
hash:/usr/local/etc/postfix/access, permit
smtpd_sender_restrictions = hash:/usr/local/etc/postfix/access
header_checks = regexp:/usr/local/etc/postfix/header_checks

hash_queue_depth = 1
hash_queue_names = incoming,hold,deferred,defer

inet_protocols = all
debug_peer_level = 9


And finally some code to protect your drive from flooding

[code] # A maximum limit of a mailbox
virtual_mailbox_limit = 1000000

# Limits only INBOX part (usefull when
# using when you have IMAP users)
virtual_mailbox_limit_inbox = yes [/code]

You may want to add a few dnsbl statements to limit the amount of unwanted
mail in your using

[code]smtpd_recipient_restrictions =


Starting you’re mailserver

Next again edit /etc/rc.conf to disable sendmail and enable postfix Insert

  • sendmail_enable=”NONE”

Comment out/remove

  • sendmail_enable=”YES”
  • sendmail_flags=”-bd”
  • sendmail_pidfile=”/var/spool/postfix/pid/”
  • sendmail_outbound_enable=”NO”
  • sendmail_submit_enable=”NO”
  • sendmail_msp_queue_enable=”NO”Now would be a great time to start postfix, but before you do touch /var/log/maillog and open a second terminal, screen or whatever and do a tail -f /var/log/maillog there (as well as maybe a tail on /var/log/messages) to see what’s going on.First start mailscanner (/usr/local/etc/rc.d/mailscanner start) but remember to add mailscanner_enable=”YES” to your /etc/rc.conf watch your logging for a while before proceeding with the next step.Now you’re ready to start postfix simply by typing postfix start

    Verify mail sending and receiving

    Verify that you can send mail by sending yourself mail. If using an which is an alias (pointing outside one of the domains for wich your new postfix server receives mail) please check you’re headers and confirm their ok. If receiving on a mailbox address (at your new postfix server), look in /usr/virtual and confirm you have a new directory named (depending on the recipient). Below that directory your mailbox name (as a directory) should emerge, containing various maildir files. Besides checking that your mail was received also check the headers!Note that users of a mailbox should have received at least one email to have their directory (and maildir files) in place When you create a new mailbox (not alias), you should send the new user a ‘welcome’ mail of some kind to let postfix create their ‘homedirectory’ in /usr/virtual. When omitted the user will receive an error when checking mail (either via imap/pop or when using webmail)

    Documents and resources used

    Besides RTFM on various packages used to install a number of special resources will be named below the packages linklist; (in no particular order)

  • Postfix, MTA used
  • Spam tagging software
  • LDAP software
  • Webmail software
  • Mailinglist software (when done, you could add mailinglists, not captured in this document)
  • ClamAV Antivirus software
  • Short for [A] [MA]il [VI]rus [S]canner
  • Apache webserver for webmail and admin (php) interface
  • The admin interface was build using phpMost usefull to produce this document was : with his pdf and schema ((mirrored here) pdf and schema)

    Others resources used

    • everything else seems to be based upon this
    • detailed enough for me

    Tom Scholten is consultant with Snow B.V., a Dutch Technical Consultancy Company supplying specialists in the fields of Storage, Networking and Unix

    Snow B.V. FreeBSD

This post is also available in: Engels