Zimbra Collaboration Server

Last modified by Sebastian Marsching on 2022/05/29 13:59

Using MailStore Archiving with Zimbra

This is a short description of how I configure Zimbra to work with the MailStore archiving software.

Changes in configuration file /opt/zimbra/conf/amavisd.conf.in:

--- amavisd.conf.in.zimbra-dist 2011-06-03 20:22:33.856452812 +0200
+++ amavisd.conf.in     2011-06-03 20:22:45.686544874 +0200
@@ -156,6 +156,10 @@
# $forward_method = 'smtp:[127.0.0.1]:10025';  # set to undef with milter!
 %%uncomment SERVICE:archiving%%$archive_quarantine_method = 'smtp:[127.0.0.1]:10025';

+# Enable archiving to fixed e-mail address
+$archive_quarantine_method = 'smtp:[127.0.0.1]:10025';
+@archive_quarantine_to_maps = ('my-archiving-address@example.com');
+
%%uncomment VAR:zimbraAmavisQuarantineAccount%%$final_virus_destiny      = D_DISCARD;
 $final_banned_destiny     = D_BOUNCE;
 $final_spam_destiny       = D_DISCARD;

This should send every e-mail passed through Zimbra to the address my-archiving-address@example.com. This is the POP3 or IMAP mailbox that MailStore uses to receive all e-mails. Amavis will take care of writing headers that help MailStore identify to which e-mail address the e-mail actually belongs, so that it can be archived in the right MailStore account.

I have been using this configuration with ZCS 7.1, 7.2 and 8.6. For other versions, the configuration file and thus the changes might look different.

Make Zimbra only accept mail for existing accounts

By default, Zimbra accepts e-mails for addresses which are not valid and later sends a bounce mail. Unfortunately this can cause SPAM back-scatter and thus should be avoided (also see my blog article).

In Zimbra 8.6 it is very simple to change this behavior:

zmprov mcf +zimbraMtaRestriction reject_unverified_recipient

Effect on Hosts Listed in zimbraMtaMyNetworks

The change described above will have no effect on hosts listed in zimbraMtaMyNetworks. Therefore, if you have mail-server acting as a front-end to the Zimbra server, you should not list it in zimbraMtaMyNetworks. zimbraMtaMyNetworks should only contain the Zimbra server itself (and the loopback address).

However, for OpenDKIM and SpamAssassin, you also want to trust your other mail servers. You can get this effect by editing the configuration files and adding the appropriate IP addresses there:

/opt/zimbra/conf/opendkim-localnets.conf.in (Zimbra 8 only):

%%zimbraMtaMyNetworksPerLine%%
192.0.2.1/32
[2001:db8::1]/128

/opt/zimbra/conf/salocal.cf.in:

%%uncomment VAR:zimbraMtaMyNetworks%%trusted_networks %%zimbraMtaMyNetworks%% 192.0.2.1/32 [2001:db8::1]/128

In recent Zimbra versions (ZCS 8.5 and newer) it might be more elegant to add your own trusted_networks line to /opt/zimbra/data/spamassassin/localrules/sauser.cf.

Important note: Earlier versions of this tutorial suggested also adding the hosts to the @mynetworks list in amavisd.conf.in. However, this is not a good idea because it will cause SpamAssassin to treat all e-mail as "submitted" by an authenticated client and thus it will also treat all hosts as trusted, even if it would not otherwise (as explained on the SpamAssassin mailing list). For this reason, zimbraMtaMyNetworks should only contain the Zimbra server itself and the @mynetworks option in the Amavis configuration should not be changed.

Mailbox Selected READ-ONLY Error in Thunderbird

Sometimes Thunderbird report the error "Mailbox selected READ-ONLY" when trying to move an e-mail from the inbox. The e-mail is copied to the target folder but not removed from the inbox. Besides the read flag will not be set correctly.

This problem seems to be related to the Ehcache feature in Zimbra. For me disable Ehcache solved the problem in Zimbra 7 and 8:

zmlocalconfig -e imap_use_ehcache=false

IPv6 and Zimbra 8

There are two potential problems when using Zimbra 8 on a IPv6-enabled host: The first problem concerns OpenLDAP and the second one concerns OpenDKIM.

Fixing the OpenLDAP Problem

Fixing the OpenLDAP problem is easy: Set the local configuration option ldap_bind_url to ldap:///:

zmlocalconfig -e ldap_bind_url=ldap:///

After this change you have to restart Zimbra. For details refer to the article in my blog.

This change should survive minor Upgrades of Zimbra (e.g. from Zimbra 8.0.3 to Zimbra 8.0.5).

Fixing the OpenDKIM Problem

Fixing the OpenDKIM problem is a bit more tricky: You have to edit the file /opt/zimbra/conf/opendkim.conf.in and change the line

Socket                %%zimbraInetMode%%:8465@[%%zimbraLocalBindAddress%%]

to

Socket                inet:8465@[127.0.0.1]

After this change you have to restart Zimbra. For details refer to the article in my blog.

Unfortunately, this change is overwritten when updating Zimbra, therefore it has to be applied again after each update.

Fixing the zmsaupdate cron-job

Some of versions of Zimbra (I noticed it in Zimbra 8.0.6) have a bug that causes the cron job for zmsaupdate to fail if /bin/sh is not pointing to a Bource Again Shell (Bash) (e.g. Ubuntu 12.04 LTS). The error message (send to the local zimbra user) looks like that:

/bin/sh: 28: /opt/zimbra/.bashrc: [[: not found
/bin/sh: 36: [: Linux: unexpected operator

The fix for this is simple: The command has to be wrapped in a call to Bash. This can be done by editing Zimbra's crontab (crontab -u zimbra -e) and changing the line

45 0 * * * . /opt/zimbra/.bashrc; /opt/zimbra/libexec/zmsaupdate

to

45 0 * * * /bin/bash -c ". /opt/zimbra/.bashrc; /opt/zimbra/libexec/zmsaupdate"

SSL / TLS options

Disabling SSLv3

The article in the Zimbra Wiki extensively describes how to disable SSLv3. Since Zimbra 8.6, the supported TLS protocol versions can be controlled through the zimbraMailboxdSSLProtocols and zimbraReverseProxySSLProtocols options.

For adjusting the list of supported ciphers you might want to refer to the following sites:

TLS Ciphers

Inspired by the BetterCrypto guide, I use the following settings (communication with other mail-servers always goes through another MTA, therefore I can be quite strict):

zmprov mcf zimbraMtaSmtpdTlsCiphers high
zmprov mcf zimbraMtaSmtpdTlsMandatoryCiphers high
zmprov mcf zimbraMtaSmtpdTlsExcludeCiphers aNULL,eNULL,LOW,3DES,MD5,EXP,PSK,DSS,RC4,SEED,ECDSA,DES
zmprov mcf zimbraMtaSmtpdTlsProtocols \!SSLv2,\!SSLv3
zmprov mcf zimbraReverseProxySSLCiphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
zmprov mcf +zimbraReverseProxySSLProtocols TLSv1.3
zmprov mcf -zimbraReverseProxySSLProtocols TLSv1
zmprov mcf -zimbraReverseProxySSLProtocols TLSv1.1
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_DHE_DSS_WITH_DES_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_DHE_RSA_WITH_DES_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_RSA_EXPORT_WITH_RC4_40_MD5
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_RSA_WITH_DES_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_RSA_WITH_RC4_128_MD5
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_RSA_WITH_3DES_EDE_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites SSL_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites TLS_DHE_RSA_WITH_AES_128_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
zmprov mcf +zimbraSSLExcludeCipherSuites TLS_DHE_RSA_WITH_AES_256_CBC_SHA
zmprov mcf +zimbraSSLExcludeCipherSuites TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

A word regarding the ciphers and protocols that we configure for the reverse proxy: We disable TLS 1.0 and 1.1 because they are not considered secure any longer and most browsers have dropped support for them anyway. We enable all TLSv1.3 ciphers because they seem to be enabled anyway, even if we do not list them. We prefer 256 bit ciphers over 128 bit ciphers. A minimal modern list supporting both TLSv1.2 and TLSv1.3 would look like this:

TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305

We include the CHACHA20 ciphers because some systems that do support them do not support AES256, and they should be better than AES128.

In addition to that list, we add the AES128-GCM ciphers because Android 5.0 and 6.0 apparently neither support AES256 nor CHACHA20. We add the CBC ciphers to the end of the list, because IE 11 on Windows 8.1 (and Windows Server 2012 R2) does not support GCM yet. The AES256-CBC variant would be sufficient for that, but also supporting AES128-CBC also gives us support for IE11 on Windows Phone 8.1, and it is probably not significantly more insecure than AES256-CBC.

It probably is a good idea to completely remove the AES-CBC ciphers when Windows 8.1 / Windows Server 2012 R2 drop out of support in 2023.

In addition to these changes, it makes sense to enable TLSv1.3 and disable TLSv1.0 and TLSv1.1 for mailboxd. This happens through zmlocalconfig. Run zmlocalconfig mailboxd_java_options to display the current options and edit the https.protocols and jdk.tls.client.protocols options. Then set the resulting string through zmlocalconfig. For example:

zmlocalconfig -e mailboxd_java_options="-server -Dhttps.protocols=TLSv1.2,TLSv1.3 -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3 -Djava.awt.headless=true -Dsun.net.inetaddr.ttl= -Dorg.apache.jasper.compiler.disablejsr199=true -XX:+UseG1GC -XX:SoftRefLRUPolicyMSPerMB=1 -XX:-OmitStackTraceInFastThrow -verbose:gc -Xlog:gc*=debug,safepoint=info:file=/opt/zimbra/log/gc.log:time:filecount=20,filesize=10m"

Strict-Transport-Security Header

It makes sense to add the Strict-Transport-Security header so that the web interface is never used through an unencrypted connection. I got the idea here). I use these settings with Zimbra 8.6.

Typically, the add_header option needs to be added to two files:

  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.https.default.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.https.template

The option is added in the following form and add it right after the ssl_verify_depth option:

    add_header Strict-Transport-Security max-age=15768000;

Since Zimbra 8.7 there is a simpler option for adding this header:

zmprov mcf +zimbraResponseHeader "Strict-Transport-Security: max-age=15768000"

If you enable the Nginx proxy for the first time, do not forget to (re-)enable the redirect mode (unless you are using the pure https mode):

zmprov ms <server> zimbraReverseProxyMailMode redirect

You also might have to configure the protocol and hostname used for generating URLs (otherwise, some generated URLs might use the http scheme):

zmprov md <domain> zimbraPublicServiceHostname <server>
zmprov md <domain> zimbraPublicServiceProtocol https

Adjusting the DH key size

By default, Nginx only uses 1024 bit keys when using the TLS_DH_* ciphers (Diffie-Hellman key exchange). This is not considered enough any longer. In order to increase this size, the Diffie-Hellman parameters need to be configured explicitly. I am using the following settings with Zimbra 8.6.

First, the DH parameters need to be generated with OpenSSL:

openssl dhparam -outform PEM -out /opt/zimbra/conf/dhparam2048.pem 2048

Next, the ssl_dhparam option has to be added to the relevant sections of the Nginx configuration. I added it to the following configuration files, right before the ssl_ecdh_curve option:

  • /opt/zimbra/conf/nginx/templates/nginx.conf.mail.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.admin.default.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.admin.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.https.default.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.https.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.sso.default.template
  • /opt/zimbra/conf/nginx/templates/nginx.conf.web.sso.template

The configuration line should look like this:

    ssl_dhparam /opt/zimbra/conf/dhparam2048.pem;

Starting with Zimbra 8.7, Zimbra uses 2048 bit DH params by default and this manual intervention is not necessary any longer.

Fixing the logrotate script

The logrotate script (/etc/logrotate.d/zimbra) for the Nginx log files has a small problem (Bug 106800): It might happen, that the compression of the logfile starts before Nginx has stopped writing to the logfile. This might result in an e-mail with the following content being sent to the administrator:

/etc/cron.daily/logrotate:
gzip: stdin: file size changed while zipping

At the moment, I am testing the following workaround for this issue: I changed the logrotate script to wait for five seconds after sending SIGUSR1 to Nginx. This should allow Nginx to reopen the file:

    postrotate
      kill -USR1 `cat /opt/zimbra/log/nginx.pid 2> /dev/null` 2> /dev/null && sleep 5 || true
    endscript

If this does not help, the delaycompress option should fix it for sure.

Enabling SpamAssassin rule updates

Since Zimbra 8, it might be necessary to explicitly enable SpamAssassin rule updates (see this article in the Zimbra knowledge-base). You can do this through zmlocalconfig:

zmlocalconfig -e antispam_enable_rule_updates=true
zmlocalconfig -e antispam_enable_restarts=true
zmlocalconfig -e antispam_enable_rule_compilation=true

The last line is only necessary if you want to compile rules (this should improve the scan performance).

Adding Pyzor and Razor to SpamAssassin

This has been heavily inspired by https://wiki.zimbra.com/wiki/Improving_Anti-spam_system and https://wiki.zimbra.com/wiki/Anti-spam_Strategies, but it use a different path for the configuration files so that they do not get lost during a Zimbra upgrade.

Pyzor

Install the package (as root):

aptitude install pyzor

Create a symbol link in the Zimbra directory (as root):

ln -s data/pyzor /opt/zimbra/.pyzor

Run all remaining actions as the zimbra user (su - zimbra).

Create the configuration directory:

mkdir /opt/zimbra/data/pyzor

Create a symbol link in the amavisd directory:

ln -s ../pyzor /opt/zimbra/data/amavisd/.pyzor

Optionally, increase the timeout for Pyzor by adding the pyzor_timeout option to the SpamAssassin configuration. For example:

pyzor_timeout 20

In recent Zimbra versions, you can add this line to /opt/zimbra/data/spamassassin/localrules/sauser.cf.

Razor

Install the package (as root):

aptitude install razor

Create a symbol link in the Zimbra directory (as root):

ln -s data/razor /opt/zimbra/.razor

Run all remaining actions as the zimbra user (su - zimbra).

Create the configuration directory:

mkdir /opt/zimbra/data/razor

Create a symbol link in the amavisd directory:

ln -s ../razor /opt/zimbra/data/amavisd/.razor

Initialize the configuration:

razor-admin -create
razor-admin -discover
razor-admin -register

Using the ClamAV Unofficial Signatures

Adding more signatures to ClamAV can help in improving the detection rate for virus scans. The ClamAV Unofficial Signatures Updater is available from GitHub. In order to install the script, one should download the newest release in the .tar.gz format from the releases page (version 5.4.1 in this example) and unpack it in /opt/zimbra. One should then create a symbol-link from clamav-unofficial-sigs to the directory created when unpacking the archive and make the script executable, for example:

ln -s clamav-unofficial-sigs-5.4.1 /opt/zimbra/clamav-unofficial-sigs
chmod a+x /opt/zimbra/clamav-unofficial-sigs/clamav-unofficial-sigs.sh

All remaining steps should be done as the zimbra user.

Create directories for the script’s configuration and data:

mkdir /opt/zimbra/conf/clamav-unofficial-sigs
mkdir /opt/zimbra/data/clamav-unofficial-sigs

Copy the master.conf and example user.conf from the distribution to the configuration directory:

cp /opt/zimbra/clamav-unofficial-sigs/config/master.conf /opt/zimbra/conf/clamav-unofficial-sigs
cp /opt/zimbra/clamav-unofficial-sigs/config/user.conf /opt/zimbra/conf/clamav-unofficial-sigs

Create a /opt/zimbra/conf/clamav-unofficial-sigs/os.conf file that contains OS-specific paths, etc. As these paths differ from the ones typically used for the operating system (Zimbra brings its own version of ClamAV), one cannot use one of the example files. The following file works with Zimbra 8.7 on Ubuntu 14.04 LTS and with the ClamAV Unofficial Signatures Updater 5.4.1, but changes might be needed when using a different version of Zimbra, a different operating system, or a differet version of the updater script.

# This file contains os configuration settings for clamav-unofficial-sigs.sh
###################
# This is property of eXtremeSHOK.com
# You are free to use, modify and distribute, however you may not remove this notice.
# Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com
##################
#
# Script updates can be found at: https://github.com/extremeshok/clamav-unofficial-sigs
#
# Originially based on:
# Script provide by Bill Landry (unofficialsigs@gmail.com).
#
# License: BSD (Berkeley Software Distribution)
#
##################
#
# NOT COMPATIBLE WITH VERSION 3.XX / 4.XX CONFIG
#
################################################################################
# SEE MASTER.CONF FOR CONFIG EXPLAINATIONS
################################################################################
# Rename to os.conf to enable this file
################################################################################

# Zimbra

clam_user="zimbra"
clam_group="zimbra"

clam_dbs="/opt/zimbra/data/clamav/db"

clamd_pid="/opt/zimbra/log/clamd.pid"

clamd_restart_opt="/opt/zimbra/bin/zmclamdctl restart"

clamd_reload_opt="/opt/zimbra/common/bin/clamdscan --config-file=/opt/zimbra/conf/clamd.conf --reload"

clamscan_bin="/opt/zimbra/common/bin/clamscan"

#clamd_socket="/var/run/clamav/clamd.ctl"

work_dir="/opt/zimbra/data/clamav-unofficial-sigs"
log_file_path="/opt/zimbra/log"

# https://eXtremeSHOK.com ######################################################

One also has to make some modifications to the user.conf file. As an absolute minimum, the line user_configuration_complete="yes" has to be added or the updater script will not work. I also chose to set the following options:

securiteinfo_enabled="no"    # SecuriteInfo
malwarepatrol_enabled="no"   # Malware Patrol
downloader_ignore_ssl="no"   # do not ignore SSL (certifcate) errors when
                             # downloading files

Finally, one has to add the following line to the crontab file of the zimbra user (crontab -u zimbra -e):

# Update unofficial ClamAV signatures
45 * * * * /bin/bash /opt/zimbra/clamav-unofficial-sigs/clamav-unofficial-sigs.sh -c /opt/zimbra/conf/clamav-unofficial-sigs >/dev/null

it is a good idea to once run this script manually in order to see whether everything is working (/bin/bash /opt/zimbra/clamav-unofficial-sigs/clamav-unofficial-sigs.sh -c /opt/zimbra/conf/clamav-unofficial-sigs).

After running the script, one can check whether ClamAV is actually using the signatures by running clamscan --debug -d /opt/zimbra/data/clamav/db /dev/null 2>&1|grep loaded. In addition to the regular ClamAV signatures, one should see the signatures added by the updater script (the exact list depends on the configuration).

Use Zimbra for system mail

The paths are for Zimbra 8.7:

update-alternatives \
 --install /usr/sbin/sendmail mta /opt/zimbra/common/sbin/sendmail 25 \
 --slave /usr/bin/mailq mta-mailq /opt/zimbra/common/sbin/mailq \
 --slave /usr/bin/newaliases mta-newaliases /opt/zimbra/common/sbin/newaliases \
 --slave /usr/share/man/man1/mailq.1.gz mta-mailqman /opt/zimbra/common/share/man/man1/mailq.1 \
 --slave /usr/share/man/man1/newaliases.1.gz mta-newaliasesman /opt/zimbra/common/share/man/man1/newaliases.1 \
 --slave /usr/share/man/man8/sendmail.8.gz mta-sendmailman /opt/zimbra/common/share/man/man1/sendmail.1 \
 --slave /usr/share/man/man5/aliases.5.gz mta-aliasesman /opt/zimbra/common/share/man/man5/aliases.5

Synchronizing contacts with the macOS Contacts app (CardDAV)

Contacts that are stored in Zimbra can be synchronized with the macOS Contacts app through the CardDAV protocol. However, there seems to be one severe limitation at least up to macOS 10.12.5 (Sierra). Only one address book can be synchronized. If there is more than one address book, the “first” one is selected. In this case, “first” seems to be decided based on the lexical order. In my case, I had an extra address book starting with the letter “A” and this one would be chosen instead of the standard “Contacts” address book. I could fix this by renaming the extra address book.

Remove submitting clients IP address from headers

By default, the IP address of the client that submitted an e-mail is visible through the Received header added by Postfix. This might not be desirable for privacy reasons. I got the basic idea from here, but I had to adapt it to make it work with Zimbra 8.8.

We add the following lines to /opt/zimbra/conf/postfix_header_checks.in:

/^Received:.*with ESMTPSA/
        IGNORE
/^X-Originating-IP:/
        IGNORE

Please note that there is a tab (not spaces) in front of IGNORE.

Unfortunately, Zimbra does not use the header checks file by default and manually setting the header_checks option in Postfix's main.cf does not work either, because it is overwritten when Postfix is started. There are two ways to fix this. One can either set the zimbraMtaBlockedExtensionWarnRecipient configuration option to FALSE like suggested here:

zmprov mcf zimbraMtaBlockedExtensionWarnRecipient FALSE

The other solution is changing the configuration of zmconfigd by removing the following lines from /opt/zimbra/conf/zmconfigd.cf:

        if VAR zimbraMtaBlockedExtensionWarnRecipient
                POSTCONF header_checks
        fi

Most likely, you also want to set the zimbraSmtpSendAddOriginatingIP to FALSE:

zmprov mcf zimbraSmtpSendAddOriginatingIP FALSE