Ok, here we go...

I needed to quickly configure a mailserver, initially - for a single domain (but probably will host more later).

I am using exim over the last two years, I don't like qmail because it's very old, and to add some new feature to it - requires patching it. BTW, did you ever try reading qmail's logs? :D
I don't like postfix, but this is probably because I didn't use it too often.

Also, as an Imap/POP3 server, I do like dovecot, easy to configure, yet very comfortable to use, and also quite stable.
Adding domains/users manually isn't too much fun, we need some sort of web interface too, to manage virtual domains, mailboxes, aliases.

Prerequisites:
- apache
- php
- mysql

I will leave this as an exercise to the reader :)

All this setup was done on a CentOS 5.3 system, so I'll give examples using yum to install the packages (on Fedora/RedHat you could use the same yum, on Debian/Ubuntu - use aptitude or apt-get)

First of all, create a MySQL database to store all the settings:

mysql> CREATE DATABASE vexim;
mysql> GRANT ALL ON vexim.* to vexim@localhost identified by 'kieHeed3ailuph8b';

Next, download the latest postfixadmin tarball from http://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-2.2.1.1/postfixadmin-2.2.1.1.tar.gz, and unpack it to /var/www/html/mailadmin/

Adjust: /var/www/html/mailadmin/config.inc.php, more exactly, the following section:

...
// Database Config
// mysql = MySQL 3.23 and 4.0
// mysqli = MySQL 4.1
// pgsql = PostgreSQL
$CONF['database_type'] = 'mysql';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'vexim';
$CONF['database_password'] = 'kieHeed3ailuph8b';
$CONF['database_name'] = 'vexim';
$CONF['database_prefix'] = '';...

We will need clamd installed, in order to enable virus checks in exim. Debian/Ubuntu have this in the base repositories, CentOS - doesn't. We can use the rpmforge packages, see http://dag.wieers.com/rpm/FAQ.php#B2 on how to add the rpmforge repositories to your system.

Install clamd:

