Skip to main content

Message to github and patreon sponsors: THANK YOU ❤️
  1. Posts/

OpenSMTPD: LDAP support, selectable source, DKIM and Goodies

·1327 words·7 mins·
Gilles Chehade
Gilles Chehade
I’m not a cat.
If you like reading articles on this website, please ❤️ consider sharing on social networks to help increase visibility: this helps buy time for new articles and projects !

This week has been crazy, my brain melted a little as I worked on filters, it melted a little as I worked on LDAP and it melted a little more as I tried to trick Eric into working on filters with me (yes, that actually worked :-).

Anyway, as usual, this is just going to be a summary of what we did as tons of little stuff have been committed here and there. You can keep track of the changes by checking the commit log of our Github mirror, or by joining our little gang on our IRC channel: #OpenSMTPD @ freenode.

Snapshots containing all the following features should be published tomorrow.

Let the fun begin !

LDAP backend

I had started working on LDAP support for OpenSMTPD a long time ago but for some reason the support was never finished and ended up rotting in my sandbox.

A few months ago, I brought the bits back to a git branch so that I would keep running into it every few days as a reminder that I should not slack. But since I'm not too much of a LDAP fan, or a LDAP user for what it's worth, I made the branch public in hope someone would pick it up and move it forward.

A poolp user had started bringing the bits up to date and getting a working support in shape for aliases lookup. Resuming from there I simplified the code further and added support for almost all kinds of lookups making OpenSMTPD capable of using LDAP as a backend for the most common use-cases.

Here's a configuration file to authenticate local users, lookup a domain and perform aliases lookups against LDAP:


table myldaptable ldap:/etc/mail/ldapd.conf

listen on egress tls auth

accept for domain alias deliver to maildir accept for any relay

and here's the table configuration:


url ldap:// username cn=admin,dc=opensmtpd,dc=org password thisbemypasswd basedn dc=opensmtpd,dc=org

aliases lookup

alias_filter (&(objectClass=courierMailAlias)(uid=%s)) alias_attributes maildrop

credentials lookup

credentials_filter (&(objectClass=posixAccount)(uid=%s)) credentials_attributes uid,userPassword

domains lookup

domain_filter (&(objectClass=rFC822localPart)(dc=%s)) domain_attributes dc

The support is functional but it needs to be improved further as it currently has two drawbacks: the backend does not reconnect to the LDAP server should it lose the connection, and it uses the aldap synchronous API which means that queries that take time to complete will be heavy on the lookup process.

Also, I only tested with OpenBSD's ldapd(8) as it was dead simple and I didn't want to add more pain than required on my shoulders. Turns out, it did make my experiment far more enjoyable that I would have assumed ;-)

Oh, and I ordered a LDAP book to get more familiar with the service as I suspect I'll be getting questions regarding LDAP every now and then given how many times it's been requested in the past. I might as well know what I'm talking about :-)

Source address selection

Eric has plugged the K_SOURCE lookup service to relay rules, this allows OpenSMTPD to perform a lookup of the source address it should use when the transfer process establishes an outgoing connection to a relay.

Until now OpenSMTPD could not force the IP address it used for outgoing trafic without relying on a hardcoded hack that was committed to the poolp branch. It was done this way on purpose and we delayed this feature until the other parts were rewritten appropriately for the puzzle to fit right.

It is now possible to force an address using the source keyword:

table myaddrs {, }

accept for any relay source

If multiple addresses are provided, they will be cycled through, and the mta code will detect which ones are no longer usable.

Intermediate bounces

A feature we had a long time ago and which disappeared during a cleanup was the support of intermediate bounces.

When OpenSMTPD fails to deliver a mail it has to notify the sender that the message was never delivered. It sometimes happens immediately, but sometimes the failure may be temporary and the daemon keeps the message and tries to deliver it every now and then (ok, the logic is slightly more complex, but you get the idea). In such cases, the bounce will not be sent before OpenSMTPD gives up on trying after 4 days by default.

Obviously, getting a mail 4 days later to tell you that no one read yours when you assumed it was already in the recipients mailbox for a while is quite irritating. The intermediate bounce will instead notify the sender that an error occured after a few temporarily failed deliveries, and let him know that the daemon will keep trying to deliver for a while.

After discussions, Eric reimplemented intermediate bounces in OpenSMTPD but did it in a slightly different way than with other daemons. By default, an intermediate bounce will be sent after a mail has been sitting in the queue for over 4 hours without being delivered ... but in addition, a set of delays may be provided in smtpd.conf to send multiple intermediate bounces. For example, if I wanted intermediate bounces to be sent after 4 hours, after a day and after two days, I could simple use:

bounce-warn 4h, 1d, 2d

The keyword may change, but the idea and code is here and working.

Tags & DKIM example

I've implemented tagging of sessions a very long time ago, I think it was actually already there when OpenSMTPD was not yet OpenSMTPD but still a poolp project :-)

The feature was hidden and undocumented, it had uses but so limited that I did not want users to start using it in random situations that I would have to cope with later. Basically, a listener may tag all sessions initiated through it; then rules may apply to specific tags allowing some rules to apply only to some sessions.

Eric realized that this was perfect to deal with one of our use-cases: DKIM signatures.

We want DKIM signatures but we don't necessarily want to write a filter for that as there are already tools that do the work. So we need to accept a message, pass it to a DKIM signing tool, which will in turn pass it back to us, so that we can send it where it needs to be sent.

A tool to do this is DKIMproxy. I won't enter the details behind DKIMproxy, I'll actually post a description of how to setup OpenSMTPD with it very soon on this blog. But the idea is that using tags we can determine which sessions we want forwarded to DKIMproxy, and which sessions are coming back from it and need to be relayed to the final destination:

listen on all interfaces that are attached to the default route

listen on egress

listen on loopback interface on port 10028 and tag DKIM

listen on lo0 port 10029 tag DKIM

only accept to relay the sessions that are tagged DKIM

accept tagged DKIM for any relay

this is reached by the sessions that are NOT tagged

and will cause OpenSMTPD to relay to the DKIMproxy

accept for any relay via smtp://

Now if I were to send mail to my gmail account, I would connect to the daemon, my session would not be tagged so it would match the last rule causing the message to be send to DKIMproxy. DKIMproxy would then relay back the mail to my loopback interface on port 10029 which would have the new session tagged DKIM causing the first rule to be matched. 4 lines. ridiculous.

OpenSMTPD goodies

Here it is, OpenSMTPD goodies, not for sale, limited to friends and a few spare I will give away or sell at low price depending on how many are left.

If you were kind enough to contribute and finish the FAQ section of the OpenSMTPD website, I might just send you a mug and a tshirt signed by Charles, Eric and I ;-)

Time to go zZz, stay tuned for more news !

You're invited to join my Discord server
This is a chat server where I hang out, discuss my projects and sometimes screencast as I work on them.

Feel free to hop in, talk about your own projects, share your thoughts: this is a virtual coworking room for anyone to join.