Table of Contents

Mail server setup with Exim, MySQL, Cyrus-Imapd, Horde webmail on Centos 5.1

Introduction

This how to describes the installation and configuration of a mail system on Centos 5.1 with selinux enabled for enhanced security. This system will be able to service HTTP, HTTPS, SMTP, TLS, SMTP-AUTH, IMAP, POP3 clients and is virtual enabled allowing more that one domain to be served from the system.

The webmail client imp will provide a feature rich interface with a webmail component, an address book, calendaring, and ability to reset passwords all with a highly configurable preference system to enable users to modify their look and feel as well as the operation of the interface.

The IMAP/POP3 system is the high performance cyrus-imapd system that runs as a sealed unit (mail users are not system users) and utilizes a high end/ performance back end for mail storage. The other cyrus-imapd features include, mail indexing for quick search operations using squat, a notification daemon that can be configured to notify via sms or email, sieve filtering system (auto response, mail filtering, notifications, filing), and built in quota system. This system will use pam_mysql via the SASL (saslauthd) mechanism to authenticate users against the Mysql database back end shared by all the components.

The exim system will be configured to enable users relay mail using TLS secured SMTP-AUTH, using the same database backend that is shared by all the components. Address verification will take place using SQL queries to the mysql backend before any mail is accepted for delivery to the cyrus-imapd mail store. Anti-virus checks are integrated into this system with the use of clamav via a Unix domain socket. Spam checks are also run at smtp time via a socket connection to the spamd spamassasin system messages scoring over 6 are automatically rejected with out being spooled to the system.

The mysql database will store the user authentication information encrypted using md5-hex as well as other information for the other webmail components.

This system is designed for high performance and security, cutting down on open ports and prefering to use unix domain sockets and running selinux in enforcing mode.

User account management takes place via the horde web interface allowing the admin easy access to add and remove accounts or to manage passwords, it is also possible to interact directly with the database table and make alterations there.

OS Installation notes

For the purpose of keeping this howto precise i will not take you through the actually installation of the Centos 5.1 system i will assume that you have a bare bones install, a kickstart to help you get a bare bones install is can be downloaded here

For this tutorial i will be using the static IP address 192.168.1.4 and the hostname mail.home.topdog-software.com with a domain of home.topdog-software.com, please feel free to make alterations to suite your environment. The working directory will be /usr/local/src all packages to be downloaded should be downloaded and extracted there.

Software installation

Install prerequisites

yum update
rpm -Uhv http://apt.sw.be/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
enabled = 0

Install Apache

yum install httpd php php-mysql php-xml php-imap php-mbstring php-mcrypt \
php-pecl-Fileinfo php-pear-DB php-pear-File php-pear-Log php-pear-Mail-Mime \
php-pear-Auth-SASL php-pear-Date php-pear-HTTP-Request php-pear-Mail php-pear-Net-Sieve \
php-pear-Net-Socket php-pear-Net-SMTP openssl mod_ssl -y

Install Exim

yum install exim system-switch-mail -y

Install Mysql

yum install mysql mysql-server -y

Install Horde

yum install horde imp-h3 ingo-h3 turba-h3 kronolith-h3 -y
wget ftp://ftp.horde.org/pub/passwd/passwd-h3-3.0.1.tar.gz
tar xzvf passwd-h3-3.0.1.tar.gz -C /usr/share/horde
mv /usr/share/horde/passwd-h3-3.0.1 /usr/share/horde/passwd

Install cyrus-imapd

I will use the Invoca systems source rpm as it is more recent and support many features that the Centos build does not provide.

yum install db4-utils -y
rpm -Uvh http://www.topdog-software.com/oss/cyrus-imapd/cyrus-imapd-perl-2.3.11-3.i386.rpm
rpm -Uvh http://www.topdog-software.com/oss/cyrus-imapd/cyrus-imapd-utils-2.3.11-3.i386.rpm
rpm -Uvh http://www.topdog-software.com/oss/cyrus-imapd/cyrus-imapd-2.3.11-3.i386.rpm

Install Pam_mysql

rpm -Uvh http://www.topdog-software.com/oss/pam_mysql/pam_mysql-0.7RC1-1.i386.rpm

Install ClamAV

yum --enablerepo=rpmforge install clamav clamav-db clamd -y

Install Spamassassin

yum install spamassassin -y

Configuration

Configure Apache

NameVirtualHost *:80
<VirtualHost *:80>
    ServerAdmin webmaster@home.topdog-software.com
