Setting up a Postfix-based relay server with user authentication via MySQL, redux

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.

Packages required:

  • courier-authdaemon
  • courier-authlib-mysql
  • libsasl2-modules

Following packages are no longer required which were used in the previous post:

  • sasl2-bin
  • libsasl2-modules-sql

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[26162]: sql_select option missing
postfix/submission/smtpd[26162]: auxpropfunc error no mechanism available
postfix/submission/smtpd[26162]: _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 ('tom@mylittlepony.be', '$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:

authmodulelist="authmysql"

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 tom@mylittlepony.be friendshipismagic
     Authenticated: tom@mylittlepony.be  (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 tom@mylittlepony.be 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…

Writing informative technical how-to documentation takes time, dedication and knowledge. Should my blog series have helped you in getting things working the way you want them to, or configure certain software step by step, feel free to tip me via PayPal (paypal@powersource.cx) or the Flattr button. Thanks!