You are not logged in. Log in now?

Show a Story

Postfix vs. SASL vs. PostgreSQL Unix Sockets

There is a certain… inelegance to be found when using Unix sockets to have Postfix and Courier communicate with a PostgreSQL database for authentication and other configuration needs. With "good" old TCP/IP sockets, things are rather straightforward since they come with a standardised syntax for host address and port.

Unix sockets are for all configuration purposes file system paths, and both Courier and Postfix make rather badly documented and most of all bad assumptions.

The basic tenor is that you put a base path as the hostname. On some systems, that will be /var/run/postgresql, on chrooted Postfix installations as configured by Debian you will need to make it /var/spool/postfix/var/run/postgresql. Postgres will create a file in there called something like .s.PGSQL.5432, where 5432 is the port. It is the port everyone assumes! If you have your DB cluster running somewhere else, things get creative.

For Courier's authpgsqlrc, things are relatively straightforward (and even correct), if hard to find:

 PGSQL_HOST /var/spool/postfix/var/run/postgresql
 PGSQL_PORT 5433

In Postfix's little world however, things suddenly become completely undocumented, and even worse, potentially broken in the nicely spread out configuration files:

 hosts = unix:/var/run/postgresql/:5433

Luckily Cyrus SASL can be completely avoided, because no matter what syntax I threw at it, there was no way to make it work with Unix sockets.

How It Really Works

The way to pass socket addresses to Postgres could actually be done all unified and simple. Looking at the relevant DB connection documentation shows that the library expects a connection string formatted like

  … host=something port=5432 …

so it is just a number of key=value pairs. The library distinguishes hostnames from socket names by a leading slash (sockets have one). So, guess what, no magic is needed assuming a sane implementation allowing you to pass separate host and port parameters. All you need to do is pass an absolute path instead of a hostname, and the normal numeric port value. The library will correctly expand that to the full socket path.

This effectively means some documentation is just plain wrong, some is misleading, and some is sadly correct by omission.

Intermediate Conclusion?

What would be the take-away message here? "Don't use the efficient way because we couldn't be arsed to implement and document it in a sane way"?

Circular Confusion

Then came a migration from Courier to Dovecot (Sieve support is totally worth it!), which meant changing the transport mechanism from maildrop to Dovecot's "deliver" LDA. This was done with Spamassassin integration, so Postfix does not pipe mails through deliver, but instead passes them on to spamc, which in turn forwards them to the LDA (one way to do it is shown here, my configuration is a bit more intricate since I opted for the overkill of supporting per-virtual-user Spamassassin configuration and proper support of local address extensions).

Fun enough, after adding that to the Postfix configuration, suddenly mail.log showed complaints that "pipe" could not access the DB table containing the transports (why it needs them is beyond me, but there you go).

This happens because the pipe cannot be run chroot like the rest of postfix, but there's no Postgres socket in the OS root. The simple but ugly solution:

 /var/spool/postfix/var/spool/postfix -> ../../

this means you can point Postfix's entire DB configuration at the chroot path (/var/spool/postfix/var/run/postgresql) whether parts run chroot or not.

By Shadowdancer, 2011-06-16, 15:32; permalink;
Last updated at 2011-06-27, 12:43 by Shadowdancer

Powered by merb 1.1.0 and DataMapper 0.10.2.