Wednesday, March 31, 2010

GroupOffice Virtual Mail Hosting on CentOS 5.4 / RHEL 5.4

SkyHi @ Wednesday, March 31, 2010


This following set of pages will explain how to set up a full blown
mail server for virtual mail hosting. This setup should work on every
distribution, you'll just need to tweak paths and files accordingly.
I've completed a guide (below) for each distribution I've performed
this setup on. The only one missing is Gentoo :(

The described setup is the result of numerous virtual mail server
installs and a lot of trial and error with a lot of different
applications. It is therefore not the only way to achieve this kind of
design, but through my experience, this is likely both one of the
easiest and best possible ways of implementing this design.

This howto / guide / documentation project also assumes a few
things. It assumes that you are not new to Linux and have a fairly
basic understanding of permissions and how to troubleshoot problems,
because **you will** run into problems doing this. It also assumes you
know how to use an internet search engine and the 'man' command.

Diagram and Overview of Complete System

To start off, below is a diagram of how all the components of our complete virtual mail system fit together:

Diagram of mail flow (incoming). Outgoing is pretty much the same thing.


As you can see from the above diagram, all components of our email
system plug into MySQL and read all our user account and domain
information from a database(s). This makes administration very easy as
all info is stored centrally in the database. The only thing this setup
potentially lacks is a front end to make user management easier. I'm no
web developer but it shouldn't be too difficult to build into a freely
available CMS.

vmail User, Group, Storage and Permissions

Create the mount point or storage directory and set SELinux type.

Create a new group called "vmail" and assign a group id of 1000.

Create a new user called "vmail" and assign a user id of 1000.

[root@CentOS-1 ~]# mkdir /data/mail
[root@CentOS-1 ~]# semanage fcontext -a -t public_content_rw_t '/data(/.*)?'
[root@CentOS-1 ~]# groupadd -g 1000 vmail
[root@CentOS-1 ~]# useradd -u 1000 -d /data/mail -s /sbin/nologin -g vmail vmail
[root@CentOS-1 ~]# chown vmail:vmail /data/mail -Rf

//Suse by default adds the user 'vmail' to other groups. Remove the 'vmail' user from those groups by editing /etc/group//

If you are using SELinux (And I hope you are) then you will need to
set the correct SE Linux permissions. If you don't, our mail server
will not be able to read and write to the appropriate directories. That
said, this part of the guide is not something you'll have done every
day if ever, so pay attention.

Check to see if SE Linux is enforcing.

[root@CentOS-1 /]# getenforce<br />Enforcing

If it is, set it to permissive. This will still log all incorrect
permissions. We require these logged to rectify the permissions later.

[root@CentOS-1 ~]# setenforce 0<br />[root@CentOS-1 ~]# getenforce<br />Permissive

We'll now continue the setup, and right at the end, build the custom modules required to make SE Linux work.

Required Packages

This is the list of packages required for this setup.

You might want to enable additional repositories which are disabled by default in CentOS. Not sure about RedHat.

[root@CentOS-1 ~]# sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/CentOS-Base.repo

Not all of these packages come from the default CentOS repositories.
Mainly 3 of the main packages we need: amavisd-new, spamassasin, and

amavisd-new we'll get from the home page. We want this to be the original.

Clam Antivirus from

Spamassasing by typing: cpan Mail::SpamAssassin

altermime<br />amavisd-new<br />arc<br />arj<br />aspell<br />aspell-en<br />autoconf<br />automake<br />bzip2<br />bzip2-libs<br />clamav<br />clamav-db<br />clamd<br />cpp<br />cyrus-sasl<br />cyrus-sasl-lib<br />cyrus-sasl-lib<br />cyrus-sasl-md5<br />cyrus-sasl-md5<br />cyrus-sasl-plain<br />cyrus-sasl-plain<br />db4<br />dovecot<br />gawk<br />gcc<br />gcc-c++<br />gzip<br />httpd<br />mailcap<br />mailx<br />mutt<br />mysql<br />mysql-server<br />ntp<br />openssl<br />openssl-perl<br />perl<br />perl-Archive-Tar<br />perl-Archive-Zip<br />perl-BSD-Resource<br />perl-BerkeleyDB<br />perl-Compress-Zlib<br />perl-Convert-BinHex<br />perl-Convert-TNEF<br />perl-Convert-UUlib<br />perl-Crypt-OpenSSL-RSA<br />perl-DBD-mysql<br />perl-DBI<br />perl-Digest-HMAC<br />perl-Digest-SHA<br />perl-Digest-SHA1<br />perl-Error<br />perl-HTML-Parser<br />perl-HTML-Tagset<br />perl-IO-Socket-INET6<br />perl-IO-Socket-SSL<br />perl-IO-Zlib<br />perl-IO-stringy<br />perl-MIME-tools<br />perl-Mail-DKIM<br />perl-Mail-SPF<br />perl-Mail-SPF-Query<br />perl-MailTools<br />perl-Net-CIDR-Lite<br />perl-Net-DNS<br />perl-Net-IP<br />perl-Net-SSLeay<br />perl-Net-Server<br />perl-NetAddr-IP<br />perl-Socket6<br />perl-String-CRC32<br />perl-Sys-Hostname-Long<br />perl-TimeDate<br />perl-URI<br />perl-Unix-Syslog<br />perl-libwww-perl<br />perl-version<br />php<br />php-cli<br />php-common<br />php-gd<br />php-imap<br />php-ldap<br />php-mbstring<br />php-mcrypt<br />php-mysql<br />php-pdo<br />php-pear<br />php-pecl-fileinfo<br />php-xml<br />policyd<br />popt<br />postfix<br />procmail<br />setroubleshoot<br />spamassassin<br />system-switch-mail<br />tar<br />unrar<br />unzip<br />zip


Set the root password if you have not already done so.

[root@server ~]# mysql -uroot<br />mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('choose_a_root_password');

Create our virtual mail databases

mysql> create database amavisd;<br />Query OK, 1 row affected (0.00 sec)<br /><br />mysql> GRANT ALL PRIVILEGES ON `amavisd` . * TO 'amavis_user'@'localhost' IDENTIFIED BY 'choose_a_password';<br />Query OK, 0 rows affected (0.00 sec)<br /><br />mysql> GRANT ALL PRIVILEGES ON `amavisd` . * TO 'amavis_user'@'' IDENTIFIED BY 'choose_a_password';<br />Query OK, 0 rows affected (0.00 sec)
mysql> create database groupoffice;<br />Query OK, 1 row affected (0.00 sec)<br /><br />mysql> GRANT ALL PRIVILEGES ON `groupoffice` . * TO 'vmail_user'@'localhost' IDENTIFIED BY 'choose_a_password';<br />Query OK, 0 rows affected (0.00 sec)<br /><br />mysql> GRANT ALL PRIVILEGES ON `groupoffice` . * TO 'vmail_user'@'' IDENTIFIED BY 'choose_a_password';<br />Query OK, 0 rows affected (0.00 sec)

Flush privileges

mysql> FLUSH PRIVILEGES;<br />Query OK, 0 rows affected (0.02 sec)


GroupOffice will provide us with a front end for administering our
mail domains, users, and aliases, as well as providing users with a
mail front end offering calenders, file sharing and address book.


Go to the group office website and follow the download links to get
the limited version from sourceforge. If you have a budget, you can buy
the full version which offers additional features like syncML for
mobile devices.

Download and install GroupOffice following the installation guide
which will come with the download. Use the database we set up in the
above step. Once installed install the Postfix Admin module, imapauth
module as well as the server client module. Once complete, resume this


Installing Postfix on CentOS

Because the standard CentOS Postfix doesn't come with support for
MySQL, you'll need to compile it yourself. Don't worry, this is a lot
easier than it might sound. (I'm yet to understand why RedHat, and in
turn CentOS do a lot of silly things like not compiling MySQL support
into many things and having bluetooth start at boot by default. How
many servers in your data centre have you got with bluetooth?)

Source code can be downloaded from:

See also:

Extract and cd to the extracted directory.

On x86_64

[root@CentOS-1 ~]# tar zxf postfix-2.5.5.tar.gz<br />[root@CentOS-1 ~]# cd postfix-2.5.5<br />[root@CentOS-1 postfix-2.6.5]# make makefiles CCARGS='-DUSE_SASL_AUTH -DUSE_CYRUS_SASL -DUSE_SSL -DUSE_TLS -DHAS_MYSQL -DHAS_LDAP \<br />-I/usr/include/sasl -I/usr/include/openssl -I/usr/include/mysql -I/usr/include' AUXLIBS='-L/usr/lib64 \<br />-L/usr/lib64/openssl/engines -L/usr/lib64/mysql -L/usr/lib64 -lsasl2 -lcrypto -lssl -lmysqlclient -lz -lm -lldap -llber'<br />[root@CentOS-1 postfix-2.6.5]# make<br />[root@CentOS-1 postfix-2.6.5]# make install

On i686

[root@CentOS-1 ~]# tar zxf postfix-2.5.5.tar.gz<br />[root@CentOS-1 ~]# cd postfix-2.5.5<br />[root@CentOS-1 postfix-2.6.5]# make makefiles CCARGS='-DUSE_SASL_AUTH -DUSE_CYRUS_SASL -DUSE_SSL -DUSE_TLS -DHAS_MYSQL -DHAS_LDAP \<br />-I/usr/include/sasl -I/usr/include/openssl -I/usr/include/mysql -I/usr/include' AUXLIBS='-L/usr/lib \<br />-L/usr/lib/openssl/engines -L/usr/lib/mysql -L/usr/lib -lsasl2 -lcrypto -lssl -lmysqlclient -lz -lm -lldap -llber'<br />[root@CentOS-1 postfix-2.6.5]# make<br />[root@CentOS-1 postfix-2.6.5]# make install

Because RHEL / CentOS use sendmail as the default MTA, install


and run it. You should be able to switch your MTA to postfix and then remove sendmail.

SSL Certificates for TLS

We'll start by generating the SSL certificates required for TLS:

mkdir /etc/postfix/ssl<br />cd /etc/postfix/ssl/<br />openssl genrsa -aes256 -rand /var/log/messages -out smtpd.key 2048
chmod 600 smtpd.key<br />openssl req -new -key smtpd.key -out smtpd.csr
openssl x509 -req -days 9999 -in smtpd.csr -signkey smtpd.key -out smtpd.crt
openssl rsa -in smtpd.key -out smtpd.key.unencrypted
mv -f smtpd.key.unencrypted smtpd.key
openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 9999

Postfix config

Edit /etc/postfix/ and look for the smtp line.
(Normally at the top) and add " -v" to the end of the line. This makes
the smtp service verbose (useful for troubleshooting.) Don't forget to
remove it when everything is up and running as I will not remind you at
the end of this guide. It should look like this:

smtp      inet  n       -       n       -       -       smtpd -v

also need to add Dovecot LDA (Local Delivery Agent) and amavis settings
to postfix config. Dovecot because we want postfix to use Dovecot for
delivery to the mailbox and amavis so that postfix knows to pass email
to it for content scanning. In addition to adding amavis, we need to
tell postfix to listen on port 10025 so that amavis can pass the
scanned email back to postfix for delivery.

#<br /># Postfix master process configuration file.  For details on the format<br /># of the file, see the Postfix master(5) manual page.<br />#<br /># ==========================================================================<br /># service type  private unpriv  chroot  wakeup  maxproc command + args<br />#               (yes)   (yes)   (yes)   (never) (100)<br /># ==========================================================================<br /><div class="important"><strong>smtp      inet  n       -       n       -       -       smtpd -v</strong></div>#        -o content_filter=smtp-amavis:[]:10024<br />#       -o<br />#submission inet n      -       n       -       -       smtpd<br />#       -o smtpd_etrn_restrictions=reject<br />#       -o smtpd_client_restrictions=permit_sasl_authenticated,reject<br />#smtps    inet  n       -       n       -       -       smtpd<br />#  -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes<br />#submission   inet    n       -       n       -       -       smtpd<br />#  -o smtpd_etrn_restrictions=reject<br />#  -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes<br />#628      inet  n       -       n       -       -       qmqpd<br />pickup    fifo  n       -       n       60      1       pickup<br />cleanup   unix  n       -       n       -       0       cleanup<br />qmgr      fifo  n       -       n       300     1       qmgr<br />#qmgr     fifo  n       -       n       300     1       oqmgr<br />tlsmgr    unix  -       -       n       1000?   1       tlsmgr<br />rewrite   unix  -       -       n       -       -       trivial-rewrite<br />bounce    unix  -       -       n       -       0       bounce<br />defer     unix  -       -       n       -       0       bounce<br />trace     unix  -       -       n       -       0       bounce<br />verify    unix  -       -       n       -       1       verify<br />flush     unix  n       -       n       1000?   0       flush<br />proxymap  unix  -       -       n       -       -       proxymap<br />smtp      unix  -       -       n       -       -       smtp<br /># When relaying mail as backup MX, disable fallback_relay to avoid MX loops<br />relay     unix  -       -       n       -       -       smtp<br />        -o fallback_relay=<br />#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5<br />showq     unix  n       -       n       -       -       showq<br />error     unix  -       -       n       -       -       error<br />discard   unix  -       -       n       -       -       discard<br />local     unix  -       n       n       -       -       local<br />virtual   unix  -       n       n       -       -       virtual<br />lmtp      unix  -       -       n       -       -       lmtp<br />anvil     unix  -       -       n       -       1       anvil<br />scache    unix  -       -       n       -       1       scache<br />#<br /># ====================================================================<br /># Interfaces to non-Postfix software. Be sure to examine the manual<br /># pages of the non-Postfix software to find out what options it wants.<br />#<br /># Many of the following services use the Postfix pipe(8) delivery<br /># agent.  See the pipe(8) man page for information about ${recipient}<br /># and other message envelope options.<br /># ====================================================================<br />#<br /># maildrop. See the Postfix MAILDROP_README file for details.<br /># Also specify in maildrop_destination_recipient_limit=1<br />#<br />#maildrop  unix  -       n       n       -       -       pipe<br />#  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}<br />#  flags=Ru user=vmail argv=/usr/bin/maildrop -d ${recipient}<br /><br /># Dovecot LDA<br /><div class="important">dovecot   unix  -       n       n       -       -       pipe<br />        flags=DRhu user=vmail argv=/usr/libexec/dovecot/deliver -d ${recipient}<br /><br />smtp-amavis   unix   -   -      y       -       2       smtp<br />        -o smtp_data_done_timeout=1200<br />        -o disable_dns_lookups=yes<br />        -o smtp_send_xforward_command=yes<br />#        -o max_use=20<br /><br /> inet  n  -      n       -       -       smtpd<br />        -o content_filter=<br />        -o local_recipient_maps=<br />        -o relay_recipient_maps=<br />        -o smtpd_restriction_classes=<br />        -o smtpd_client_restrictions=<br />        -o smtpd_helo_restrictions=<br />        -o smtpd_sender_restrictions=<br />        -o smtpd_recipient_restrictions=permit_mynetworks,reject<br />        -o mynetworks=<br />        -o strict_rfc821_envelopes=yes<br />        -o smtpd_error_sleep_time=0<br />        -o smtpd_soft_error_limit=1001<br />        -o smtpd_hard_error_limit=1000<br /><br />retry     unix  -       -       n       -       -       error<br /><br />proxywrite unix -       -       n       -       1       proxymap<br /><br />policy    unix  -       n       n       -       -       spawn<br />        user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl</div><br />

Postfix config

Edit /etc/postfix/ and make it look like mine, changing the obvious stuff like domain name etc.

Also be aware, that any line starting with a space is assumed to be a part of the previous line.

I will also not be explaining what each line of the config means
because this is clearly documented on the postfix website. Paste any
line you're unsure about into a search engine and RTFM.

myhostname =<br />mydomain   =<br />myorigin   = $mydomain<br />mail_name  = Farm Yard Mail<br />mail_version = 111.111<br /><br />content_filter = smtp-amavis:[]:10024<br /><br />#queue_directory   = /var/spool/postfix<br />#command_directory = /usr/sbin<br />#daemon_directory  =<br />mail_owner = postfix<br />default_privs     = nobody<br /><br />inet_interfaces  = all<br />inet_protocols   = all<br /># mydestination    = localhost,<br />mynetworks_style = host<br />mynetworks       =<br /><br />unknown_local_recipient_reject_code = 550<br />local_recipient_maps = $virtual_mailbox_maps $virtual_alias_maps unix:passwd.byname<br />alias_maps = $virtual_alias_maps<br /><br />in_flow_delay       = 1s<br />recipient_delimiter = +<br />home_mailbox        = Maildir/<br /><br />mail_spool_directory = /var/spool/mail<br />#mailbox_transport    = dovecot<br />#local_transport      = dovecot<br />smtpd_banner         = $myhostname ESMTP $mail_name version $mail_version<br /><br />debugger_command =<br />         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin<br />         xxgdb $daemon_directory/$process_name $process_id & sleep 5<br /><br />sendmail_path = /usr/sbin/sendmail<br />newaliases_path = /usr/bin/newaliases<br />mailq_path = /usr/bin/mailq<br /><br />setgid_group = postdrop<br /><br />html_directory = /usr/share/doc/packages/postfix/html<br />manpage_directory = /usr/share/man<br />sample_directory = /usr/share/doc/packages/postfix/samples<br />readme_directory = /usr/share/doc/packages/postfix/README_FILES<br /><br />biff = no<br /><br /># Restric message size to 20MB<br />message_size_limit              = 20971520<br /><br />smtpd_sasl_local_domain         = $mydomain,<br />smtpd_sasl_auth_enable          = yes<br />smtpd_sasl2_auth_enable         = yes<br />broken_sasl_auth_clients        = yes<br />smtpd_sasl_authenticated_header = yes<br /><br />smtpd_helo_restrictions         = permit_mynetworks<br />                                permit_sasl_authenticated<br />                                warn_if_reject<br />                                reject_non_fqdn_hostname<br />                                reject_invalid_hostname<br /><br />smtpd_sender_restrictions       = reject_unauth_pipelining<br />                                reject_unknown_sender_domain<br />                                reject_non_fqdn_sender<br /><br />smtpd_recipient_restrictions    = permit_mynetworks<br />                                permit_sasl_authenticated<br />                                permit_auth_destination<br />                                permit_mx_backup<br />                                reject_unauth_destination<br />                                reject_unlisted_recipient<br />                                reject_non_fqdn_recipient<br />                                reject_unknown_reverse_client_hostname<br />                                check_policy_service unix:private/policy<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br /><br />smtpd_client_restrictions       = permit_mynetworks<br />                                permit_sasl_authenticated<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br />                                reject_rbl_client<br /><br />smtpd_tls_auth_only             = no<br />smtp_use_tls                    = yes<br />smtpd_use_tls                   = yes<br />smtp_tls_note_starttls_offer    = yes<br />smtpd_tls_key_file              = /etc/pki/tls/certs/smtpd.key<br />smtpd_tls_cert_file             = /etc/pki/tls/certs/smtpd.crt<br />smtpd_tls_CAfile                = /etc/pki/tls/certs/ca-bundle.crt<br />smtpd_tls_loglevel              = 1<br />smtpd_tls_received_header       = yes<br />smtpd_tls_session_cache_timeout = 3600s<br />tls_random_source               = dev:/dev/urandom<br /><br />smtpd_helo_required     = yes<br />disable_vrfy_command    = yes<br />smtpd_data_restrictions = reject_unauth_pipelining<br />smtpd_etrn_restrictions = reject<br /><br />show_user_unknown_table_name = no<br /><br />transport_maps  = mysql:/etc/postfix/<br /># relay_domains   = mysql:/etc/postfix/<br /># relay_transport = relay<br /># relay_recipient_maps =<br /># transport_maps  = hash:/etc/postfix/transport<br /># relay_domains   = $transport_maps<br /><br /><br />virtual_mailbox_base    = /data/mail<br />virtual_minimum_uid     = 1000<br />virtual_uid_maps        = static:1000<br />virtual_gid_maps        = static:1000<br />virtual_transport       = dovecot<br />virtual_alias_maps      = proxy:mysql:/etc/postfix/<br />virtual_mailbox_domains = proxy:mysql:/etc/postfix/<br />virtual_mailbox_maps    = proxy:mysql:/etc/postfix/<br /><br /><br /># set the domain you'll be testing from.<br /># All email from this domain will be logged at debug level.<br /># Remember to turn off afterwards.<br />debug_peer_list = somedomain.tld<br /><br />command_directory = /usr/sbin<br />daemon_directory = /usr/libexec/postfix<br />queue_directory = /var/spool/postfix<br />data_directory = /var/lib/postfix<br />

Postfix MySQL config

In the above config we've set postfix to use mysql for certain
lookups, namely virtual domains, aliases and mailboxes. We now need to
configure those SQL queries.


#<br />user = vmail_user<br />password = choose_a_password<br />hosts = localhost<br />dbname = groupoffice<br />query = SELECT transport FROM pa_domains WHERE domain='%d' AND active='1'


#<br />user = vmail_user<br />password = choose_a_password<br />hosts = localhost<br />dbname = groupoffice<br />query = SELECT goto FROM pa_aliases WHERE address='%s' AND active='1'<br />#expansion_limit = 100


#<br />user = vmail_user<br />password = choose_a_password<br />hosts = localhost<br />dbname = groupoffice<br />query = SELECT domain FROM pa_domains WHERE domain='%u' AND transport='virtual' AND backupmx='0' AND active='1'


#<br />user = vmail_user<br />password = choose_a_password<br />hosts = localhost<br />dbname = groupoffice<br />query = SELECT maildir FROM pa_mailboxes WHERE username='%s' AND active = '1'<br />#expansion_limit = 100


Install dovecot and then configure it to do imap, pop3 and LDA (Local Delivery Agent).

Copy /usr/share/doc/packages/dovecot/ to /etc/dovecot

Copy /usr/share/doc/packages/dovecot/dovecot-openssl.cnf to /etc/dovecot

Edit dovecot-openssl.cnf and then run to generate SSL certificates needed for remote pop3 and imap authentication.


Edit /etc/dovecot.conf

I've left out all the commentary

base_dir = /var/run/dovecot/<br />protocols = imap imaps pop3 pop3s<br />disable_plaintext_auth = no<br />log_path = /var/log/dovecot.log<br />info_log_path = /var/log/<br />log_timestamp = "%b %d %H:%M:%S "<br /><br />ssl_listen =<br />ssl_disable = no<br />ssl_cert_file = /etc/pki/dovecot/certs/dovecot.pem<br />ssl_key_file = /etc/pki/dovecot/private/dovecot.pem<br />ssl_parameters_regenerate = 168<br />ssl_cipher_list = ALL:!LOW<br />verbose_ssl = yes<br /><br />login_dir = /var/run/dovecot/login<br />login_chroot = yes<br />login_greeting = Dovecot ready.<br />login_log_format_elements = user=<%u> method=%m rip=%r lip=%l %c<br />login_log_format = %$: %s<br /><br />mail_location = maildir:/data/mail/%d/%n<br />mail_log_prefix = "%Us(%u): "<br />mail_log_max_lines_per_sec = 10<br />verbose_proctitle = yes<br /><br />first_valid_uid = 1000<br />last_valid_uid = 1000<br />mailbox_idle_check_interval = 30<br />maildir_copy_with_hardlinks = yes<br /><br />protocol imap {<br />  login_executable = /usr/libexec/dovecot/imap-login<br />  mail_executable = /usr/libexec/dovecot/imap<br />  imap_max_line_length = 65536<br />  mail_plugin_dir = /usr/lib/dovecot/imap<br />  login_greeting_capability = no<br />}<br /><br />protocol pop3 {<br />  login_executable = /usr/libexec/dovecot/pop3-login<br />  mail_executable = /usr/libexec/dovecot/pop3<br />  mail_plugin_dir = /usr/lib/dovecot/pop3<br />}<br /><br />protocol lda {<br />  postmaster_address = <a class="mailto" href=""></a><span class="mailto"></span><br />  hostname =<br />  sendmail_path = /usr/lib/sendmail<br />  auth_socket_path = /var/run/dovecot/auth-master<br />}<br /><br />auth_executable = /usr/libexec/dovecot/dovecot-auth<br />auth_verbose = yes<br /><br />auth default {<br />  mechanisms = plain login<br />  passdb sql {<br />    args = /etc/dovecot-sql.conf<br />  }<br />  userdb sql {<br />    args = /etc/dovecot-sql.conf<br />  }<br />  user = vmail<br />}<br /><br />dict {<br />}<br /><br />plugin {<br />}<br />


Next step, edit dovecot-sql.conf so that dovecot knows how to connect to MySQL and what to query.

driver = mysql<br />connect = host=localhost dbname=groupoffice user=vmail_user password=some_password<br />default_pass_scheme = PLAIN-MD5<br />password_query = SELECT password FROM pa_mailboxes WHERE username=concat('%n', '@', '%d') AND active='1'<br />user_query = SELECT 'maildir:/data/mail/%d/%n' AS mail, 1000 AS uid, 1000 AS gid FROM pa_mailboxes WHERE username='%u' AND active='1'


Copy the below script into /usr/bin and alias to "maildirmake".

#!/bin/sh<br />#<br /># maildirmake.dovecot -- create maildirs<br /># Copyright (c) 2003, Jaldhar H. Vyas<br /># "Do what thou wilt" shall be the whole of the license.<br />#<br />dir=$1<br />if [ -z "$dir" ]; then<br />  echo "Must supply a directory path"<br />  exit 1<br />fi<br /><br />if [ "$dir" = "-h" ]; then<br />  echo "usage: $0 directory"<br />  exit 0<br />fi<br /><br />umask 077<br />mkdir -p $dir/{cur,new,tmp} || echo "$!" && exit 1<br />chmod u+rwxg-a- $dir $dir/{cur,new,tmp} || echo "$!" && exit 1<br /><br />exit 0


As we already have a working authentication mechanism, why waste
time trying to configure another. Just set saslauthd to use your IMAP


# Directory in which to place saslauthd's listening socket, pid file, and so<br /># on.  This directory must already exist.<br />SOCKETDIR=/var/run/saslauthd<br /><br /># Mechanism to use when checking passwords.  Run "saslauthd -v" to get a list<br /># of which mechanism your installation was compiled with the ablity to use.<br />MECH="rimap -r -O localhost"<br /><br /># Additional flags to pass to saslauthd on the command line.  See saslauthd(8)<br /># for the list of accepted flags.<br />FLAGS=

Should you run into trouble, SASLAUTH can be disabled by hashing out the below lines.

smtpd_sasl_local_domain         = $mydomain,<br />smtpd_sasl_auth_enable          = yes<br />smtpd_sasl2_auth_enable         = yes<br />broken_sasl_auth_clients        = yes<br />smtpd_sasl_authenticated_header = yes


Before installing amavisd-new, it is recommended that you read the documentation which can be found here.

Make sure you read through these two specifically as they relate to our setup.

Install amavisd-new and create the needed MySQL tables.

Here is the MySQL table structure taken from the documentation. You can
simply copy and paste these lines into phpMyAdmin or the SQL command
prompt. Before you do that however, you'll need to create a new
database and set up permissions for an amavis user. (We performed this
process above for GroupOffice in the MySQL section. You need to repeat
this for a new database and new user / password)

I have also included my entire amavisd-new config file and highlighted the most important parts.

Tagging and stuff you don't need to worry too much about because this is set per policy saved in MySQL.

I'd also reccomend inserting the example data from the documantation. I can't stress this enough, when it comes to amavis RTFM!

DKIM mail signing and verification with amavisd-new

First, generate a public/private keypair.

$ amavisd genrsa /var/db/dkim/

add to amavisd.conf

$enable_dkim_verification = 1;<br />$enable_dkim_signing = 1;<br />dkim_key('', 'mail', '/var/db/dkim/');<br />@dkim_signature_options_bysender_maps = (<br />    { '.' => { ttl => 21*24*3600, c => 'relaxed/simple' } } );<br /><div class="important">@mynetworks = qw(<br />         ;  # list your internal networks</div>

I'm not sure you need the bit highlighted in red. When I researched
setting this up the docs I found said to include it, but I've since
removed it because is not part of my network, it's the whole
internet, and @mynetworks is already defined in the config.

now run:

$ amavisd showkeys<br />; key#1, domain, /var/db/dkim/<br /> 3600 TXT (<br />  "v=DKIM1; p="<br />  "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkaQacPtiHIi1Vh9TkiyJRSs8L"<br />  "m6ctFnlcFMQSYcai09/m0ifxatDd3uX8Y5lqM7ReAasFz5GqoqIhqjqIyOLozgKo"<br />  "QwhqQu0jstgDs+prA2P2pJdCWtNd7KDCKPkNnBm8j5Ei+q3vm26eU+n3GhZeUp0l"<br />  "B+kYD5G2bEpgtn7y4wIDAQAB")

add the public key to your DNS zone (in a bind zone file this needs to
be all on one line, remove any "quote" marks and the brackets, but not
the space on the first line within the quote marks as this is important)

The final thing should look like this (for bind)   IN   TXT   "v=DKIM1; p=MIGfMA0GCSqG.............tn7y4wIDAQAB"

Increment SOA sequence number and reload DNS; then test signing and a published key:

$ amavisd testkeys<br />TESTING#1:    => pass

if all went well:

$ amavisd reload


Install spamassassin and Perl modules that relate to email, mime type and content filtering.

Install razor and pyzor agents.

There's not much to do here, I'm just using the defaults and they
work pretty well. Should you wish to tweak something, make sure you
can't do it in amavis first. Tweak spamassassin by editing the config
files in /etc/mail/spamassassin/


Again, same as above there's not much to do, install and make sure
freshclam is updating the engine and pattern files. Make sure amavis is
pointing to the correct path for the socket file. (See highlighted line

@av_scanners = (<br /><br />### <a class="ext" href="" title=""></a><span class="ext"></span><br />['ClamAV-clamd',<br /><div class="important">  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.socket"],</div>  qr/\bOK$/m, qr/\bFOUND$/m,<br />  qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],

Post Installation Tasks

Add our mail components to the vmail group

Once you know everything is working, remember to turn of debug logging where ever you might have enabled it.

Remove " -v" from the end of the smtp line in Postfix file.

SE Linux

Remember how we disabled, or rather, set SELinux to permissive right
at the beginning, well now that everything is working, it's time to set
SELinux back to enforce and fix everything this will break. From
/var/log/maillog and /var/log/audit/audit.log I know dovecot was not
able to access my mail dir /data/mail

[root@CentOS-1 ~]# grep dovecot_t /var/log/audit/audit.log | audit2allow -m dovecot > dovecot.te<br />[root@CentOS-1 ~]# grep dovecot_t /var/log/audit/audit.log | audit2allow -m dovecot<br /><br />module dovecot 1.0;<br /><br />require {<br />        type dovecot_t;<br />        type public_content_rw_t;<br />        class file { rename setattr read lock create write getattr link unlink };<br />        class dir { write search read remove_name getattr add_name };<br />}<br /><br />#============= dovecot_t ==============<br />allow dovecot_t public_content_rw_t:dir { write search read remove_name getattr add_name };<br />allow dovecot_t public_content_rw_t:file { rename setattr read lock create write getattr link unlink };

I also know that clamAV is having trouble creating tmp files for scanning.

[root@CentOS-1 ~]# grep clamscan_t /var/log/audit/audit.log | audit2allow -m clamscan > clamscan.te<br />[root@CentOS-1 ~]# grep clamscan_t /var/log/audit/audit.log | audit2allow -m clamscan<br /><br />module clamscan 1.0;<br /><br />require {<br />        type amavis_var_lib_t;<br />        type clamscan_t;<br />        type proc_t;<br />        class dir { search setattr read create write getattr rmdir remove_name add_name };<br />        class file { write getattr read create unlink };<br />}<br /><br />#============= clamscan_t ==============<br />allow clamscan_t amavis_var_lib_t:dir { search setattr read create write getattr rmdir remove_name add_name };<br />allow clamscan_t amavis_var_lib_t:file { write read create unlink getattr };<br />allow clamscan_t proc_t:file { read getattr };<br />

What I've just done is shown you what our new SELinux policy modules
will look like. You will need to run the same commands as your output
will likely differ from mine (unless you follow this howto word for

Now we need to create and install these modules:

[root@CentOS-1  ~]# grep dovecot_t /var/log/audit/audit.log | audit2allow -M dovecot<br />******************** IMPORTANT ***********************<br />To make this policy package active, execute:<br /><br />semodule -i dovecot.pp<br /><br />[root@CentOS-1 ~]# <br />[root@CentOS-1 ~]# semodule -i dovecot.pp
[root@CentOS-1 ~]# grep clamscan_t /var/log/audit/audit.log | audit2allow -M clamscan<br />******************** IMPORTANT ***********************<br />To make this policy package active, execute:<br /><br />semodule -i clamscan.pp<br /><br />[root@CentOS-1 ~]# <br />[root@CentOS-1 ~]# semodule -i clamscan.pp

Now set SELinux back to enforcing "setenforce 1" and try make sure
everything still works. If it doesn't, find out what's being blocked
from maillog and other daemon logs, including audit log and allow that
process access to the filesystem like we've just done.

More information on SELinux is available on the CentOS Wiki

Mail Relaying

** This section applied to the old howto and does not relate
to the setup which uses GroupOffice. I'd recommend you ignore this
completely unless you know what you are doing. I've left it in the
guide for reference purposes only. **

In the above setup, there is no way to relay mail for other domains
you might be accepting email for. If you would like to relay mail for
other domains you need to firstly modify the MySQL tables created by
postfixadmin. You'll then also need to modify the postfix config.

Also, please note that you will not be able to add relay
destinations via postfixadmin. I use phpmyadmin when needed. The
destination entry will be the same as it would if you used the
transport file. E.g. "smtp:[somewhere.some-domain.tld]"

Modify the SQL database by adding a new field in which to save the transport information.

ALTER TABLE `domain` ADD `destination` VARCHAR( 150 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL AFTER `transport` ;

Add this to Postfix

transport_maps  = mysql:/etc/postfix/<br />relay_domains   = $transport_maps

And then create the transport maps cf file.


#<br />user = vmail_user<br />password = choose_a_password<br />hosts = localhost<br />dbname = vmail_db<br />query = SELECT destination FROM domain WHERE domain='%d' AND transport='relay' AND active='1'


Telnet testing. When you telnet on port 25 you should be greeted by 220 response code followed by the server name and MTA name.

[root@CentOS-1 ~]# telnet 25<br />Trying<br />Connected to (<br />Escape character is '^]'.<br />220 ESMTP Farm Yard Mail version 111.111<br />ehlo localhost<br /><br />250-PIPELINING<br />250-SIZE 20971520<br />250-ETRN<br />250-STARTTLS<br />250-AUTH PLAIN LOGIN<br />250-AUTH=PLAIN LOGIN<br />250-ENHANCEDSTATUSCODES<br />250-8BITMIME<br />250 DSN<br />^]<br />telnet> q<br />Connection closed.

Repeat the steps above against amavis using port 10024

Of course, the other way to test is to use another email account to send email.


This is always the most difficult part.

Off the top of my head, I very often got permission denied errors,
normally app_A trying to connect to app_B. Make sure your permissions
are set correctly on the various sockets. Make sure your maildirs are
not world readable and owned by vmail. Try setting SELinux to
permissive with "setenforce 0"