</VirtualHost>
<VirtualHost *:80>
        Servername mail.home.topdog-software.com
        DocumentRoot /usr/share/horde
        ErrorLog logs/mail-error_log
        CustomLog logs/mail-access_log common
</VirtualHost>
#Alias /horde /usr/share/horde
<Directory /usr/share/horde>

    Options +FollowSymLinks
    php_admin_flag safe_mode off
    php_admin_flag magic_quotes_runtime off
    php_flag session.use_trans_sid off
    php_flag session.auto_start off
    php_admin_flag file_uploads on
    #php_admin_flag allow_url_fopen on

    php_value post_max_size 20M
    php_value upload_max_filesize 10M

    php_admin_value open_basedir "/usr/share/horde:/usr/share/horde/config:/usr/share/pear:/tmp"
    php_admin_flag register_globals off
</Directory>

<Directory /usr/share/horde/config>
    Order Deny,Allow
    Deny from all
</Directory>

<DirectoryMatch "^/usr/share/horde/(.*/)?(config|lib|locale|po|scripts|templates)/(.*)?">
    Order Deny,Allow
    Deny from all
</DirectoryMatch>
memory_limit = 64M
Servername mail.home.topdog-software.com:443
DocumentRoot /usr/share/horde

Configure Exim

 system-switch-mail (select exim)

Anti-virus / Sanesecurity Checks

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

RBL's

drop    message       = REJECTED because $sender_host_address is in a black list spamhaus.org
           dnslists      = zen.spamhaus.org

drop    message       = REJECTED because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
           dnslists      = bl.spamcop.net

drop    message       = REJECTED because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
           dnslists      = dnsbl.sorbs.net

Anti Spam

drop  message   = REJECTED - We don't accept messages from hosts without reverse DNS
        log_message = No reverse DNS
        domains = ! lsearch;/etc/exim/checks_exempt_hosts
        !verify = reverse_host_lookup
        !verify = sender/callout=2m,defer_ok
        !condition =  ${if eq{$sender_verify_failure}{}}
drop  message  = REFUSED - no HELO/EHLO greeting
        log_message = remote host did not present greeting
        condition = ${if def:sender_helo_name {false}{true}}
deny ratelimit = 250 / 15m / strict
       message = You can only send $sender_rate per $sender_rate_period
       log_message = RATE: $sender_rate/$sender_rate_period (max $sender_rate_limit)

accept
smtp_accept_max_nonmail = 30
smtp_max_unknown_commands = 1
pipelining_advertise_hosts = 
spamd_address = /var/run/spamassassin/spamd.sock
accept  condition  = ${if >={$message_size}{100000} {1}}
        add_header = X-Spam-Note: SpamAssassin run bypassed due to message size

  warn    spam       = nobody/defer_ok
        add_header = X-Spam-Flag: YES

  accept  condition  = ${if !def:spam_score_int {1}}
        add_header = X-Spam-Note: SpamAssassin invocation failed

  warn    add_header = X-Spam-Score: $spam_score ($spam_bar)\n\
#       X-Spam-Report: $spam_report

  drop    condition = ${if >{$spam_score_int}{60} {1}}
        message   = Your message scored $spam_score SpamAssassin point. Report follows:\n\
        $spam_report

Mail routing

hide mysql_servers = localhost/horde/horde/hordepassword
localuser:
  driver = accept
  local_parts = ${lookup mysql {SELECT REPLACE(user_uid,'${quote_mysql:@$domain}','') \
         as user FROM horde_users WHERE user_uid='${quote_mysql:$local_part@$domain}'}{$value}}
  transport = local_delivery
  cannot_route_message = Unknown user
local_delivery:
  driver = lmtp
  socket = /var/lib/imap/socket/lmtp
  batch_max = 50
  user = cyrus

SMTP Authentication


plain:
  driver = plaintext
  public_name = PLAIN
  server_prompts = :
  server_set_id = $2
  server_condition = ${if saslauthd{{$2}{$3}{pop}}{1}{0}}
  server_advertise_condition = true

login:
  driver = plaintext
  public_name = LOGIN
  server_prompts = "Username:: : Password::"
  server_condition = ${if saslauthd{{$1}{$2}{pop}}{1}{0}}
  server_set_id = $1
  server_advertise_condition = true

Full sample configuration

Download the full configuration file here

Configure Mysql

