Postfix AUTH using SASL and LDAPDB

In a previous article, I described how to configure postfix to realize user authentication using SASL mechanisms. In this article, I want to go a step forward and validate the user's credential against the LDAP directory directly, and not through saslauthd and pam.

So, the goal of this article is to describe the configuration of cyrus-sasl to use the ldapdb driver.

Postfix will call the cyrus-sasl library (and its modules) to connect to Slapd and verify the user's credential. This method avoids having saslauthd running in the background.

Before starting

In order to bind to the ldap directory using the ldapdb driver, cyrus-sasl requires a proxy user. This user must be configured in the LDAP directory with the authz-to attribute, and some additionnal rules in slapd.conf. The whole process is detailled in the OpenLDAP Chapter.

You need to keep in mind that the default configuration of Postfix on Debian executes most of the processes in a chroot. Thus, postfix can only access the binaries and libraries located under queue_directory (queue_directory = /var/run/postfix, by default). This is very important because postfix won't be able to access the ldapdb driver by default, since this one is located under /usr/lib/sasl2/.

Postfix basis

The use of SASL authentication is described on the official website in this howto. Mainly, postfix needs a few configuration parameters and a sasl library.

So, let's start with the usual:

# aptitude install postfix postfix-ldap libsasl2-2 libsasl2-modules-ldap sasl2-bin

Work with the Chroot

If you take a look to Postfix's master.cf file, you will notice the following:

# head /etc/postfix/master.cf -n 20


#
# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet   n       -       -       -       -       smtpd

The column chroot, if not set to n, will chroot the corresponding service into the queue_directory directory. On my configuration, this is defined in main.cf as:

# grep queue_directory /etc/postfix/main.cf

# The queue_directory specifies the location of the Postfix queue.
queue_directory = /var/spool/postfix

In /var/spool/postfix we have the complete tree of folders and libraries that postfix needs to work it its chroot.

root@samchiel:/var/spool/postfix# ls -al
total 80
drwxr-xr-x 20 root    root     4096 May 18 15:00 . 
drwxr-xr-x  8 root    root     4096 May 18 13:36 .. 
drwx------  2 postfix root     4096 May 18 13:36 active 
drwx------  2 postfix root     4096 May 18 13:36 bounce 
drwx------  2 postfix root     4096 May 18 13:36 corrupt 
drwx------  2 postfix root     4096 May 18 13:36 defer 
drwx------  2 postfix root     4096 May 18 13:36 deferred 
drwxr-xr-x  2 root    root     4096 Sep 18  2009 dev 
drwxr-xr-x  2 root    root     4096 May 20 17:14 etc 
drwx------  2 postfix root     4096 May 18 13:36 flush 
drwx------  2 postfix root     4096 May 18 15:00 hold 
drwx------  2 postfix root     4096 May 18 13:36 incoming 
drwxr-xr-x  2 root    root     4096 May 20 17:14 lib 
drwx-wx--T  2 postfix postdrop 4096 May 18 13:36 maildrop 
drwxr-xr-x  2 root    root     4096 May 18 15:00 pid 
drwx------  2 postfix root     4096 May 20 17:14 private 
drwx--s---  2 postfix postdrop 4096 May 20 17:14 public 
drwx------  2 postfix root     4096 May 18 13:36 saved 
drwx------  2 postfix root     4096 May 18 15:00 trace 
drwxr-xr-x  3 root    root     4096 May 18 13:36 usr 

There is a usr folder in which we need to copy the sasl2 library installed on our system.

# cp /usr/lib/sasl2/* /var/spool/postfix/usr/lib/sasl2/

