====== 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 [[http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:openldap:openldap_debian#sasl|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 [[http://www.postfix.org/SASL_README.html|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/.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). [[http://marc.info/?l=postfix-users&m=127472877702669&w=2|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 :)