skip-networking
/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h impi password 'new-password' -p

Configure Horde

cp /usr/share/horde/scripts/sql/create.mysql.sql .
vi create.mysql.sql
REPLACE INTO user (host, user, password)
    VALUES (
        'localhost',
        'horde',
-- IMPORTANT: Change this password!
        PASSWORD('hordepassword')
);
mysql -p < create.mysql.sql
mysql -p horde < /usr/share/horde/turba/scripts/sql/turba_objects.mysql.sql
mysql -p horde < /usr/share/horde/kronolith/scripts/sql/kronolith.mysql.sql

Horde configuration

<?php
$conf['debug_level'] = E_ALL;
$conf['max_exec_time'] = 0;
$conf['compress_pages'] = true;
$conf['umask'] = 077;
$conf['use_ssl'] = 2;
$conf['server']['name'] = $_SERVER['SERVER_NAME'];
$conf['server']['port'] = $_SERVER['SERVER_PORT'];
$conf['session']['name'] = 'Horde';
$conf['session']['use_only_cookies'] = true;
$conf['session']['cache_limiter'] = 'nocache';
$conf['session']['timeout'] = 0;
$conf['cookie']['domain'] = $_SERVER['SERVER_NAME'];
$conf['cookie']['path'] = '/';
$conf['sql']['username'] = 'horde';
$conf['sql']['password'] = 'hordepassword';
$conf['sql']['socket'] = '/var/lib/mysql/mysql.sock';
$conf['sql']['protocol'] = 'unix';
$conf['sql']['database'] = 'horde';
$conf['sql']['charset'] = 'iso-8859-1';
$conf['sql']['phptype'] = 'mysqli';
$conf['auth']['admins'] = array('Administrator', 'andrew@home.topdog-software.com');
$conf['auth']['checkip'] = true;
$conf['auth']['checkbrowser'] = true;
$conf['auth']['alternate_login'] = false;
$conf['auth']['redirect_on_logout'] = false;
$conf['auth']['params']['driverconfig'] = 'horde';
$conf['auth']['params']['table'] = 'horde_users';
$conf['auth']['params']['username_field'] = 'user_uid';
$conf['auth']['params']['password_field'] = 'user_pass';
$conf['auth']['params']['encryption'] = 'md5-hex';
$conf['auth']['params']['show_encryption'] = false;
$conf['auth']['driver'] = 'sql';
$conf['signup']['allow'] = false;
$conf['log']['priority'] = PEAR_LOG_NOTICE;
$conf['log']['ident'] = 'HORDE';
$conf['log']['params'] = array();
$conf['log']['name'] = '/tmp/horde.log';
$conf['log']['params']['append'] = true;
$conf['log']['type'] = 'file';
$conf['log']['enabled'] = true;
$conf['log_accesskeys'] = false;
$conf['prefs']['params']['driverconfig'] = 'horde';
$conf['prefs']['driver'] = 'sql';
$conf['datatree']['params']['driverconfig'] = 'horde';
$conf['datatree']['driver'] = 'sql';
$conf['group']['driver'] = 'datatree';
$conf['cache']['default_lifetime'] = 1800;
$conf['cache']['params']['dir'] = Horde::getTempDir();
$conf['cache']['params']['gc'] = 86400;
$conf['cache']['driver'] = 'file';
$conf['token']['driver'] = 'none';
$conf['mailer']['params']['auth'] = '0';
$conf['mailer']['type'] = 'smtp';
$conf['vfs']['params']['driverconfig'] = 'horde';
$conf['vfs']['type'] = 'sql';
$conf['sessionhandler']['params']['persistent'] = false;
$conf['sessionhandler']['params']['rowlocking'] = true;
$conf['sessionhandler']['params']['socket'] = '/var/lib/mysql/mysql.sock';
$conf['sessionhandler']['params']['protocol'] = 'unix';
$conf['sessionhandler']['params']['hostspec'] = 'localhost';
$conf['sessionhandler']['params']['username'] = 'horde';
$conf['sessionhandler']['params']['password'] = 'hordepassword';
$conf['sessionhandler']['params']['database'] = 'horde';
$conf['sessionhandler']['type'] = 'mysql';
$conf['problems']['email'] = 'webmaster@home.topdog-software.com';
$conf['problems']['maildomain'] = 'home.topdog-software.com';
$conf['problems']['tickets'] = false;
$conf['menu']['apps'] = array();
$conf['menu']['always'] = true;
$conf['menu']['links']['help'] = 'authenticated';
$conf['menu']['links']['help_about'] = true;
$conf['menu']['links']['options'] = 'authenticated';
$conf['menu']['links']['problem'] = 'never';
$conf['menu']['links']['login'] = 'all';
$conf['menu']['links']['logout'] = 'authenticated';
$conf['hooks']['permsdenied'] = false;
$conf['hooks']['username'] = false;
$conf['hooks']['preauthenticate'] = false;
$conf['hooks']['postauthenticate'] = false;
$conf['hooks']['authldap'] = false;
$conf['portal']['fixed_blocks'] = array();
$conf['accounts']['driver'] = 'null';
$conf['imsp']['enabled'] = false;
$conf['kolab']['enabled'] = false;
$_prefs['initial_application'] = array(
    'value' => 'imp',
    'locked' => true,
    'shared' => true,
    'type' => 'select',
    'desc' => sprintf(_("What application should %s display after login?"), $GLOBALS['registry']->get('name'))
);
$this->applications['horde'] = array(
    'fileroot' => dirname(__FILE__) . '/..',
    'webroot' => '',
    'initial_page' => 'login.php',
    'name' => _("Horde"),
    'status' => 'active',
    'templates' => dirname(__FILE__) . '/../templates',
    'provides' => 'horde'
);

