A common feature with mail environments is to use distribution groups that you could add and remove group members from. This is fairly common among organizations. For example, one might have hq@example.net and a list of members stored in LDAP. I wanted to have the ability to use mail distribution groups with my OpenLDAP infrastructure. LDAP group members could then easily be removed or added using ldapmodify or Apache Directory Studio.


First, we need to tell Postfix about our LDAP distribution group config.

  1. Open /etc/postfix/main.cf
  2. Edit the virtual_alias_maps line and put ldap:/etc/postfix/ldap/ldap-groups.cf after the aliases definition.
    virtual_alias_maps = ldap:/etc/postfix/ldap/ldap-aliases.cf,ldap:/etc/postfix/ldap/ldap-groups.cf

    Since the LDAP server is local I do not need TLS in ldap-groups.cf. The following is sufficient.


server_host = ldap://localhost
search_base = ou=Groups,ou=Mail,dc=example,dc=net
version = 3
bind = no
query_filter = mail=%s
result_attribute = mailGroupMember

The group attrributes can be loaded using postfix-book schema

An LDAP mail distribution group could look like this.

dn: mail=hq@example.email,ou=Groups,ou=Mail,dc=example,dc=net
objectClass: top
objectClass: organizationalPerson
objectClass: PostfixBookMailAccount
mail: hq@example.email
mailEnabled: TRUE
mailUidNumber: 5000
mailGidNumber: 5000
cn: hq
sn: group
description: hq@example.email distribution group
mailGroupMember: user1@example.email
mailGroupMember: user2@example.email
mailGroupMember: user3@example.email
mailGroupMember: user4@example.email
mailGroupMember: user5@example.email
mailGroupMember: user6@example.email

So now, when an email is sent to hq@example.email that email will land in every group member's Inbox. Each group member will be defined by the mailGroupMember attribute.

Once you have this configured it is a good idea to tail the logs and send a test mail to the group. If everything is setup correctly the mail logs will show the email delivered to all group members.

This is an example of some Postfix Header Checks which I use to help with mail spammers. This has been working as expected really well. One thing to note is that I did need to comment out #/^Precedence:.*bulk/ REJECT We do not accept your spam because it would prevent auto-reply emails from being sent such as vacation or out-of-office responses.

To load these header checks I am using the pcre format defined inside main.cf.

header_checks = pcre:/etc/postfix/header_checks

GitHub Gist


I started this small project a while back for my own use and over the course of several years the Infrastructure I had planned came together. I can't really recall when I started getting interested in all this mail stuff. I've always been fascinated by any kind of communications technology. I think it must have been a year or two before I transitioned into an Email/LDAP administrator role at a former enterprise company. In any case, despite the difficulties that Email technology undoubtedly brings I am quite proud of what I've accomplished. Everything was and still is a learning process. Here is some basic info.


Mail Layout

I'm not really a fan of the POP/POP3 protocols, so the Infrastructure uses IMAP by default. IMAP mail is synced and replicated between two Dovecot hosts using dsync. This works great alongside the ManageSieve protocol for server-side mail filtering. For SMTP there are three MX hosts. TLS on port 587 with required LDAP authentication.

As for the backend, from what I can tell it seems to be far more common to see some kind of SQL deployment used and not really Directory Services like LDAP. For instance, virtual users are commonly stored in a MySQL database and are managed using the PostfixAdmin web based interface. Rather than SQL, I opted for OpenLDAP to store all virtual information. This includes mailboxes, aliases, domains and mail distribution groups. I use OpenLDAP exclusively for most services. The great thing that I really like about this kind of setup is how well replication over TLS between the LDAP master and child hosts works. I know replication also exists with SQL servers like MySQL/MariaDB and PostgreSQL, but honestly I'm not a big fan of using SQL for mail services.

There is a shiny webmail instance powered by Roundcubemail that of course does use SQL for it's backend database. Roundcubemail is also tied into OpenLDAP, so users can use their LDAP credentials to login. Some useful plugins that are already available:

  1. Sieve mail rules
  2. Password
  3. PGP
  4. 2-Factor Auth using TOTP compatible app such as google-authenticator


The mail infrastructure incorporates OpenDKIM and SPF. I'm not going to go into the specifics of OpenDKIM and SPF here, but basically mail sent from any of the LDAP loaded domains will be validated as not spam by evaluating the authenticity of a particular message.

SpamAssassin is also used in conjunction with ClamAV - an open source standard for mail gateway scanning software. Here is an example showing valid OpenDKIM signing and SpamAssassin scanning from the mail headers.

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=archlinux.email;
    s=mail; t=1500851753;
X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on
X-Spam-Status: No, score=-1.1 required=5.0 tests=ALL_TRUSTED,DKIM_SIGNED,
    DKIM_VALID,DKIM_VALID_AU autolearn=ham autolearn_force=no version=3.4.1
