Select Page

Saving copies of all email using Exim

I’ve often seen questions on lists by people who want to save all incoming and outgoing mail on a specific server in an archive, this is usually due to some auditor requesting it or corporate legal types requesting it.

The Exim documentation says it can be done but does not give examples neither does any of the two Exim books, the mailing lists are short of working examples and Google does not help either! Eventually came across a russian language site that had a working setup so I figured I’d document it here in English.

The basic idea is I want a maildir made that has sub folders for each user containing incoming and outgoing mail.

You’ll need to use 2 types of Exim configuration, one being a System Filter and one being a Shadow Transport.

Handling outgoing mail is done using the system filter, I’ll set this up to only affect mail matching domain.com. In the main Exim configuration configure the basics of system wide filters by simply adding the following to the top section:

system_filter = /etc/exim/systemfilter.txt
system_filter_directory_transport = local_copy_outgoing

This defines the file where the filter will live as well as a transport that will be used to delivery the mails created by the filter. You could potentially use one of your existing transports, I like using a separate one for clarity, in your transports section add the local_copy_outgoing:

local_copy_outgoing:
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
group = exim
user = exim
mode = 0660
maildir_format = true
create_directory = true

NOTE: This is using user exim and group exim, you want to adjust it for your local needs.

Now simply create the filter in /etc/exim/systemfilter.txt:

if $sender_address_domain is domain.com
then
unseen save /var/mail/domain.com/mailarchive/.${tr{$sender_address}{.}{_}}.outgoing/
endif

This filter will save the mail in a maildir under /var/mail/domain.com/mailarchive/ the mailbox for a name.surname@domain.com user will be name_surname@domain_com.outgoing using this format means most IMAP clients will display it nicely since .’s tend to confuse them a bit. You can adjust this to taste.

Incoming mail is easier, Exim provides a shadow_transport facility that lets you call another transport for each local delivery, this transport will get a copy of the mail and its result won’t affect the further deliver of the actual email, perfect for calling vacation type commands or doing this kind of mail copying.

My needs are only for intercepting mail that reaches the Maildir’s so I’ll only need to hook into my address_directory transport, if you have other needs like intercepting actual real unix account emails then you can hook into the local_delivery transport using the same method. My address_directory transport looks like the one below, the last 2 lines are the important ones.

address_directory:
driver = appendfile
create_directory
delivery_date_add
directory_mode = 770
envelope_to_add
maildir_format
return_path_add
shadow_transport = local_copy_incoming
shadow_condition = ${if eq {$domain}{domain.com}{yes}{no}}

This calls a transport called local_copy_incoming to deliver the copy of the email, just add the following into your transports again adjusting user id, group id and file paths to your liking. This will do the file name expansion in a similar format I’m just using a slightly more complex form of the text replace here as a different example of things you can do, end result is the same.

local_copy_incoming:
driver = appendfile
directory = /var/mail/domain.com/mailarchive/ \
.${tr {$local_part}{.}{_}}@${tr {$domain}{.}{_}}.incoming/
delivery_date_add
envelope_to_add
return_path_add
group = exim
user = exim
mode = 0660
maildir_format = true
create_directory = true

NOTE: The above line that ends in “\” is a continuation onto the next, remove the “\” and join the two lines in your config.

You can now restart your Exim server, if you’ve done it all right and created the main Maildir where this all live under your incoming and outgoing mail for domain.com will all be saved on a per user basis.

Exim Vacation Messages

I hate vacation messages but business people tend to want them. They’re easy to do by the user on Exchange but it’s a bit harder on pure IMAP/POP based services since it’s not all integrated into one with the mail client.

Exim can do vacation messages using its Autoreply Transport. It supports all the usual stuff like only notifying a specific sender once every couple of days etc.

To get this going requires two bits of config, first you need a router. Routers in the exim configuration is processed top-down as they appear in the config file, so you’ll want to put the vacation handling above any localuser handling, virtual hosting etc.

A sample router is shown below, it’s for local users and they just have to put a .vacation file in their home directory to activate the functionality, the .vacation file should contain the text they want mailed. You can easily adapt the location of this to be in your virtual mail hierarchy by changing the file locations below:

uservacation:
driver = accept
domains = +local_domains
require_files = $home/.vacation
# do not reply to errors or lists
condition =  ${if or { \
{match {$h_precedence:} {(?i)junk|bulk|list}} \
{eq {$sender_address} {}} \
} {no} {yes}}
# do not reply to errors or bounces or lists
senders = ! ^.*-request@.*:\
! ^bounce-.*@.*:\
! ^.*-bounce@.*:\
! ^owner-.*@.*:\
! ^postmaster@.*:\
! ^webmaster@.*:\
! ^listmaster@.*:\
! ^mailer-daemon@.*:\
! ^root@.*
no_expn
transport = uservacation_transport
unseen
no_verify

You’ll notice that it does not autoreply to certain people, the kind of from addresses that mailing lists typically use, it will also ignore bounce messages.

Once you have the router configured you’ll need a transport, this will call the autoreply transport and do the hard work. It will use a Berkley style database in ~/.vacation.db to store the list of people it has contacted in the last 14 days. Users can just delete this file if they want to reset it all.