IMP configuration

<?php
$conf['utils']['spellchecker'] = '/usr/bin/aspell';
$conf['utils']['gnupg'] = '/usr/bin/gpg';
$conf['utils']['gnupg_keyserver'] = array('pgp.mit.edu');
$conf['utils']['gnupg_timeout'] = '10';
$conf['utils']['openssl_cafile'] = '/etc/pki/tls/certs';
$conf['utils']['openssl_binary'] = '/usr/bin/openssl';
$conf['menu']['apps'] = array('ingo', 'kronolith', 'passwd', 'turba');
$conf['user']['select_sentmail_folder'] = false;
$conf['user']['allow_resume_all_in_drafts'] = true;
$conf['user']['allow_folders'] = true;
$conf['user']['allow_resume_all'] = false;
$conf['user']['allow_view_source'] = true;
$conf['user']['alternate_login'] = false;
$conf['user']['redirect_on_logout'] = false;
$conf['server']['change_server'] = false;
$conf['server']['change_port'] = false;
$conf['server']['change_protocol'] = false;
$conf['server']['change_smtphost'] = false;
$conf['server']['change_smtpport'] = false;
$conf['server']['server_list'] = 'none';
$conf['server']['sort_limit'] = '0';
$conf['server']['cache_folders'] = false;
$conf['server']['cache_msgbody'] = true;
$conf['mailbox']['show_attachments'] = false;
$conf['mailbox']['show_preview'] = false;
$conf['mailbox']['show_xpriority'] = false;
$conf['fetchmail']['show_account_colors'] = false;
$conf['fetchmail']['size_limit'] = '4000000';
$conf['msgsettings']['filtering']['words'] = './config/filter.txt';
$conf['msgsettings']['filtering']['replacement'] = '****';
$conf['spam']['reporting'] = false;
$conf['notspam']['reporting'] = false;
$conf['msg']['prepend_header'] = true;
$conf['msg']['append_trailer'] = true;
$conf['compose']['allow_cc'] = true;
$conf['compose']['allow_bcc'] = true;
$conf['compose']['allow_receipts'] = true;
$conf['compose']['special_characters'] = true;
$conf['compose']['use_vfs'] = false;
$conf['compose']['link_attachments'] = false;
$conf['compose']['add_maildomain_to_unexpandable'] = false;
$conf['compose']['attach_size_limit'] = '0';
$conf['compose']['attach_count_limit'] = '0';
$conf['hooks']['vinfo'] = false;
$conf['hooks']['signature'] = false;
$conf['hooks']['trailer'] = false;
$conf['hooks']['fetchmail_filter'] = false;
$conf['hooks']['mbox_redirect'] = false;
$conf['hooks']['mbox_icon'] = false;
$conf['hooks']['spam_bounce'] = false;
$conf['maillog']['use_maillog'] = true;
$conf['tasklist']['use_tasklist'] = true;
$conf['notepad']['use_notepad'] = true;
<?php

