Let’s do Postfix slowly and properly – Part 6: Relay authenticating with SASL

Piggybacking off of Dovecot to provide authentication services for Postfix.

This post continues the Postfix series but depends on the Dovecot setup from that series. If you don’t have Dovecot running alongside Postfix, this post should be skipped.

I have explored the default setting of Postfix to allow local machines to send without authenticating, simply based on them being on the local network (permit_mynetworks). Even if I am not always on my own local network I could make this work by installing a webmail client on the same server (or in the same docker network). Even if we are accessing the client over the internet, the client is on a local machine and part of the local network’seen from the perspective of the postfix. Therefore permit_mynetworks would give the OK and allow it to send without any further restrictions. But then there are smartphones and getting notified whenever I have mail rather than having to check manually. So I need a way to authenticate as a legitimate user who should be allowed to send when I’m not on the local network of the server. One way to do this is by piggybacking on the MDA’s authentication. In other words: If I am already authenticating with the IMAP server, why not use that to also authenticate me with Postfix for sending? This link between the two reminds me of what I said in the first post in this series: That the division into MTA and MDA probably wasn’t engineered as much as just arrived at by stops and starts.

In this post I will create such a setup with Dovecot playing the part of the MDA. Dovecot will offer up an authentication service on the machine. When I ask Postfix to send something, Postfix will approach this service and ask if I should be allowed to send. And Dovecot will say yes or no, depending on whether or not I have previously properly authenticated with it.

It is important to note, that this is a service wholly independent of authentication mechanisms or encryption, see the last post on Dovecot in the series for details. Dovecot acts as a go-between and Postfix doesn’t need to know what’s on the other end. In other words, it doesn’t matter how I have set up Dovecot to authenticate me for IMAP, I can just tack on an SMTP authentication service to whatever way I have told Dovecot to allow access to my IMAP server. If I am authenticated to get mail, I will then be allowed to send mail. This means that if I have set Dovecot up to not ask for a password to check my mail, I will not be asked for one to send mail.

EDIT: It is also worth noting that it is probably a good idea doing this step simultaneously with part 7 about encryption as that one goes into more detail about setting up a separate submission service – which this one kind of relies on.

This will require me to make three changes: 1) I need to tell Dovecot to add a service that listens for authentication requests from Postfix, 2) I need to tell Postfix how and where to find the Dovecot authentication service, and 3) I need to Postfix to allow relay from SASL authenticated users.

Note that for various reasons, both Docker specific and SASL related, I found myself forced to switch from Ubuntu 18.04 to Debian 10 Buster for the Postfix and Dovecot containers in order to get more recent versions of the programs (e.g. Postfix 3.4). Since I forgot to note all the issues prompting this upgrade, I can only say caveat emptor and YMMV with older versions.

Add Dovecot auth service

There’s a division of labour between Dovecot’s conf.d files that gradually becomes apparent if they’re not immediately selfevident (like 10-ssl.conf). Much like Postfix’s master.cf, 10-master.conf contains the definitions of services, that respond to outside requests, like imap(s) and now also the authentication service. I’m going to deviate slightly from most examples here in that, rather than a unix socket I will use a standard TCP port as the interface for the auth service. Inside of the service auth { ... } brackets, I add the following:

    inet_listener {                                                                                                                                                                               
        port = 12345                                                                                                                                                                              
    }

(Port 12345 is derived from an example from the dovecot wiki where the writer obviously just meant is as ‘pick your own port because there is no formally defined one‘ but I never got round to changing it, and now 12345 is what I associate with it)

The reason for choosing ports over sockets is obviously that ports are much easier to use in a container setup. The downside is that there are fewer easy-use tools at my disposal to restrict access to the port than to the socket. Still I figure if somebody has gotten access to listening in on inter-container communications, they already have root on the host? I could be mistaken, though – I guess there’s a sniffing challenge to be attempted there. The dovecot wiki page linked to above has the socket setup ready to copy-paste.

Connect Postfix to the service

Setting up Postfix to use this service for sending authentication should have been easy. Just add the following to main.cf and I should have been good to go:

# SASL auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = inet:dovecot:12345

smtpd_sasl_auth_enable is the flip switch that turns it on, and smtpd_sasl_type tells Postfix to expect a Dovecot server at the other end. The settings have the smtpd_ prefix because the connections asking for the right to send, are ingoing (usually on the submission port, port 587). The smtpd_sasl_path value tells Postfix that what follows is a host:tcp-port setup, rather than a path (inet), that the host is dovecot (because that is the name of the dovecot container and therefore the container host name; substitute the domain name of a local or internet host if that is what is used), and that the port is 12345.

Long story short, I found out that there are some issues with DNS for this particular setting. Some might call it a bug, the Postfix developers seem to disagree. The upshot is that “dovecot” is never translated into an IP address, nor is a any other kind of either local host name or internet domain. Entering the dovecot container’s IP address worked perfectly though.

smtpd_sasl_path = inet:192.168.1.3:12345

Needless to say, for this to work consistenly, I did have to convert the docker setup to a fixed IP addres one. That is a bit outside the scope of this post, but there are some good ressources on StackOverflow and the Docker documentation.

Permit sasl for relay

As soon as my email setup got back on it’s feet after a long downtime (measured in months), spam started trickling in. Spam, spam, spam, wonderful spam, etc. So with this reminder I immediately set my smtpd_relay_restrictions to just defer_unauth_destination which means that Postfix will not touch any mail except the ones for which it considers itself the end destination. In other words: No sending, only receiving. Now it’s time to relax a little:

smtpd_relay_restrictions = permit_sasl_authenticated defer_unauth_destination

With a container setup permit_mynetworks doesn’t make any sense unless one is aiming for the exact kind of webmail/MTA kind of cohabitation, I refered to at the top of the post. And that setup is equally well served with SASL. So to keep things simple I leave out permit_mynetworks and the definition of mynetworks with it.

Testing

In Postfix logs sending emails now generate a line with an explicit sasl authentication:

Sep 13 20:13:52 VHOST postfix/submission/smtpd[218]: 286DA1427B4: client=unknown[10.0.4.1], sasl_method=PLAIN, sasl_username=myuser@mydomain.net

whereas dovecot, on the receiving end, seems to just log it as any other login:

Sep 13 22:13:52 imap-login: Info: Login: user=<myuser@mydomain.net>, method=PLAIN, rip=192.168.1.1, lip=192.168.1.3, mpid=234, TLS, session=<reSW5HSSqI4KAAQB>

If the authentication fails (because of bad passwords, the user isn’t recognised by Dovecot, or the like), I find the words “SASL PLAIN authentication failed” in the Postfix logs with notes on clients, usernames etc.

Continuing on

As mentioned in part 2 of the Dovecot series, I want to detail one other way to tie Postfix and Dovecot together, and that is using LMTP. However, seeing as I am now authorizing users to send email out onto the internet – the scariest part of any selfhosted email setup – I feel like we have to continue straight to talking about encryption, particularly encryption of the submission stage. We’ll get back to the scheduled Dovecot broadcast after a few more Postfix issues have been taken care of.

passport booklet on top of white paper © Nicole Geri, Unsplash license

1 Comment

Just to let you know, I’ve been using

`smtpd_sasl_path = inet:dovecot:12345`

for ages and it always worked flawlessly. And yes, I’m running it inside a docker-compose with dynamic IP addresses so I’m dependant on IP address lookup of other containers.

Atm I’m using Postfix 3.7.10-0 from Debian, not sure if they maybe applied a patch or something.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.