Recipe: email delivery system for automatic tests

Some time ago I was asked to help in implementing an automatic test for one of the application flows. In this flow the system under test was sending emails with dynamic content to the end user, and the test had to verify the content of those emails.

The testing system had two basic requirements:

  1. The email address to send those emails should be unique, since several tests of this kind will run simultaneously.
  2. The retrieval of those emails should be as simple as possible, without any dependency on specific programming languages or libraries.

To make the long story short – the solution that we implemented consists of the ‘sendmail’ daemon with specially crafted aliases file, shell script to parse incoming email and store it in the own file by username, ‘httpd’ daemon to expose the files with emails over HTTP and a cron script to clean those files; all those based on standard RHEL 5.5 distribution. 

Step 1 – Basic ‘sendmail’ configuration

There are two items to verify in ‘sendmail’ configuration – that the list of server names (“/etc/mail/local-host-names”) is updated, and that the daemon listens to external IPs also in addition to localhost (via “DaemonPortOptions” directive in “/etc/mail/sendmail.cf”). Both are extensively covered over the internet.

Step 2 – Services

Verify that both ‘sendmail’ and ‘httpd’ daemons are configured to run on OS startup.

[root@test-mx] # chkconfig sendmail on
[root@test-mx] # chkconfig httpd on
[root@test-mx] # service sendmail start
Starting sendmail: [ OK ]
Starting sm-client: [ OK ]
[root@test-mx] # service httpd start
Starting httpd: [ OK ]
[root@test-mx] #

Step 3 – Firewall

Allow external incoming connections to ‘sendmail’ and ‘httpd’ daemons (on SMTP and HTTP ports, respectively) on local firewall by adding the highlighted lines to “/etc/sysconfig/iptables”. Don’t forget to restart ‘iptables’ service to apply the changes!

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
-A RH-Firewall-1-INPUT -p 50 -j ACCEPT
-A RH-Firewall-1-INPUT -p 51 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp -m udp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport http -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport smtp -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT

Step 4 – Email directory

Create a directory to store “delivered” emails and expose it over HTTP; note that is should be writable by ‘sendmail’ daemon!

[root@test-mx] # mkdir /var/spool/delivery
[root@test-mx] # chmod go+w /var/spool/delivery
[root@test-mx] # ln -s /var/spool/delivery /var/www/html

Step 5 – Mail aliases

Append the following lines to “/etc/aliases” file and run “newaliases” command to update ‘sendmail’ about the changes.

u:   root
u+*: |/etc/smrsh/delivery.sh

The second line delivers all emails addressed to the local addresses that start with “u+…” to the special delivery script. The first line looks useless, but it is actually need to force ‘sendmail’ to process the second line. [The email addresses that are handled by the second line are in fact aliases of “u” user, so the user itself also has to be defined…]

Step 6 – Delivery script

Create the script that will be invoked by ‘sendmail’ and will store incoming emails in the file system. Note that the script has to be located under “/etc/smrsh” directory, otherwise ‘sendmail’ daemon has no access to this script!

#!/bin/ksh
TARGET_DIR="/var/spool/delivery"
TEMP_FILE="${TARGET_DIR}/delivery.$$"

# Stream to temporary file
rm -rf ${TEMP_FILE}
while read LINE; do
 echo ${LINE} >> ${TEMP_FILE}
done

# Look for 'for <user@server>' line and extract username
TARGET_USER=`grep '^for ' ${TEMP_FILE} | cut -d' ' -f2 | cut -d'@' -f1 | cut -c 2-`
TARGET_FILE="${TARGET_DIR}/${TARGET_USER}.txt"

# Move temp file to target
mv ${TEMP_FILE} ${TARGET_FILE}

exit 0

Step 7 – Daily cleanup

Cleanup is a good task to perform daily. We will delegate that to ‘cron’ daemon by creating a new file with the following content under “/etc/cron.daily/”:

#!/bin/bash
rm -f /var/spool/delivery/*

That’s all. Now the email are accessible via “http://test-mx/delivery/”.

Leave a Reply