$servers['cyrus'] = array(
    'name' => 'localserver',
    'server' => 'localhost',
    'hordeauth' => 'full',
    'protocol' => 'imap/notls',
    'port' => 143,
    'maildomain' => '',
    'smtphost' => 'localhost',
    'smtpport' => 25,
    'realm' => '',
    'preferred' => '',
    'admin' => array(
        'params' => array(
            'login' => 'cyrus',
            'password' => '',
            'userhierarchy' => 'user.',
            'protocol' => 'imap/notls',
            'hostspec' => 'localhost',
            'port' => 143
        )
    ),
    'quota' => array(
        'driver' => 'cyrus',
        'params' => array(),
    ),
    'acl' => array(
        'driver' => 'rfc2086',
    ),
);
$_prefs['compose_popup'] = array(
    'value' => 0,
    'locked' => true,
    'shared' => true,
    'type' => 'checkbox',
    'desc' => _("Compose messages in a separate window?"));

Kronolith configuration

<?php
$conf['calendar']['params']['table'] = 'kronolith_events';
$conf['calendar']['params']['driverconfig'] = 'horde';
$conf['calendar']['driver'] = 'sql';
$conf['storage']['params']['table'] = 'kronolith_storage';
$conf['storage']['params']['driverconfig'] = 'horde';
$conf['storage']['driver'] = 'sql';
$conf['metadata']['keywords'] = false;
$conf['reminder']['server_name'] = 'home.topdog-software.com';
$conf['reminder']['from_addr'] = 'postmaster@home.topdog-software.com';
$conf['autoshare']['shareperms'] = 'none';
$conf['menu']['print'] = true;
$conf['menu']['import_export'] = true;
$conf['menu']['apps'] = array('imp', 'ingo', 'kronolith', 'turba');

Turba configuration

<?php
$conf['menu']['import_export'] = true;
$conf['menu']['apps'] = array('imp', 'ingo', 'kronolith', 'turba');
$conf['client']['addressbook'] = 'localsql';
$conf['comments']['allow'] = true;
$conf['documents']['type'] = 'horde';

Ingo configuration

<?php
$conf['menu']['apps'] = array('imp', 'kronolith', 'turba');
$conf['storage']['driver'] = 'prefs';
$conf['storage']['maxblacklist'] = 0;
$conf['storage']['maxwhitelist'] = 0;
$conf['rules']['userheader'] = true;
$conf['rules']['usefolderapi'] = true
<?php
$backends['sieve'] = array(
    'driver' => 'timsieved',
    'preferred' => 'localhost',
    'hordeauth' => 'full',
    'params' => array(
        'hostspec' => 'localhost',
        'logintype' => 'PLAIN',
        'usetls' => true,
        'port' => 2000,
        'scriptname' => 'ingo',
    ),
    'script' => 'sieve',
    'scriptparams' => array()
);

Passwd configuration

<?php
$conf['menu']['apps'] = array('imp', 'ingo', 'kronolith', 'turba');
$conf['backend']['backend_list'] = 'hidden';
$conf['user']['change'] = true;
$conf['user']['refused'] = array('root', 'bin', 'daemon', 'adm', 'lp', 'shutdown',
'halt', 'uucp', 'ftp', 'anonymous', 'nobody', 'httpd', 'operator', 'guest', 'diginext', 'bind', 'cyrus', 'courier', 'games', 'kmem', 'mailnull', 'man', 'mysql', 'news', 'postfix', 'sshd', 'tty', 'www');
$conf['password']['strengthtests'] = false;
$conf['hooks']['full_name'] = true;
$conf['hooks']['default_username'] = false;
$conf['hooks']['username'] = false;
$conf['hooks']['userdn'] = false;
<?php
$backends['hordesql'] = array (
    'name' => 'Horde Authentication',
    'preferred' => '',
    'password policy' => array(
        'minLength' => 5,
        'maxLength' => 8,
        'maxSpace' => 0,
        'minUpper' => 1,
        'minLower' => 1,
        'minNumeric' => 1,
        'minSymbols' => 1
    ),
    'driver' => 'sql',
    'params' => array_merge($conf['sql'],
                            array('table' => 'horde_users',
                                  'user_col' => 'user_uid',
                                  'pass_col' => 'user_pass',
                                  'show_encryption' => false)),
);