uservacation_transport:
driver = autoreply
file = $home/.vacation
file_expand
once = $home/.vacation.db
# to use a flat file instead of a db specify once_file_size
#once_file_size = 2K
once_repeat = 14d
from = $local_part@$domain
to = $sender_address
subject = "Re: $h_subject"

That should do it, in a virtual domain setup you could easily integrate this into a web interface that maintains the text files required, there are other features like a standard prefix before each vacation message etc, refer to the Exim Documentation for details on these.

Public – Private key encryption using OpenSSL

Sometimes I need to encrypt some stuff but do not want to install PGP or GPG. I typically use OpenSSL for this kind of thing and have written a simple frontend script to achieve strong password based encryption using OpenSSL. Sometimes you need public / private key encryption though, below will show you how to do it using just OpenSSL.

Public/Private key encryption is a method used usually when you want to receive or send data to thirdparties. The system requires everyone to have 2 keys one that they keep secure – the private key – and one that they give to everyone – the public key. Data encrypted using the public key can only ever be unencrypted using the private key. This method of encryption that uses 2 keys is called asymmetric encryption.

So by example if Person A want to send Person B data in a secure fashion she just have to encrypt it with Person B’s public key, only Person B can then open the file using her private key. There are other advantages to this kind of encryption. If I met you in person and gave you my public key, I can send you something electronically using my private key to encrypt it, if the public key you have can decrypt that data then you can trust that it was sent by me, it’s mathematical proof of identity. This is the basis for Digital Signatures.

Using OpenSSL on the command line you’d first need to generate a public and private key, you should password protect this file using the -passout argument, there are many different forms that this argument can take so consult the OpenSSL documentation about that.

$ openssl genrsa -out private.pem 1024

This creates a key file called private.pem that uses 1024 bits. This file actually have both the private and public keys, so you should extract the public one from this file:

$ openssl rsa -in private.pem -out public.pem -outform PEM -pubout

You’ll now have public.pem containing just your public key, you can freely share this with 3rd parties.
You can test it all by just encrypting something yourself using your public key and then decrypting using your private key, first we need a bit of data to encrypt:

$ echo 'too many secrets' > file.txt

You now have some data in file.txt, lets encrypt it using OpenSSL and the public key:

$ openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl

This creates an encrypted version of file.txt calling it file.ssl, if you look at this file it’s just binary junk, nothing very useful to anyone. Now you can unencrypt it using the private key:

$ openssl rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt

You will now have an unencrypted file in decrypted.txt:

$ cat decrypted.txt<br>
too many secrets

All of these examples use the RSA encryption method, some hard core mathematical information about it here.

There are a fair few limitations to this approach – it will only encrypt data up to the key size for example. And you really should never encrypt english plain text using a method like this. You’d use this to safely encrypt a random generated password and then aes encrypt the actual text you care about. Look in the comments for examples of that.

RedHat Linux and Java

I am in the process of building a automated RedHat Linux installer which requires me to configure the following on a RedHat Enterprise Machine:

RedHat has stopped providing any kind of Java binaries with their base OS, you have to buy a copy of the RedHat Application Server for $999.
As is often the case in this kind of situation there are other options that does not cost money, the group over at JPackage.Org does a great job of packaging all things Java in generic RPMs that will work on most RPM based distributions.
There are limitations though, jpackage is not allowed to distribute binaries of the non-free code such as the Sun JVM itself but they do provide source RPMs that lets you build this on your own after downloading the source from the Sun website.
I have written up a Wiki entry that details from start to end the process in getting the above working on CentOS. CentOS is of course a binary distribution of RedHat Enterprise Linux, they take the opensource SRPMs as provided by RedHat and removes all RedHat branding from the OS giving you a functional equivalent to RedHat Enterprise without the price tag. I use it on my development systems and so for the moment this guide only applies directly to CentOS though the differences are small.
This may look daunting at first but it really is not, once you’ve built the binary RPMs of the non-free code it is a breeze to install many machines with these RPMs using only a few commands and 1 config file. So you’ll soon reap the benefits especially if you are tasked with configuring a cluster of webservers that should all be on the same patch levels.
You can find the full guide here: Tomcat 5 on RedHat Enterprise Linux using JPackage.org Packages.

GMaps PHP version 1.3

After returning from a few days away to Cornwall I got a bit fed up with the formatting issues that exist with Google Maps and Firefox, I discovered a post on the Google Maps EZ forum about fixing it.
In order to fix this I added a new configuration option in the map section of the configuration file called divwidth. You will need to add your own option to your config file to upgrade to the latest version, simply replace the gmap.inc.php file with the new one found in the tarball.
The latest version can be found here: http://www.devco.net/code/gmapsphp-current.tgz. Full updated documentation can be found here.

Gmaps PHP Documentation

I’ve realized for a while now that simply announcing new versions of code that I put out listing what’s changed does not really work, as a new user it would be nearly impossible to find all the posts I’ve made related to a specific bit of code.
I’ve therefore installed a copy of MoinMoin Wiki that I will use to write documentation for projects with.
The first project to get the Wiki treatment is my PHP Library for Google Maps EZ, you can find the full documentation here.