Laconica server and posting to made-up email
addresses with postfix recipient checking

Posted June 27, 2009 by Stefan Caunter, NearSourceIt
We present a way to avoid using a catch all policy on a laconica server to accept email posts.
We validate recipients using postfix and the mysql database. Only after we confirm that the
recipient is one of the made-up email addresses for posting on the system, do we pass the message
to the posting script.

We only accept mail posts to valid recipients, and only then process the message.

This technique also avoids sending posts to the script maildaemon.php for validation, which is
not only expensive, but is already robustly done by postfix. The script processes any post
it receives at this point in the transaction, resulting in better speed for the laconica system.

If you are running postfix your system will not accept mail for unknown users, and email posts
to the laconica made-up email addresses will always be rejected. The solution is virtual
users, found in the existing laconica database.

Technique

We lookup the incomingemail address in the user table. Postfix does this check with a simple query. This allows a valid email post to be accepted. The email is subsequently aliased to a local user where is it sent in a pipe to the maildaemon script. The benefits are that we do not open our mail server to accepting all mail in a catch all address for our domain, which would then all hit the maildaemon.php script, providing a denial of service vector, as the expensive shell process to analyze each message ran, checking every message to the domain whether a valid post was received, or not.

Reference

Engelschall's mysql virtual users setup chapter.

Requirements

laconica server postfix with mysql with virtual users pulled from the laconica user database table.

Postfix setup:

A virtual delivery agent stores all messages in a directory. We need this to work, even though we are not going to deliver to the recipient. We want to validate the recipient, and after the validation, deal with the message by aliasing it to a local user. The virtual delivery agent is a unix user, with a uid and gid. The user must have appropriate permissions to the directory where the mail will go. This directory is defined in the postfix configuration file. The laconica database table user is queried for the incomingemail address for a match. The file to configure it is shown below: # mysql-aliases.cf user = laconica password = somethingobvious dbname = laconica table = user hosts = 127.0.0.1 query = SELECT user.incomingemail FROM user where user.incomingemail='%s'; Example postfix main.cf configuration addition: virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql/mysql-aliases.cf virtual_mailbox_domains = pbj.ca virtual_mailbox_base = /var/mail/laconica virtual_minimum_uid = 100 virtual_uid_maps = hash:/usr/local/etc/postfix/virtual_uid_map virtual_gid_maps = hash:/usr/local/etc/postfix/virtual_gid_map The virtual map files are hashed into a tiny db. Each one simply contains a catch all for the domain. @pbj.ca 1111 These commands make it available to postfix. postmap hash:virtual_uid_map postmap hash:virtual_gid_map The user and group must exist, with the corresponding uid and gid. The virtual_mailbox_base directory must exist, with user and group ownership corresponding to the system user and group with that uid and gid, so that user and group need to exist. My original idea was to alias to a system user with a .forward that was a pipe to the maildaemon.php script. Virtual users do not work this way. You cannot use the alias map for them. Create the file virtual_alias_map in /usr/local/etc/postfix with this text: @pbj.ca post@localhost and run postmap hash:/usr/local/etc/postfix/virtual_alias_map We include in main.cf: virtual_alias_maps = hash:/usr/local/etc/postfix/virtual_alias_map and set mydestination in main.cf to mydestination = localhost.$mydomain However, still anyone can post. The db lookup is working, but the catchall is still sending everything to post@pbj.ca. We need to check in the db for the virtual_alias_map, so we don't have a catchall problem. This solves everything: Take out the catchall hash entry in main.cf and replace it with a db lookup: #virtual_alias_maps = hash:/usr/local/etc/postfix/virtual_alias_map virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql/virtual_alias_maps.cf The virtual_alias_maps.cf file looks like this: user = laconica password = somethingobvious dbname = laconica hosts = 127.0.0.1 query = SELECT 'post@localhost' FROM user WHERE incomingemail='%s' We now check for the incomingemail address, and alias it to post@localhost. Conveniently, post@pbj.ca is no longer valid, as it does not exist in the virtual table. Now, a .forward entry for the system user post that calls maildaemon.php will post on behalf of a remote user.

Improvements

Postfix checks for message size and payload should be implemented if desirable. Adding processing for message attachments.