# ls -l /var/spool/postfix/usr/lib/sasl2/
total 736
-rw-r--r-- 1 root root 13476 mai 25 08:03 libanonymous.a
-rw-r--r-- 1 root root   855 mai 25 08:03 libanonymous.la
-rw-r--r-- 1 root root 13016 mai 25 08:03 libanonymous.so
-rw-r--r-- 1 root root 13016 mai 25 08:03 libanonymous.so.2
-rw-r--r-- 1 root root 13016 mai 25 08:03 libanonymous.so.2.0.22
-rw-r--r-- 1 root root 15814 mai 25 08:03 libcrammd5.a
-rw-r--r-- 1 root root   841 mai 25 08:03 libcrammd5.la
-rw-r--r-- 1 root root 15352 mai 25 08:03 libcrammd5.so
-rw-r--r-- 1 root root 15352 mai 25 08:03 libcrammd5.so.2
-rw-r--r-- 1 root root 15352 mai 25 08:03 libcrammd5.so.2.0.22
-rw-r--r-- 1 root root 46420 mai 25 08:03 libdigestmd5.a
-rw-r--r-- 1 root root   864 mai 25 08:03 libdigestmd5.la
-rw-r--r-- 1 root root 43500 mai 25 08:03 libdigestmd5.so
-rw-r--r-- 1 root root 43500 mai 25 08:03 libdigestmd5.so.2
-rw-r--r-- 1 root root 43500 mai 25 08:03 libdigestmd5.so.2.0.22
-rw-r--r-- 1 root root 13924 mai 25 08:03 libldapdb.a
-rw-r--r-- 1 root root   848 mai 25 08:03 libldapdb.la
-rw-r--r-- 1 root root 14480 mai 25 08:03 libldapdb.so
-rw-r--r-- 1 root root 14480 mai 25 08:03 libldapdb.so.2
-rw-r--r-- 1 root root 14480 mai 25 08:03 libldapdb.so.2.0.22
-rw-r--r-- 1 root root 13650 mai 25 08:03 liblogin.a
-rw-r--r-- 1 root root   835 mai 25 08:03 liblogin.la
-rw-r--r-- 1 root root 13460 mai 25 08:03 liblogin.so
-rw-r--r-- 1 root root 13460 mai 25 08:03 liblogin.so.2
-rw-r--r-- 1 root root 13460 mai 25 08:03 liblogin.so.2.0.22
-rw-r--r-- 1 root root 29076 mai 25 08:03 libntlm.a
-rw-r--r-- 1 root root   829 mai 25 08:03 libntlm.la
-rw-r--r-- 1 root root 28532 mai 25 08:03 libntlm.so
-rw-r--r-- 1 root root 28532 mai 25 08:03 libntlm.so.2
-rw-r--r-- 1 root root 28532 mai 25 08:03 libntlm.so.2.0.22
-rw-r--r-- 1 root root 13970 mai 25 08:03 libplain.a
-rw-r--r-- 1 root root   835 mai 25 08:03 libplain.la
-rw-r--r-- 1 root root 14036 mai 25 08:03 libplain.so
-rw-r--r-- 1 root root 14036 mai 25 08:03 libplain.so.2
-rw-r--r-- 1 root root 14036 mai 25 08:03 libplain.so.2.0.22
-rw-r--r-- 1 root root 21710 mai 25 08:03 libsasldb.a
-rw-r--r-- 1 root root   866 mai 25 08:03 libsasldb.la
-rw-r--r-- 1 root root 18080 mai 25 08:03 libsasldb.so
-rw-r--r-- 1 root root 18080 mai 25 08:03 libsasldb.so.2
-rw-r--r-- 1 root root 18080 mai 25 08:03 libsasldb.so.2.0.22

Configure LDAPDB for Postfix

The LDAPDB driver

Postfix can query the LDAP directory using the ldapdb driver (the libldapdb.so file above). To do so, Postfix needs a configuration file located in /etc/postfix/sasl/smtpd.conf. This path is not configurable, only the name of the file is (but smtpd is the default so we keep it).

The content of this file is very simple:

# cat smtpd.conf

pwcheck_method: auxprop
auxprop_plugin: ldapdb
mech_list: DIGEST-MD5 PLAIN LOGIN
ldapdb_uri: ldap://localhost
ldapdb_id: postfix
ldapdb_pw: highsecuritypassword
ldapdb_mech: DIGEST-MD5
  • pwcheck_method: define the type of driver to use
  • auxprop_plugin: define the name of the sasl driver to use
  • mech_list: this is the list of SASL mechanisms exposed to the client (ie, the end user)
  • ldapdb_uri: the address of the LDAP server
  • ldapdb_id: the name (UID attribute) of the proxy user in the LDAP directory
  • ldapdb_pw: its password, in clear text
  • ldapdb_mech: the authentication mechanism used by postfix to authenticate on the LDAP directory

This configuration requires that the user 'postfix' has the proxy permission for the users of the email server. We can confirm that with the LDAPWHOAMI command, as follow:

# ldapwhoami  -Y DIGEST-MD5 -U postfix  -H ldap://localhost -R linuxwall.info -X u:julien

SASL/DIGEST-MD5 authentication started
Please enter your password:
SASL username: u:julien
SASL SSF: 128
SASL data security layer installed.
dn:cn=julien vehent,ou=people,dc=linuxwall,dc=info

