msgbartop
Adam Palmer MBCS CITP, Linux, PHP Programmer, MySQL Developer, Embedded Hardware, Security Consultant
Did my blog help you? Please link to me!
  dns test
 
RSS Feed
msgbarbottom

19 Oct 08 Exim, MySQL, Courier IMAP, Courier POP3 & Spamassassin – vdomain and vuser set up.

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:

apt-get install mysql-server-5.0 mysql-client-5.0 spamassassin, spamc, courier-authlib-mysql courier-imap, courier-pop3.

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.

tar -xjf exim-4.67.tar.gz
cd exim-4.67
Edit Local/Makefile, and change:

#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:

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

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:

– phpMyAdmin SQL Dump
– version 2.11.8.1
– http://www.phpmyadmin.net

– Host: localhost
– Generation Time: Oct 19, 2008 at 07:07 PM
– Server version: 5.0.32
– PHP Version: 5.2.0-8+etch11

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:

INSERT INTO `domains` (`userid`) VALUES (’adamsinfo.com’);
INSERT INTO `fw` (`email`, `forward`) VALUES (’info@adamsinfo.com’, ‘adam@adamsinfo.com’);
INSERT INTO `users` (`id`, `crypt`, `name`, `uid`, `gid`, `home`, `maildir`, `spam`, `forward_enable`) VALUES
(’adam@adamsinfo.com,’, 0123456789abcdef’, ‘adam@adamsinfo.com’, 8, 8, ‘/email/adamsinfo.com/adam’, ‘/email/adamsinfo.com/adam/Maildir’, 1, 1);

My user filter is:

ns3:~/exim-4.67# cat /email/adamsinfo.com/adam.forward
#Exim filter
if
$h_X-Spam-Status: CONTAINS “Yes”
or
“${if def:h_X-Spam-Flag {def}{undef}}” is “def”
then
deliver <junk@localhost>
finish
else
deliver <adam@adamsinfo.com>
finish
endif

I haven’t changed the default spamassassin configuration. Just set:
ENABLED=1
in /etc/default/spamassassin

Restart spamassassin, and start up exim:

exim -bd -q15m

You should have the following processes running:

exim
spamd
courierlogger
couriertcpd
couriertcpd
courier-authlib/authdaemond
mysql

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: , , , , , , , , , , , , , , , ,



Leave a Comment