Right before publishing my previous article on how to set up a Postfix-based server with MySQL SASL authentication, I stumbled upon another way to do it (which I briefly touched in the alternatives section), namely Courier Authdaemon.
I decided to give this a go, before taking my server into production, but it took a LOT of poking, peeking, stracing and googling to get to the end result. But I managed to crack it, so now I have identical functionality to the previous post, but with encrypted passwords in my database, which is always better.
Following packages are no longer required which were used in the previous post:
This setup no longer uses saslauthd, so the binary package can go. The SQL driver definitely needs to go, or it will produce errors in syslog at every SASL library load due to missing configuration statements:
postfix/submission/smtpd: sql_select option missing postfix/submission/smtpd: auxpropfunc error no mechanism available postfix/submission/smtpd: _sasl_plugin_load failed on sasl_auxprop_plug_init for plugin: sql
The MySQL database table used for authentication is created as follows:
CREATE TABLE users (`username` VARCHAR(80) NOT NULL, `password` VARCHAR(128) NOT NULL, PRIMARY KEY (username)) ENGINE=MyISAM; INSERT INTO `users` VALUES ('firstname.lastname@example.org', '$1$EUvYRIjf$tNq.K9Z3ST8XOT2ltkV6M/');
This database setup is pretty much equal to the previous post, but the password field length has been increased to accomodate for hashed passwords. 20 characters will certainly not cut it, even a simple salted MD5 hash is 36 bytes long.
Make authdaemon use its MySQL module in /etc/courier/authdaemonrc:
Configure the MySQL parameters for it in /etc/courier/authmysqlrc:
MYSQL_SERVER 127.0.0.1 MYSQL_USERNAME mail MYSQL_PASSWORD topsekr1t MYSQL_DATABASE mail # username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname, options MYSQL_SELECT_CLAUSE SELECT username, '', password, 65535, 65535, '/nowhere', '', '', 'SMTP user', '' FROM users WHERE username='$(local_part)@$(domain)'
I’ve defined a custom SELECT query in the configuration, so I can use a simple table without needing to detail home directories and such for every simple user; you can of course add more (or all) fields if you so desire, but they will not actually be used anywhere, so I’ve defined them as strings in the query itself.
Note that I used 127.0.0.1 instead of localhost, as in the previous article, to connect to MySQL. I’m not sure this is needed because authdaemon doesn’t run inside Postfix’s chroot. I copied the settings but I’m pretty confident it’ll work just using the MySQL Unix socket.
Now, after you’ve set this up, make sure you test if your authdaemon setup is working. You can do this with the authtest binary:
# authtest email@example.com friendshipismagic Authenticated: firstname.lastname@example.org (uid 65535, gid 65535) Home Directory: /nowhere Maildir: (none) Quota: (none) Encrypted Password: $1$EUvYRIjf$tNq.K9Z3ST8XOT2ltkV6M/ Cleartext Password: friendshipismagic Options: (none)
This is the sort of output you’re after. If you made a mistake in the query, the database connection or grants, or anything else that makes the MySQL query not work, you’ll get
# authtest email@example.com friendshipismagic Authentication FAILED: Input/output error
Before you get this working, it makes no sense trying to use this through Postfix, as debugging will be very hard and nunless you toggle magic things and then look very carefully, you won’t easily spot it in the logs.
Add the postfix user to the “daemon” group, which is needed to speak to the authdaemon socket (similar to it having to be in the sasl group to be able to speak to saslauthd):
# adduser postfix daemon
Configure Postfix’s usage of authdaemon via /etc/postfix/sasl/smtpd.conf:
pwcheck_method: authdaemond log_level: 0 mech_list: PLAIN LOGIN authdaemond_path: /var/run/courier/authdaemon/socket
Unfortunately, unlike with saslauthd, the path to the authdaemond socket cannot be specified as a command-line or configuration file parameter. It’s hardcoded to be /var/run/courier/authdaemon/socket. This creates a problem for us, as Postfix (by default, and I recommend this) chroots to /var/spool/postfix. So we create a bind mount to place the regular directory inside the Postfix chroot.
First create the target directory:
# mkdir /var/spool/postfix/var/run/courier
Then add the following line into /etc/fstab, to mount this upon bootup:
/var/run/courier /var/spool/postfix/var/run/courier bind bind 0 0
Check if you did it correctly by mounting it now:
# mount -a # df -h Filesystem Size Used Avail Use% Mounted on (...) tmpfs 50M 216K 50M 1% /var/spool/postfix/var/run/courier
Other Postfix main.cf configuration is identical to the previous article, repeated here for completeness.
I’ve used the following Postfix parameters in /etc/postfix/main.cf to be able to use SASL authentication:
broken_sasl_auth_clients = yes smtpd_sasl_auth_enable = yes smtpd_helo_required = yes smtpd_tls_CAfile = /etc/ssl/certs/smtpd.pem smtpd_tls_received_header = yes smtpd_tls_loglevel = 1
Note that while we’re not (or no longer) using saslauthd, we’re still using SASL libraries to connect to authdaemon, so we still configure Postfix for SASL.
The following directives are used to also use TLS on outgoing mail (being relayed), if available – with fallback to unencrypted:
smtp_use_tls = yes smtp_tls_security_level = may smtp_tls_loglevel = 1
Using TLS on outgoing mail is a good idea, as it’ll not deter, but at least slow down our friends at the NSA-and-friends in case they are really interested in your e-mail.
We shouldn’t forget to create the certificate used for TLS; doing this locally with a 10 year validity:
openssl req -new -x509 -nodes -out /etc/ssl/certs/smtpd.pem -keyout /etc/ssl/certs/smtpd.pem -days 3650
You can also use an actual, signed, certificate of course.
To be able to relay using TLS on port 587, and use SMTPS on port 465, add (on Debian, uncomment) the following blocks in /etc/postfix/master.cf:
submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING smtps inet n - - - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
If you want to be able to relay using TLS through regular port 25 as well, add the following to main.cf:
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
Of course, now we simply wait until someone comes and tells me that my previous method with sasl auxprop also takes encrypted passwords, and I simply didn’t test right because my database column was too narrow…