Secure Horde installation

 chown apache:root -R /usr/share/horde/config
 chown apache:root -R /usr/share/horde/*/config
 chmod -R go-rwx /usr/share/horde/config
 chmod -R go-rwx /usr/share/horde/*/config
 chown -R root:root /usr/share/horde/scripts
 chown -R root:root /usr/share/horde/*/scripts
 chmod -R go-rwx /usr/share/horde/scripts
 chmod -R go-rwx /usr/share/horde/*/scripts
 chmod a-rwx /usr/share/horde/test.php
 chmod a-rwx /usr/share/horde/*/test.php
 find /usr/share/horde/ -iname readme -exec rm -f {} \;
 find /usr/share/horde/ -iname todo -exec rm -vf {} \;
 find /usr/share/horde/ -iname license -exec rm -vf {} \;
 find /usr/share/horde/ -iname copying -exec rm -vf {} \;
 find /usr/share/horde/ -iname docs -exec rm -vrf {} \;

Configure Cyrus-imapd

The cyrus-imapd system will have virtual hosting enabled, sieve scripts, quota's set to 10MB, auto creation (& auto subscription) of the mailbox with these folders (INBOX,sent-mail,drafts,spam,trash). Authentication of users will take place aganist the Mysql database via SASL using the saslauthd daemon.

configdirectory: /var/lib/imap
servername: TDS-IMAP/POP3
partition-default: /var/spool/imap
virtdomains: on
defaultdomain: localhost.localdomain
admins: andrew@home.topdog-software.com
postmaster: support@home.topdog-software.com
quotawarn: 85
lmtp_over_quota_perm_failure: 1
lmtp_strict_quota: 1
autocreatequota: 10240
createonpost: 1
autocreateinboxfolders: sent-mail|drafts|spam|trash
autosubscribeinboxfolders: sent-mail|drafts|spam|trash
autocreate_sieve_script: /etc/default_sieve
autocreate_sieve_compiledscript: /etc/default_sieve_script.bc
sievedir: /var/lib/imap/sieve
md5_dir: /var/lib/imap/md5
#sievenotifier: sms
#sendsms: /usr/bin/mysmsprog
sendmail: /usr/sbin/sendmail
hashimapspool: true
sasl_pwcheck_method: saslauthd
sasl_mech_list: PLAIN
allowplainwithouttls: 0
tls_cert_file: /etc/pki/cyrus-imapd/cyrus-imapd.pem
tls_key_file: /etc/pki/cyrus-imapd/cyrus-imapd.pem
tls_ca_file: /etc/pki/tls/certs/ca-bundle.crt
loglevel: info
START {
  # do not delete this entry!
  recover       cmd="ctl_cyrusdb -r"

  # this is only necessary if using idled for IMAP IDLE
  idled         cmd="idled"
  # replication
  # syncclient       cmd="/usr/lib/cyrus-imapd/sync_client -r"
}

# UNIX sockets start with a slash and are put into /var/lib/imap/sockets
SERVICES {
  # add or remove based on preferences
  imap          cmd="imapd" listen="imap" prefork=1 proto=tcp maxchild=100 maxfds=1000 provide_uuid=1
#  imaps                cmd="imapd -s" listen="imaps" prefork=1
  pop3          cmd="pop3d" listen="pop3" prefork=1 proto=tcp maxchild=100 maxfds=1000 provide_uuid=1
#  pop3s                cmd="pop3d -s" listen="pop3s" prefork=1
  sieve         cmd="timsieved" listen="localhost:sieve" prefork=0 proto=tcp maxfds=1000 provide_uuid=1

  # these are only necessary if receiving/exporting usenet via NNTP
#  nntp         cmd="nntpd" listen="nntp" prefork=3
#  nntps                cmd="nntpd -s" listen="nntps" prefork=1

  #fud
  # fud           cmd="fud" listen="fud" prefork=1 proto="udp"
  # at least one LMTP is required for delivery
#  lmtp         cmd="lmtpd" listen="lmtp" prefork=0
  lmtpunix      cmd="lmtpd" listen="/var/lib/imap/socket/lmtp" prefork=1 maxfds=1000 provide_uuid=1

  # this is only necessary if using notifications
  notify        cmd="notifyd" listen="/var/lib/imap/socket/notify" proto="udp" prefork=1
  # replication
}

EVENTS {
  # this is required
  checkpoint    cmd="ctl_cyrusdb -c" period=30 maxfds=1000

  # this is only necessary if using duplicate delivery suppression,
  # Sieve or NNTP
  delprune      cmd="cyr_expire -E 3" at=0400

  # this is only necessary if caching TLS sessions
  #tlsprune     cmd="tls_prune" at=0400
  squat         cmd="squatter"  period=30
}

Configure Pam_mysql

Pam_mysql will be used to authenticate the following cyrus-imapd services aganist the mysql database, IMAP,POP,SIEVE,LMTP,CSYNC.

Pam_mysql configuration

Enable pam_mysql for the services make the changes below.

auth       optional     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
account    required     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
auth       optional     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
account    required     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
auth       optional     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
account    required     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
auth       optional     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
account    required     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
auth       optional     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3
account    required     pam_mysql.so user=horde passwd=hordepassword host=/var/lib/mysql/mysql.sock db=horde table=horde_users usercolumn=user_uid passwdcolumn=user_pass crypt=3

Saslauthd configuration

SOCKETDIR=/var/run/saslauthd

# Mechanism to use when checking passwords.  Run "saslauthd -v" to get a list
# of which mechanism your installation was compiled to use.
MECH=pam

# Additional flags to pass to saslauthd on the command line.  See saslauthd(8)
# for the list of accepted flags.
FLAGS="-r -n 0 -c"

Configure ClamAV

usermod -G exim clamav
LocalSocket /var/run/clamav/clamd.socket
#TCPSocket 3310
#TCPAddr 127.0.0.1
wget http://www.sanesecurity.co.uk/clamav/update_sanesecurity.txt -O /usr/local/bin/update_sanesecurity.sh
chmod +x /usr/local/bin/update_sanesecurity.sh
ln -s /usr/local/bin/update_sanesecurity.sh /etc/cron.hourly/
/usr/local/bin/update_sanesecurity.sh
module clamdlocal 1.0;

require {
        type proc_t;
        type var_t;
        type sysctl_kernel_t;
        type var_spool_t;
        type clamd_t;
        class dir { write search read remove_name add_name };
        class file { write getattr read lock create unlink };
}

#============= clamd_t ==============
allow clamd_t proc_t:file { read getattr };
allow clamd_t sysctl_kernel_t:dir search;
allow clamd_t sysctl_kernel_t:file read;
allow clamd_t var_spool_t:dir read;
allow clamd_t var_spool_t:file { read getattr };
allow clamd_t var_t:dir { write read add_name remove_name };
allow clamd_t var_t:file { write getattr read lock create unlink };
checkmodule -M -m -o clamdlocal.mod clamdlocal.te
semodule_package -o clamdlocal.pp -m clamdlocal.mod
semodule -i clamdlocal.pp

Configure Spamassassin

SPAMDOPTIONS=" -l -d -c -m5 -H -m 10 --socketpath=/var/run/spamassassin/spamd.sock --socketowner=exim"
module spamdlocal 1.0;

require {
        type spamd_t;
        type spamd_var_run_t;
        class capability { fowner chown kill };
        class sock_file { write create unlink getattr setattr };
}

#============= spamd_t ==============
allow spamd_t self:capability { fowner chown kill };
allow spamd_t spamd_var_run_t:sock_file { write create unlink getattr setattr };
checkmodule -M -m -o spamdlocal.mod spamdlocal.te
semodule_package -o spamdlocal.pp -m spamdlocal.mod
semodule -i spamdlocal.pp

Final touches

Disable services

Disable unwanted services, use this script

Enable services

chkconfig --level 234 exim on
chkconfig --level 234 mysqld on
chkconfig --level 234 spamassassin on
chkconfig --level 234 clamd on
chkconfig --level 234 httpd on
chkconfig --level 234 saslauthd on
chkconfig --level 234 cyrus-imapd on
service mysqld restart
service saslauthd restart
service spamassassin restart
service clamd restart
service exim restart
service cyrus-imapd restart
service httpd restart

Create admin user

USE horde;

REPLACE INTO horde_users (user_uid,user_pass)
    VALUES (
        'andrew@home.topdog-software.com',
-- Change this
        md5('verystrongpassword'),
);
mysql -p horde < admin.sql

Firewall

Add these rules in your configuration file /etc/sysconfig/iptables

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

*filter
:FORWARD DROP [0:0]
:INPUT DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m multiport -j ACCEPT --dports 80,443,25,110,143
-A INPUT -p icmp -m icmp -m limit --icmp-type 8 --limit 5/min -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -s 192.168.1.4 -j ACCEPT
COMMIT

Login

Phew ! you are done. Open your browser and go to https://192.168.1.4/ and login with the details above, you can the create other users under administration → users. You can test all the other features as well.

References