[root@server #] yum install clamd

Adjust the following parameters in clamd settings:

#In /etc/clamd.conf:
...
User exim
...
#In /etc/freshclam.conf:
...
DatabaseOwner exim
...

Adjust permissions/ownership:

[root@server #] chown -R exim:exim /var/log/clamav/
[root@server #] chown -R exim:exim /var/clamav/

Now on to exim, dovecot and postfixadmin.
Install exim and dovecot from repositories,

[root@server #] yum install dovecot exim
[root@server #] mv /etc/exim/exim.conf /etc/exim/exim.conf-orig
[root@server #] mv /etc/dovecot.conf /etc/dovecot.conf-orig

Sample config used for exim, this doesn't include spam filtering, or rbl checks, but this can be easily added:

primary_hostname = mail.eulinux.org
hide mysql_servers = localhost/vexim/vexim/kieHeed3ailuph8b

domainlist local_domains = ${lookup mysql{SELECT `domain` \
                            FROM `domain` WHERE \
                            `domain`='${quote_mysql:$domain}' AND \
                            `active`='1'}}

domainlist relay_to_domains = ${lookup mysql{SELECT `domain` \
                            FROM `domain` WHERE \
                            `domain`='${quote_mysql:$domain}' AND \
                            `active`='1'}}

hostlist   relay_from_hosts = localhost:127.0.0.0/8:192.168.0.0/16  #add the hosts from which you allow relaying here

acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data

av_scanner = clamd:/var/run/clamav/clamd.sock

# spamd_address = 127.0.0.1 783

qualify_domain = mail.playlainc.com
qualify_recipient = mail.playlainc.com
allow_domain_literals = false
exim_user = exim
exim_group = exim
never_users = root
rfc1413_query_timeout = 0s

sender_unqualified_hosts = +relay_from_hosts
recipient_unqualified_hosts = +relay_from_hosts

ignore_bounce_errors_after = 45m
timeout_frozen_after = 15d
helo_accept_junk_hosts = 192.168.0.0/16
auto_thaw = 1h
smtp_banner = "$primary_hostname, ESMTP EXIM $version_number"
smtp_accept_max = 50
smtp_accept_max_per_connection = 25
smtp_connect_backlog = 30
smtp_accept_max_per_host = 20
split_spool_directory = true
remote_max_parallel = 15
return_size_limit = 70k
message_size_limit = 64M
helo_allow_chars = _
smtp_enforce_sync = true

log_selector = \
    +all_parents \
    +connection_reject \
    +incoming_interface \
    +lost_incoming_connection \
    +received_sender \
    +received_recipients \
    +smtp_confirmation \
    +smtp_syntax_error \
    +smtp_protocol_error \
    -queue_run

syslog_timestamp = no

begin acl

acl_check_rcpt:
  accept  hosts = :
  deny    message       = "incorrect symbol in address"
          domains       = +local_domains
          local_parts   = ^[.] : ^.*[@%!/|]

  deny    message       = "incorrect symbol in address"
          domains       = !+local_domains
          local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./

  accept  local_parts   = postmaster
          domains       = +local_domains

  deny    message       = "HELO/EHLO required by SMTP RFC"
          condition     = ${if eq{$sender_helo_name}{}{yes}{no}}

  accept  authenticated = *

  deny    message       = "Your IP in HELO - access denied!"
          hosts         =  * : !+relay_from_hosts : !81-196.lissyara.su
          condition     = ${if eq{$sender_helo_name}\
    {$sender_host_address}{true}{false}}

  deny    condition     = ${if eq{$sender_helo_name}\
    {$interface_address}{yes}{no}}
          hosts         = !127.0.0.1 : !localhost : *
          message       = "My IP in your HELO! Access denied!"

  deny    condition     = ${if match{$sender_helo_name}\
    {\N^\d+$\N}{yes}{no}}
          hosts         = !127.0.0.1 : !localhost : *
          message       = "Incorrect HELO string"

# filter spammers from dynamic ips
  deny    message       = "your hostname is bad (adsl, poll, ppp & etc)."
          condition     = ${if match{$sender_host_name} \
                               {adsl|dialup|pool|peer|dhcp} \
                               {yes}{no}}

  warn
        set acl_m0 = 30s
  warn
        hosts = +relay_from_hosts:4.3.2.1/32:192.168.0.0/16 #disable waits for 'friendly' hosts
        set acl_m0 = 0s
  warn
        logwrite = Delay $acl_m0 for $sender_host_name \
[$sender_host_address] with HELO=$sender_helo_name. Mail \
from $sender_address to $local_part@$domain.
        delay = $acl_m0


  accept  domains       = +local_domains
          endpass
          message       = "No such user"
          verify        = recipient

  accept  domains       = +relay_to_domains
          endpass
          message       = "i don't know how to relay to this address"
          verify        = recipient

#  deny    message       = "you in blacklist - $dnslist_domain \n $dnslist_text"
#          dnslists      = opm.blitzed.org : \
#                          cbl.abuseat.org : \
#                          bl.csma.biz

  accept  hosts         = +relay_from_hosts

  deny    message       = "Homo hominus lupus est"

acl_check_data:

  # check for viruses
  deny malware = *
  message = "Your message contains viruses: $malware_name"

  # if needed - add spam filtering here

  # permit everything else
  accept

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

system_aliases:
    driver      = redirect
    allow_fail
    allow_defer
    data = ${lookup mysql{SELECT `goto` FROM `alias` WHERE \
            `address`='${quote_mysql:$local_part@$domain}' OR \
                `address`='${quote_mysql:@$domain}'}}

dovecot_user:
  driver = accept
  condition = ${lookup mysql{SELECT `goto` FROM \
  `alias` WHERE \
  `address`='${quote_mysql:$local_part@$domain}' OR \
  `address`='${quote_mysql:@$domain}'}{yes}{no}}
  transport = dovecot_delivery

begin transports

remote_smtp:
  driver = smtp
  interface = 4.3.2.1 #your public ip address, if it's the main server ip - you could skip this parameter. if it's an alias- it must be specified in clear


dovecot_delivery:
  driver = pipe
  command = /usr/libexec/dovecot/deliver -d $local_part@$domain
  message_prefix =
  message_suffix =
  delivery_date_add
  envelope_to_add
  return_path_add
  log_output
  user = exim

address_pipe:
  driver = pipe
  return_output

address_reply:
  driver = autoreply

begin retry

*                    *       F,2h,15m; G,16h,1h,1.5; F,4d,6h

begin rewrite

begin authenticators

auth_plain:
  driver = plaintext
  public_name = PLAIN
  server_condition = ${lookup mysql{SELECT `username` FROM \
                     `mailbox` WHERE `username` = \
                     '${quote_mysql:$auth2}' AND `password` = \
                     '${quote_mysql:$auth3}'}{yes}{no}}
  server_prompts = :
  server_set_id = $auth2

auth_login:
  driver = plaintext
  public_name = LOGIN
  server_condition = ${lookup mysql{SELECT `username` FROM \
                     `mailbox` WHERE `username` = \
                     '${quote_mysql:$auth1}' AND `password` = \
                     '${quote_mysql:$auth2}'}{yes}{no}}
  server_prompts = Username:: : Password::
  server_set_id = $auth1

auth_cram_md5:
  driver = cram_md5
  public_name = CRAM-MD5
  server_secret = ${lookup mysql{SELECT `password` FROM \
                        `mailbox` WHERE `username` \
                        = '${quote_mysql:$auth1}'}{$value}fail}
  server_set_id = $auth2

Now, on to dovecot:

#File: /etc/dovecot.conf
base_dir = /var/run/dovecot/
protocols = imap pop3

listen = *
disable_plaintext_auth = no
shutdown_clients = yes
log_timestamp = "%b %d %H:%M:%S "
syslog_facility = mail
ssl_disable = yes

login_dir = /var/run/dovecot/login
login_chroot = no

login_process_size = 64
login_process_per_connection = yes
login_processes_count = 3
login_max_processes_count = 128
login_max_connections = 256
login_greeting = Dovecot ready man.
login_log_format_elements = user=<%u> method=%m rip=%r lip=%l %c
login_log_format = %$: %s
mail_location = maildir:~/home/vmail/%d/%n

first_valid_uid=93
last_valid_uid=0

protocol imap {
  mail_plugins = quota imap_quota
  imap_client_workarounds = outlook-idle netscape-eoh tb-extra-mailbox-sep
}

protocol pop3 {
  pop3_uidl_format = %08Xu%08Xv
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
}

protocol lda {
  postmaster_address = admin@eulinux.org
  auth_socket_path = /var/run/dovecot/auth-master
}

auth default {
  mechanisms = plain
  passdb pam {
  }

  passdb sql {
    args = /etc/dovecot-mysql.conf
  }

  userdb passwd {
  }

  userdb sql {
    args = /etc/dovecot-mysql.conf
  }
  user = root
  socket listen {
    master {
      path = /var/run/dovecot/auth-master
      mode = 0660
      user = exim
      group = exim
    }
  }
}

dict {
}

plugin {
}


#File: /etc/dovecot-mysql.conf
driver = mysql
connect = host=localhost dbname=vexim user=vexim password=kieHeed3ailuph8b
default_pass_scheme = CRYPT
password_query = SELECT `username` as `user`, `password` FROM  `mailbox` WHERE `username` = '%n@%d' AND `active`='1'
user_query = SELECT "" AS `home`, 93 AS `uid`, 93 AS `gid` FROM `mailbox` WHERE `username` = '%n@%d' AND `active`='1'

Now, start the services:

for srv in exim dovecot clamd; do service ${srv} start; done

Access your postfixadmin configuration file at: http://hostname/mailadmin/setup.php

You should see a list of 'OK' messages.

The setup.php script will attempt to create the database structure (or upgrade it if you're coming from a previous version).

Assuming everything is OK you can create the admin user using the form displayed. Once you submit the form, all that's left to do is to delete "setup.php"

That seems to be all! ;)

If you notice any errors or have any questions on this - please comment

Comments