Automated build and setup of exim-tls with virtuals users

Table of contents

  1. Licence
  2. Introduction
  3. Credits
  4. The Automated build process
    1. Requirements
    2. Script setup
  5. Exim setup
  6. Mysql setup
  7. Bugs

Licence

Copyright (c)  2003  Vinai Kopp 

Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the "GNU Free Documentation License" can be found here.

All programs listed on this site are free software; you can redistribute 
them and/or modify them under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
These programs are distributed in the hope that they will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You can obtain a copy of the GNU General Public License here
along with these programs; or write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Introduction

When building a new server I wanted to use virtual users for mail accounts using exim-tls.
The users should be stored in mysql tables so I could easily integrate courier-popd and
spamassassin.
I luckily found an excelent description of the setup by Thomas Pircher.
The current debian exim-tls package is built without mysql support. That
almost convinced me not to use this setup because I didn't want to have to rebuild
the exim-tls-mysql package manualy if a security update would be announced.
So I decided to use the debian source package to build an exim-tls-mysql.deb and
automate the build of the reconfigured package as much as possible. But
I didn't want a build environment on the production server.
Here is what I decided to do:
I set up some scripts on a development host with a permanent internet connection.
When a new exim-tls package is announced on the debian security-announce
mailing list I run a script on the server to trigger the buildhost to fetch the
source of the new package, patch the required files and build a new exim-tls-mysql.deb.
Once the package is finished the server downloads it and I can install it using dpkg -i.

A lot of the scripts could be recoded to have fewer dependencies and it leaves some
things to be desired (e.g. if the patch fails with the new source the automatic
build fails - but at least it should be easy to figure out how to fix it)
I also tried to avoid fancy scripting in favor of easy to understand and maintainable
scripts.

I also included my version of the mysql tables and my exim.conf because it is slightly
different from the ones used by Thomas Pircher, in case somebody finds them useful.

Please send any corrections, comments and questions to <vinai@netzarbeiter.com>.


Credits

The mysql and exim setup is nothing but a modified version of the setup by
Thomas Pircher <tehpeh@gmx.net> described here: http://www.tty1.net/virtual_domains_en.html


The Automated build process

Requirements

requirements on the production server:
- curl (or curl-ssl if the buildhost is reachable over https)

requirements on the buildhost:
- must be reachable via a static IP or (dyndns) domain name
- a httpd with php4 support (apache)
- a directory in the web-tree where the httpd is allowed to write (so the new deb
  can be offered for download)
- perl
- mmv
- sudo
- a sudoers entry for httpd user for "/usr/bin/apt-get update"
  (e.g. "www-data ALL = NOPASSWD: /usr/bin/apt-get update")
- a build environment (dpkg-buildpackage, fakeroot, patch etc)
- up-to-date development libraries (e.g. libmysqlclient-dev and libssl-dev)

Script setup

On the server side there is one script: get-exim-tls-mysql-deb
It needs to be configured where the buildhost is and how to authorize
(www-authenticate user and pass). I just droped the script in /usr/local/bin.

On the buildhost side there is one php script, one bash script and one patch file

 exim-tls-mysql.php   - triggers the download and build process
                        must be configured where the final deb will be
                        and where the build script is
 build-exim-tls-mysql - downloads the new sources and builds the new deb
                        needs to be configured where the diff file can be found
 exim-tls-to-exim-tls-mysql.diff - a couple of patches to the pakage source
                        please update the maintainer on line 11to reflect your name
                        and email so people would blame you for the package


This is my setup (ajust to your directory structure):

 /var/www/domain/ssl/debbuild/exim-tls-mysql.php  <--- the trigger script
 /var/www/domain/ssl/debbuild/exim-tls-mysql      <--- directory for the finished new deb
 /var/www/domain/debian-build/exim-tls-mysql      <--- here the build-exim-tls-mysql script and the diff live

Exim setup

All virtual users are handled by the same system user (mail in my case).
I went with the maildir format because I wanted to use the courier-popd.
The maildir directories for the virtual users are located at
/var/mail/vusers/{username}/Maildir.
They must be writeable by the system account for the virtual users.

Here is the exim.config we use.

Here are the parts that are interresting for the mysql virtual user setup:



######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################

# The mysql server
hide mysql_servers = "/dbname/dbuser/dbpass"

local_domains = localhost : host.example.com : mysql;SELECT id FROM domains WHERE domain = '${sg {$key}{'}{}}' AND status = '1';


######################################################################
#                      DIRECTORS CONFIGURATION                       #
######################################################################

# mysql aliases director
# The same regarding the file_- and pipe_transport options
# applies as with the system_aliases director

mysql_aliases:
  driver = aliasfile
  file_transport = address_file
  pipe_transport = address_pipe
  search_type = mysql
  queries = SELECT value FROM aliases WHERE lookup = '${sg {$local_part@$domain}{'}{}}' AND value != '' AND status = '1'; : \
            SELECT value FROM aliases WHERE lookup = '${sg {@$domain}{'}{}}'            AND value != '' AND status = '1';

# .forward equivalent lookup or virtual users
# virtual users may not use forwards to files, pipes or use includes

vuser_forward:
  driver = forwardfile
  check_local_user = false
  check_ancestor
  forbid_file
  forbid_include
  forbid_pipe
  user = mail
  group = mail
  data = ${lookup mysql {SELECT forward FROM vusers WHERE user='${sg {$local_part}{'}{}}' AND forward != '' AND status = '1';} {$value} fail}

# This director searches for a matching user in tha mysql database

virtual_user:
  driver = aliasfile
  file_transport = address_file
  directory_transport = address_directory
  pipe_transport = address_pipe
  search_type = mysql
  query = SELECT CONCAT(maildir, '/') FROM vusers WHERE user = '${sg {$local_part}{'}{}}' AND maildir != '' AND status = '1';
  user = mail
  group = mail


######################################################################
#                   AUTHENTICATION CONFIGURATION                     #
######################################################################

plain:
   driver = plaintext
   public_name = PLAIN
   server_condition = "${if crypteq {$3} {${lookup mysql {SELECT crypt FROM vusers WHERE user = '${sg {$2}{'}{}}' AND status = '1' AND asmtp = '1';}}} {1}{0}}"
   server_set_id = $2

login:
   driver = plaintext
   public_name = LOGIN
   server_prompts = "Username:: : Password::"
   server_condition = "${if crypteq {$2} {${lookup mysql {SELECT crypt FROM vusers WHERE user = '${sg {$1}{'}{}}' AND status = '1' AND asmtp = '1';}}} {1}{0}}"
   server_set_id = $1

cram:
  driver = cram_md5
  public_name = CRAM-MD5
  server_secret = "${lookup mysql {SELECT clear FROM vusers WHERE user = '${sg {$1}{'}{}}' AND status = '1' AND asmtp = '1';} {$value} fail}"
  server_set_id = $1



Mysql setup

Here are our mysql tables, they are probably self explanatory.
The exim mysql user only needs the select privilege.

Bugs

- probably a lot: this is a hack
- diff doesn't apply cleanly to the current stable mysql-tls source package (it works but with a fuzz factor)