This command confirms that 'postfix' can take the identity of 'julien' and act on its behalf. More information about that in the LDAP article.

Activate the SASL Authentication

Postfix's main configuration file, the main.cf needs some parameters in order to activate SASL Authentication.

# grep smtpd_sasl main.cf

smtpd_sasl_auth_enable = yes
smtpd_sasl_type = cyrus
smtpd_sasl_path = smtpd
smtpd_sasl_security_options = noanonymous
smtpd_sasl_authenticated_header = yes
  • smtpd_sasl_auth_enable: activate the SASL authentication
  • smtpd_sasl_type: we use cyrus-sasl library, so sasl type is cyrus, but it could also be dovecot
  • smtpd_sasl_path: the name of the configuration file. Postfix will look for a file called /etc/postfix/sasl/<smtpd_sasl_path>.conf. So by setting this to smtpd (which is the default value), the smtpd.conf file set above will be accessed.
  • smtpd_sasl_security_options: several parameters can be set here to control the type of sasl authentication that are valid. We simply remove the anonymous authentication.
  • smtpd_sasl_authenticated_header: add a header in the sent emails with the name of the authenticated user who sent the email.

During my test, I tried to set the smtpd_sasl_local_domain directive to my local domain. But this directive breaks the SASL authentication, and therefore MUST NOT BE SET AND SHOULD BE LEFT BLANK. (I put it in big and red because it drove me nuts for a few days). See the thread here.

Change the restriction

Now that postfix has the sasl parameter, we need to tell it to accept email submission from authenticated user, even if they are not part of the trusted networks. For that, we edit the smtpd_recipient_restrictions as follow:

smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, check_policy_service inet:127.0.0.1:60000

The permit_sasl_authenticated directive will allow valid users to send emails from outside the network. The other parameters (such as the policy) are used for other purposes not described here.

Restart and test

Configuration is done, now let's just restart and test the behavior of postfix:

# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

# smtptest -a julien -m digest-md5 localhost -r linuxwall.info

S: 220 smtp2.linuxwall.info
C: EHLO example.com
S: 250-ptichoun.linuxwall.info
S: 250-PIPELINING
S: 250-SIZE 104857600
S: 250-VRFY
S: 250-ETRN
S: 250-STARTTLS
S: 250-AUTH PLAIN DIGEST-MD5 LOGIN
S: 250-ENHANCEDSTATUSCODES
S: 250-8BITMIME
S: 250 DSN
C: AUTH DIGEST-MD5
S: 334 /fdwXdWcj+VgWw6rH3Ek22xY9VmyX6H5mMkN79eeZHWBhzs5uswC0LSvhMhbXi8dfdwXdWcj+VgWw6rH3Ek22xY9VmyX6H5mMkN79eeZHWBhzsuswC0LSvhMhbXi8d/fdwXdWcj+VgWw6rH3Ek22xY9VmyX6H5mMkN79eeZHWBhzs5uswC0LSvhMhbXi8d
Please enter your password:
C: vxd1eCjfoadMDsIgQrAAWjfvvVkJcYgvLbcpF+wo2eah8WHTP6tE6bJiLodHv8Itvxd1eCjfoadMDsIgQrAAWjfvvVkJcYgvLbcpF+wo2eah8WHTP6tE6bJiLodHv8Itvxd1eCjfoadMDsIgQrAAWjfvvVkJcYgvLbcpF+wo2eah8WHTP6tE6bJiLodHv8Itvxd1eCjfoadMDsIgQrAAWjfvvVkJcYgvLbcpF+wo2eah8WHTP6tE6bJiLodHv8I
S: 334 i8DzMdyg8vEw9WFEW47A4unQHD8PzLGM4Wvrksw1t7IOF4vJX9KzRqjwfkI9lNwn
C:
S: 235 2.7.0 Authentication successful
Authenticated.
Security strength factor: 0
quit
221 2.0.0 Bye
Connection closed.

If we check the logs:

# tail /var/log/auth.log

May 28 11:21:07 ptitchoun smtptest: DIGEST-MD5 client step 2
May 28 11:21:07 ptitchoun postfix/smtpd[15499]: DIGEST-MD5 client step 2
May 28 11:21:07 ptitchoun postfix/smtpd[15499]: DIGEST-MD5 client step 2
May 28 11:21:07 ptitchoun postfix/smtpd[15499]: DIGEST-MD5 client step 3
May 28 11:21:07 ptitchoun smtptest: DIGEST-MD5 client step 3
# tail /var/log/slapd.log  -n 500 |grep conn=34