Received: from [] (localhost)
    (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))
    (No client certificate requested)
    (Authenticated sender: tony)
    by mail.example.net (Postfix) with ESMTPSA id B2508202D4;
    Sun, 23 Jul 2017 16:15:52 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=archlinux.email;
    s=mail; t=1500851752;


I've written some useful tools to help manage LDAP records. For example, I have some simple interactive scripts to help with creating new mail accounts and mail distribution groups. Apache Directory Studio is also a great desktop application to manage Directory Services like LDAP.

For the most part mail forwarding is not too common within my Infrastructure. With Sieve deployed in my environment using the ManageSieve protocol - mail users are able to easily setup a redirect to their preferred email address. This all works fine, but I also wanted to have the ability to setup mail forwarding directly within OpenLDAP.

Today I went ahead and pushed a commit for postfix-book.schema to include a mailForwardingAddress attribute. The existing PostfixBookMailForward objectClass contains our mailForwardingAddress attribute, respectively.


Assuming the schema is loaded into your environment, we can now tell Postfix to use LDAP mail forwarding.


We can create ldap-forward.cf in /etc/postfix/ldap with something like

server_host = ldap://ldap.example.com/
search_base = ou=Mail,dc=example,dc=com
version = 3
bind = no
query_filter = (&(|(mailAlias=%s)(mail=%s))(objectClass=PostfixBookMailForward))
result_attribute = mailForwardingAddress

The query_filter will match a user's primary mail address or any mail aliases while the result_attribute is the forwarded email address.

The main.cf file should have the ldap-forward.cf file defined in virtual_alias_maps using proxy:ldap:/etc/postfix/ldap/ldap-forward.cf

virtual_alias_maps = ldap:/etc/postfix/ldap/ldap-aliases.cf,ldap:/etc/postfix/ldap/ldap-groups.cf proxy:ldap:/etc/postfix/ldap/ldap-forward.cf

To verify mail forwarding we can see that our forwarded email address does get returned when querying the primary or alias email address.

postmap -q me@example.email ldap:/etc/postfix/ldap/ldap-forward.cf


There are many ways to configure a virtual mail environment using postfix, but in this post I will describe the steps I took to configure postfix to work with OpenLDAP on a Linux host. The end goal was to utilize LDAP lookup tables for virtual domains, mailboxes and aliases.


First, instead of LDA I chose LMTP as my local delivery agent. As described in Dovecot's LMTP wiki, I changed virtual_transport = dovecot to = lmtp:unix:private/dovecot-lmtp

master.cf should also contain an external delivery method for LMTP similar to

dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/lmtp -f ${sender} -d ${recipient}

Next, I included the following 8 virtual lines in main.cf

virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_minimum_uid = 5000
virtual_mailbox_domains = ldap:/etc/postfix/ldap/ldap-virtual-domains.cf
virtual_mailbox_maps = ldap:/etc/postfix/ldap/ldap-vmailbox.cf
virtual_alias_maps = ldap:/etc/postfix/ldap/ldap-aliases.cf
virtual_mailbox_limit = 512000000
virtual_mailbox_base = /home/vmail/

Notice I have specified ldap: instead of the more common hash: database with the absolute path to 3 files needed for domains, mailboxes and aliases.



server_host = ldap://ldap.example.net/
search_base = ou=Domains,dc=example,dc=net
version = 3
bind = no
query_filter = (&(ObjectClass=dNSDomain)(dc=%s))
result_attribute = dc

Verify domain in LDAP queries successfully

postmap -q domain1.net ldap:/etc/postfix/ldap/ldap-virtual-domains.cf

will return domain1.net

If there is no result the domain may not be in LDAP. To add domains in LDAP see this page.



server_host = ldap://ldap.example.net/
search_base = ou=Mail,dc=example,dc=net
version = 3
bind = no
query_filter = (&(objectclass=inetOrgPerson)(mail=%s))
result_attribute = mail

Verify user mailbox in LDAP queries successfully

postmap -q johndoe@domain1.net ldap:/etc/postfix/ldap/ldap-vmailbox.cf

will return johndoe@domain1.net

If there is no result make sure the email address exists as the primary mail account and not a mail alias. See this page on how you can add new mail user records in LDAP.



server_host = ldap://ldap.example.net/
search_base = ou=Mail,dc=example,dc=net
version = 3
bind = no
query_filter = (&(objectclass=PostfixBookMailAccount)(mailAlias=%s))
result_attribute = mail

Verify user mail alias in LDAP queries successfully

postmap -q superjohn@domain2.me ldap:/etc/postfix/ldap/ldap-aliases.cf

will return johndoe@domain1.net

Notice when you query an alias the primary email address is returned and not the actual alias.

If all queries return expected results for mail domains, mailboxes and aliases we can proceed with configuring Dovecot to work with LDAP.