There’s a couple of guides out there on how to set up Exim, MySQL, Courier and Spamassassin in a virtual user environment but I thought I’d put together a high level basic guide myself.
My installation is running on debian etch 4.0 stable (2.6.18-6-686)
As this is a vdomain/vuser setup, you will not require a system shell/login for the users you add.
To start off, just install the packages you’ll need with apt-get:
Now if you already have exim4-* installed which is the default, you have two choices. We want to install our own exim version from source but without breaking the repository. The choices are to either just rename exim4 to exim4.old and then create your new exim install, or alternatively create a dummy empty exim4 package remove exim4-* and install your dummy package. This will resolve the dependancies issue in the package manager. Neither method is particularly clean – I have installed a dummy package personally and removed the real exim4 set of packages.
In my example, I’m using exim-4.67. This was the latest at the time.
#LOOKUP_MYSQL=yes
to
LOOKUP_MYSQL=yes
# LOOKUP_INCLUDE=-I /usr/local/ldap/include -I /usr/local/mysql/include -I /usr/local/pgsql/include
to
LOOKUP_INCLUDE=-I/usr/include/mysql
# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq -lgds -lsqlite3
to
LOOKUP_LIBS=-lmysqlclient
EXIM_MONITOR=eximon.bin
to
#EXIM_MONITOR=eximon.bin
make
make install
Now create the following configuration for your exim:
hide mysql_servers = 127.0.0.1/CHANGEME_DBNAME/CHANGEME_DBUSER/CHANGEME_DBPASS
smtp_enforce_sync = false
primary_hostname = CHANGE_ME.com
#daemon_smtp_ports = smtp
domainlist local_domains = @ : ${lookup mysql {SELECT userid FROM domains \
WHERE userid=”${quote_mysql:${domain}}” }}
domainlist relay_to_domains = localhost
#hostlist relay_from_hosts = 127.0.0.1
hostlist relay_from_hosts = 127.0.0.1 : 192.168.0.0/16
acl_smtp_rcpt = acl_check_rcpt
# qualify_recipient =
# allow_domain_literals
never_users =
untrusted_set_sender = *
local_sender_retain = true
local_from_check = false
# The setting below causes Exim to do a reverse DNS lookup on all incoming
# IP calls, in order to get the true host name. If you feel this is too
# expensive, you can specify the networks for which a lookup is done, or
# remove the setting entirely.
#host_lookup = *
#rfc1413_hosts = *
rfc1413_query_timeout = 0s
ignore_bounce_errors_after = 1h
timeout_frozen_after = 1d
# freeze_tell = postmaster
#tls_advertise_hosts = *
#tls_try_verify_hosts = *
#tls_verify_certificates = /usr/exim/certs/cacert.pem
#tls_certificate = /usr/exim/certs/host.cert
#tls_privatekey = /usr/exim/certs/host.key
#log_selector = +tls_peerdn
#received_header_text = “Received: \
# ${if def:sender_rcvhost {from ${sender_rcvhost}\n\t}\
# {${if def:sender_ident {from ${sender_ident} }}\
# ${if def:sender_helo_name {(helo=${sender_helo_name})\n\t}}}}\
## by ${primary_hostname} \
# ${if def:received_protocol {with ${received_protocol}}} \
# ${if def:tls_cipher {($tls_cipher)\n\t}}\
# ${if def:tls_peerdn {($tls_peerdn)(verified=$tls_certificate_verified)\n\t}} \
# (Exim ${version_number} #${compile_number})\n\t\
# id ${message_id}\
# ${if def:received_for {\n\tfor $received_for}}”
####################################################################
# ACL CONFIGURATION #
# Specifies access control lists for incoming SMTP mail #
######################################################################
begin acl
acl_check_rcpt:
accept hosts = :
deny local_parts = ^.*[@%!/|] : ^\\.
accept local_parts = postmaster
domains = +local_domains
require verify = sender
accept domains = +local_domains
endpass
message = unknown user
verify = recipient
accept domains = +relay_to_domains
endpass
message = unrouteable address
verify = recipient
accept hosts = +relay_from_hosts
accept authenticated = *
#
# condition = $tls_certificate_verified
# accept condition = $tls_certificate_verified
#
warn log_message = verified peer dn $tls_peerdn
deny message = relay not permitted
accept hosts = : +relay_from_hosts
accept condition = \
${if \
and {\
{match{$recipients}{\N<?(abuse|postmaster)@(.+\.)?sput\.nl>?\N}}\
{match{$h_to:}{\N>?(abuse|postmaster)@(.+\.)?sput\.nl<?\N}}\
}\
{yes}{no}}
deny message = This message scored $spam_score spam points. See http://www.sput.nl/spam/
condition = ${if >{$spam_score_int}{50}{1}{0}}
#accept hosts = :
#deny local_parts = ^.*[@%!/|] : ^\\.
#deny message = host is listed in $dnslist_domain
#dnslists = sbl-xbl.spamhaus.org
#deny message = This message was determined to be spam, dynamic host ranges not accepted
#condition = ${if match {$sender_host_name}{\N(dynamic|adsl|dial|cable|comcast|chello|sketcheddoorway.com|rr.com|ddns|charter
.com|ppp|pool|blueyonder.co.uk|wanadoo.fr|dsl)\N}{yes}{no}}
######################################################################
# ROUTERS CONFIGURATION #
# Specifies how addresses are handled #
######################################################################
# THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT! #
# An address is passed to each router in turn until it is accepted. #
######################################################################
begin routers
dnslookup:
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
no_more
spamcheck_router:
no_verify
# check_local_user
condition = “${if and { \
{!def:h_X-Spam-Flag:} \
{!eq {$received_protocol}{spam-scanned}} \
{eq {${lookup mysql{SELECT spam FROM users WHERE name=’${quote_mysql:${local_part}@${domain}}’}}}{1}}
\
} {1}{0}}”
driver = accept
transport = spamcheck
#spamdeliver_router:
# driver = redirect
# allow_fail
# allow_defer
# condition = “${if match {$h_x-spam-status:}{Yes} {1}{0} }”
# data = ${lookup mysql{ SELECT maildir FROM users \
# WHERE id=’${quote_mysql:${local_part}@${domain}}’ }}/.Junk
# file_transport = address_directory
# directory_transport = address_directory
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup{$local_part}lsearch{/etc/aliases}}
file_transport = address_file
pipe_transport = address_pipe
condition = ${if eq {$domain}{$primary_hostname} {yes}{no} }
#localuser:
# driver = accept
# check_local_user
# group=mail
# transport = local_delivery
# condition = ${if eq {$domain}{$primary_hostname} {yes}{no} }
#userforward:
# driver = redirect
# check_local_user
# file = $home/.forward
# no_verify
# no_expn
# check_ancestor
## allow_filter
# file_transport = address_file
# directory_transport = address_directory
# reply_transport = address_reply
userforward:
driver = redirect
allow_fail
allow_defer
condition = “${lookup mysql{SELECT forward_enable FROM users WHERE name=’${quote_mysql:${local_part}@${domain}}’ }}”
# condition = ${lookup{$local_part@$domain}lsearch{/email/forward_enable}}
file = /email/${domain}/${local_part}.forward
user = ${local_part}-${domain}
no_verify
no_expn
allow_filter
file_transport = address_file
pipe_transport = address_pipe
directory_transport = address_directory
reply_transport = address_reply
virtual_userforward:
driver = redirect
data = ${lookup mysql{SELECT forward FROM fw WHERE \
email=’${quote_mysql:${local_part}@${domain}}’}}
virtual_user:
driver = redirect
allow_fail
allow_defer
data = ${lookup mysql{ SELECT maildir FROM users \
WHERE id=’${quote_mysql:${local_part}@${domain}}’ }}
file_transport = address_directory
directory_transport = address_directory
virtual_all_aliases:
driver = redirect
data = ${lookup mysql{SELECT forward FROM fw WHERE email= \
‘@${quote_mysql:${domain}}’}}
######################################################################
# TRANSPORTS CONFIGURATION #
######################################################################
# ORDER DOES NOT MATTER #
# Only one appropriate transport is called for each delivery. #
######################################################################
begin transports
remote_smtp:
driver = smtp
spamcheck:
driver = pipe
command = /usr/exim/bin/exim -oMr spam-scanned -bS
use_bsmtp = true
transport_filter = /usr/bin/spamc
home_directory = “/tmp”
current_directory = “/tmp”
user = mail
group = mail
log_output = true
return_fail_output = true
return_path_add = false
message_prefix =
message_suffix =
local_delivery:
driver = appendfile
file = /var/mail/$local_part
delivery_date_add
envelope_to_add
return_path_add
address_pipe:
driver = pipe
return_fail_output
return_path_add
environment = EXTENSION=${substr_1:$local_part_suffix}
address_file:
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
address_directory:
driver = appendfile
maildir_format
delivery_date_add
envelope_to_add
return_path_add
address_reply:
driver = autoreply
######################################################################
# RETRY CONFIGURATION #
######################################################################
begin retry
# This single retry rule applies to all domains and all errors. It specifies
# retries every 15 minutes for 2 hours, then increasing retry intervals,
# starting at 1 hour and increasing each time by a factor of 1.5, up to 16
# hours, then retries every 6 hours until 4 days have passed since the first
# failed delivery.
# Domain Error Retries
# —— —– ——-
#* * F,2h,15m; G,16h,1h,1.5; F,2d,3h
* * F,2h,15m;
######################################################################
# REWRITE CONFIGURATION #
######################################################################
# There are no rewriting specifications in this default configuration file.
begin rewrite
######################################################################
# AUTHENTICATION CONFIGURATION #
######################################################################
begin authenticators
login:
driver = plaintext
public_name = LOGIN
server_prompts = Username:: : Password::
server_condition = \
${if crypteq{$2}{${lookup mysql{SELECT crypt FROM users WHERE id=’${quote_mysql:$1}’}{$value}fail}} {yes}{no}}
server_set_id = $1
#plain:
# driver = plaintext
# public_name = PLAIN
# server_condition = \
# ${if crypteq{$2}{${lookup mysql{SELECT crypt FROM users WHERE id=’${quote_mysql:$1}’}{$value}fail}} {yes}{no}}
# server_set_id = $1
# End of Exim configuration file
Make sure to change the CHANGEME values above. This config is the one that I use however feel free to tweak or modify to suit you.
Here is the SQL database schema:
SET SQL_MODE=”NO_AUTO_VALUE_ON_ZERO”;
–
– Database: `maildb`
–
– ——————————————————–
–
– Table structure for table `domains`
–
CREATE TABLE IF NOT EXISTS `domains` (
`userid` char(128) NOT NULL default ”,
KEY `userid` (`userid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
– ——————————————————–
–
– Table structure for table `fw`
–
CREATE TABLE IF NOT EXISTS `fw` (
`email` text,
`forward` text
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
– ——————————————————–
–
– Table structure for table `users`
–
CREATE TABLE IF NOT EXISTS `users` (
`id` char(128) NOT NULL default ”,
`crypt` char(128) NOT NULL default ”,
`name` char(128) NOT NULL default ”,
`uid` int(10) unsigned NOT NULL default ‘65534′,
`gid` int(10) unsigned NOT NULL default ‘65534′,
`home` char(255) NOT NULL default ”,
`maildir` char(255) NOT NULL default ”,
`spam` int(11) NOT NULL default ‘0′,
`forward_enable` int(11) NOT NULL default ‘0′,
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Next, configure the courier-pop3 and courier-imap authenticator:
File: /etc/courier/authdaemonrc, add:
authmodulelist=”authmysql”
File: /etc/courier/authmysqlrc, create:
MYSQL_SERVER CHANGEME_DBSERVER
MYSQL_USERNAME CHANGEME_DBUSER
MYSQL_PASSWORD CHANGEME_DBPASS
MYSQL_DATABASE CHANGEME_DBNAME
MYSQL_USER_TABLE users
MYSQL_CRYPT_PWFIELD crypt
MYSQL_UID_FIELD uid
MYSQL_GID_FIELD gid
MYSQL_LOGIN_FIELD id
MYSQL_HOME_FIELD home
MYSQL_NAME_FIELD name
MYSQL_MAILDIR_FIELD maildir
Restart courier services
Sample entries in my database look as follows:
My user filter is:
I haven’t changed the default spamassassin configuration. Just set:
ENABLED=1
in /etc/default/spamassassin
Restart spamassassin, and start up exim:
You should have the following processes running:
This HOWTO should get you up and running without too much hassle. I know that it needs some improvement, specifically the exim config and would welcome any feedback.
Tags: courier, courier-authlib-mysql, courier-imap, courier-pop, courier-pop3, exim, exim4, make, make install, MySQL, source, spamassassin, spamc, spamd, tar, vdomain, vuser