May 28 11:21:07 ptitchoun slapd[17048]: conn=34 fd=20 ACCEPT from IP=127.0.0.1:34519 (IP=127.0.0.1:389)
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=0 do_bind
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=0 BIND dn="" method=163
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Debug: DIGEST-MD5 server step 1
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=0 RESULT tag=97 err=14 text=SASL(0): successful result:
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=1 do_bind
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=1 BIND dn="" method=163
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Debug: DIGEST-MD5 server step 2
May 28 11:21:07 ptitchoun slapd[17048]: SASL Canonicalize [conn=34]: authcid="postfix"
May 28 11:21:07 ptitchoun slapd[17048]: send_ldap_result: conn=34 op=1 p=3
May 28 11:21:07 ptitchoun slapd[17048]: SASL Canonicalize [conn=34]: slapAuthcDN="cn=postfix administrator,ou=infrastructure,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Error: unable to open Berkeley db /etc/sasldb2: Permission denied
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Error: unable to open Berkeley db /etc/sasldb2: Permission denied
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Error: unable to open Berkeley db /etc/sasldb2: Permission denied
May 28 11:21:07 ptitchoun slapd[17048]: SASL [conn=34] Error: unable to open Berkeley db /etc/sasldb2: Permission denied
May 28 11:21:07 ptitchoun slapd[17048]: send_ldap_result: conn=34 op=1 p=3
May 28 11:21:07 ptitchoun slapd[17048]: SASL Canonicalize [conn=34]: authzid="postfix"
May 28 11:21:07 ptitchoun slapd[17048]: SASL proxy authorize [conn=34]: authcid="postfix" authzid="postfix"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=1 BIND authcid="postfix" authzid="postfix"
May 28 11:21:07 ptitchoun slapd[17048]: SASL Authorize [conn=34]:  proxy authorization allowed authzDN=""
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=1 BIND dn="cn=postfix administrator,ou=infrastructure,dc=linuxwall,dc=info" mech=DIGEST-MD5 sasl_ssf=128 ssf=128
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=1 RESULT tag=97 err=0 text=
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=2 do_extended
May 28 11:21:07 ptitchoun slapd[17048]: send_ldap_result: conn=34 op=2 p=3
May 28 11:21:07 ptitchoun slapd[17048]: parseProxyAuthz: conn=34 "cn=julien vehent,ou=people,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=2 PROXYAUTHZ dn="cn=julien vehent,ou=people,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=2 EXT oid=1.3.6.1.4.1.4203.1.11.3
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=2 WHOAMI
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=2 RESULT oid= err=0 text=
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=3 do_search
May 28 11:21:07 ptitchoun slapd[17048]: parseProxyAuthz: conn=34 "cn=julien vehent,ou=people,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=3 PROXYAUTHZ dn="cn=julien vehent,ou=people,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=3 SRCH base="cn=julien vehent,ou=people,dc=linuxwall,dc=info" scope=0 deref=0 f
ilter="(objectClass=*)"
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=3 SRCH attr=userPassword cmusaslsecretDIGEST-MD5
May 28 11:21:07 ptitchoun slapd[17048]: ==> limits_get: conn=34 op=3 dn="cn=julien vehent,ou=people,dc=linuxwall,dc=info"
May 28 11:21:07 ptitchoun slapd[17048]: send_ldap_result: conn=34 op=3 p=3
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=3 SEARCH RESULT tag=101 err=0 nentries=1 text=
May 28 11:21:07 ptitchoun slapd[17048]: connection_closing: readying conn=34 sd=20 for close
May 28 11:21:07 ptitchoun slapd[17048]: connection_close: deferring conn=34 sd=20
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=4 do_unbind
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 op=4 UNBIND
May 28 11:21:07 ptitchoun slapd[17048]: connection_resched: attempting closing conn=34 sd=20
May 28 11:21:07 ptitchoun slapd[17048]: connection_close: deferring conn=34 sd=20
May 28 11:21:07 ptitchoun slapd[17048]: connection_resched: attempting closing conn=34 sd=20
May 28 11:21:07 ptitchoun slapd[17048]: connection_close: conn=34 sd=20
May 28 11:21:07 ptitchoun slapd[17048]: conn=34 fd=20 closed

Proxy authentication works. We are all set :)

en/ressources/dossiers/postfix/sasl_ldapdb.txt · Last modified: 2011/05/18 04:26 (external edit)
CC Attribution-Noncommercial-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0