Creating a custom LSF email router using exim

The University of Applied Sciences Weingarten, as many others, uses an installation of LSF (Lehre Studium Forschung) to manage students, departments and courses.When working at the datacenter of the university, I was part of a project to make the data of the LSF data available to the email system, to make LSF the primary system to connect user accounts with departments or classes. While accounts were primarily managed using LDAP, the system was not flexible enough to hold information about enrolled courses or roles in LSF, like professor, student, assistant, member of department X, etc.

Connecting LSF to the email system

News article: LSF email router

A custom webservice within the LSF framework however, replied a query with a nice plaintext list of user account names, which easily can be mapped to the canonical email adress of each given account. So we have the data, now how do we route the emails?

The datacenter ran two powerful Cisco Ironport appliances, used as MX, filter and first email router. I decided to put the lsf-email-router part on a separate Linux machine, the lists.hs-weingarten.de server, already running exim4 and an instance of mailman for the faculty. The Ironport appliances already routed every mail for lists.hs-weingarten.de subdomain to the lists server.

Special addresses

The exim4 router should detect special LSF email addresses (starting with lsf-), querying LSF for the data, and forward the email to the received list of user accounts, injecting the emails back into the email system to the main mail routers, that resolved account names to actual mailboxes.

Several adresses and queries were prepared, which can be easily extended, for example:

  • lsf-einrichtung-XXX@… (department XXX)
  • lsf-veranstaltung-xyz@… (all members of course xyz)
  • lsf-einrichtung-XXX-funktion-YYY@… (department XXX, role YYY)
  • lsf-veranstaltung-XYZ-20112@… (members of course xyz of winter semester 2011/12)
  • lsf-studiengang-XY@… (all students of the field XY)

A complete list of available dynamic addresses can be found in the service portal: http://rz-serviceportal.hs-weingarten.de/lsf-email-verteiler

The custom script

Two ninjas created a short perl script to be called from exim4, that did the following tasks:

  1. Separate the parts of the email address and check for validity
  2. Determine cache file for the special list and exit script, if cache file is younger than 6 hours. This is done to not query the webservice more often than necessary and using 6h old data was acceptable in this case.
  3. If the cache file does not exist or is older, the LSF webservice is queried for the list of accounts. On success, the cache file is created or updated.
  4. On failure, the LSF might be down, but the email system shouldn’t. If a cache file exists and is younger than 1 week, use it anyway.
  5. If the cache file is too old, delete it and fail. Do not use cached file that is too old but rather fail routing the email, which will result in a mailer daemon message back to the sender.
  6. The perl script exits with an error code 0 or 1 to indicate success or failure.

Now we have a cache file with valid email addresses, or we don’t.

The exim configuration

Integrating the perl script into exim, using exim4′s splitted config, was demeaningly easy, but was actually the tricky part (file /etc/exim4/conf.d/router/475-lsf-router):

lsf_router:
    driver = redirect
    local_part_prefix = lsf-
    file = ${run{/usr/bin/perl /etc/exim4/lsf-router.pl \
           --lsflist $local_part_prefix$local_part} \
           {/var/cache/lsf-router/$local_part_prefix$local_part} \
           {/var/cache/lsf-router/error}}

This router entry works as a redirect router, giving it a file to expand email adresses from. Like a boss, we used exim’s ${run} string expansion to run our script every time an email is routed that maches the lsf- local part prefix of the address. Depending on the success or failure of the ran command, the expansion returns one of two strings. On success, the used filename is the whole local part of the destination address, on failure it is a non-existing path, which will result in a permanent routing failure.

Voilà, our LSF email router is done and working. Sending an email to lsf-veranstaltung-XYZ@… results in the email being routed to the Linux box, the script will be run to refresh the cache for the special email address, querying LSF at most every 6 hours, expanding the data and exim will forward the email to all user accounts.

It’s like using mailman for managing university courses and departments – except that it’s great.

For further information, feel free to contact me or rechenzentrum@hs-weingarten.de

See also:

Comments are closed.