Table of Contents
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
<note>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.</note>
<note warning>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/.</note>
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.
<note important>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.</note>
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 :)