Skip to main content
Version: 1.3

Milters configuration

info

This page is for a multi-server installation only. Install the Web admin server before you continue. Java 21 or later must be installed on this host too, see Java.

Every Zimbra MTA server runs two of the three services:

ComponentService unitDefault portPurpose
msh-zr-nodemsh-zr-node.service7082Receives the rules and settings from the web admin for the milter on this host
msh-zr-miltermsh-zr-milter.service7080Milter engine (processes mail for the Zimbra MTA)

The web admin is not started here. It connects to this host on port 7082 and sends the rules, the settings and the license.

Repeat this page for each MTA server.

Commands​

Here are the commands needed to install and start the node and milter services. They can be invoked one by one in the terminal. If you would like to have a description of each command, please refer to the Description section below.

sudo useradd --system --no-create-home --shell /usr/sbin/nologin --home /opt/msh-zr msh
sudo tar zxvf msh-zr-<VERSION>.tgz -C /opt
sudo sh /opt/msh-zr/lib/fixperms.sh
sudo systemctl link /opt/msh-zr/lib/msh-zr-node.service /opt/msh-zr/lib/msh-zr-milter.service
sudo systemctl daemon-reload
sudo systemctl enable msh-zr-node.service msh-zr-milter.service
sudo systemctl start msh-zr-node.service msh-zr-milter.service

Description​

Add the msh system user.

  • --system - creates a system account (no password, no interactive login)
  • --no-create-home - won't auto-create /home/msh, since /opt/msh-zr is its home
  • --shell /usr/sbin/nologin - prevents login
  • --home /opt/msh-zr - sets /opt/msh-zr as its "home"
  • msh - username
sudo useradd --system --no-create-home --shell /usr/sbin/nologin --home /opt/msh-zr msh

Extract the msh-zr-<VERSION>.tgz package to the /opt system directory. This creates /opt/msh-zr. Use the same package version as on the web admin server.

sudo tar zxvf msh-zr-<VERSION>.tgz -C /opt

Run the fixperms.sh script, which secures the application installation by:

  1. Ensuring proper ownership (msh user/group)
  2. Restricting directory access (750)
  3. Restricting file access (640, making files non-executable by default)
  4. Explicitly making only the necessary scripts executable (750)
  5. Making the node's token and certificate files readable by the msh user only (600)
  6. Making the Zimbra extension and zimlets in zimbra/ readable by everyone (755 and 644); for that, /opt/msh-zr itself lets others pass through without listing it (751)
sudo sh /opt/msh-zr/lib/fixperms.sh

Create symbolic links for the node and milter unit files, allowing the system to recognize and manage the services. The web admin is not linked: it runs on the web admin server.

sudo systemctl link /opt/msh-zr/lib/msh-zr-node.service /opt/msh-zr/lib/msh-zr-milter.service

Reload systemd to apply changes from the newly linked service files.

sudo systemctl daemon-reload

Enable the services to start automatically on system boot.

sudo systemctl enable msh-zr-node.service msh-zr-milter.service

Start the services. The order does not matter.

sudo systemctl start msh-zr-node.service msh-zr-milter.service

On its first start the node creates its access token (etc/node.token) and its HTTPS certificate (etc/node.p12, with its password in etc/node.p12.password). You need the token to add the server in the web admin, see Copy the node token.

Verify​

systemctl status msh-zr-node.service msh-zr-milter.service

Both must show Active: active (running). Live logs:

journalctl -u msh-zr-node.service -u msh-zr-milter.service -f

Open the node port to the web admin​

The web admin connects to this host on TCP 7082. Allow it from the web admin server only, for example with ufw:

sudo ufw allow from <web-admin-server-ip> to any port 7082 proto tcp

Do not open port 7080: only the Zimbra MTA on this host talks to the milter. See Ports and firewall.

Copy the node token​

On its first start the node created an access token. The web admin needs it to connect. Print it:

sudo cat /opt/msh-zr/etc/node.token
note

Treat the token like a password: anyone who has it and can reach port 7082 can send settings to this server.

Add this server with the token on the Servers page of the web admin, see Add a server.

Register the milter in the Zimbra MTA​

Every MTA server sends mail to the milter on its own host (localhost:7080), so the settings are made per server with zmprov ms and the commands below are run on each MTA server, after the milter on that server is running. $(zmhostname) resolves to the host you run them on.

Do not set the same attributes globally with zmprov mcf: an MTA server whose milter is not installed yet would then hand mail to a milter that is not there.

Run the following on this MTA server. If you would like to have a description of each command, please refer to the description below.

sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMilterServerEnabled TRUE'
sudo su - zimbra -c 'zmmilterctl start'
sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaSmtpdMilters "inet:localhost:7080"'
sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaNonSmtpdMilters "inet:localhost:7080"'
sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaMilterDefaultAction accept'
sudo su - zimbra -c 'zmmtactl reload'

Description​

All zmprov and zmmtactl commands must run as the zimbra user, so each command switches to it with sudo su - zimbra -c.

warning

Zimbra 8.6 and later have a bug that can overwrite Postfix's smtpd_milters parameter with an empty value, so the milter silently stops receiving mail (Bug 97706). Work around it by enabling Zimbra's own milter server (zimbraMilterServerEnabled) and starting it with zmmilterctl start before configuring the MSH Zimbra Rules milter below, even if you do not otherwise use Zimbra's built-in milter - see the FAQ for what that built-in milter actually does.

sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMilterServerEnabled TRUE'
sudo su - zimbra -c 'zmmilterctl start'

Register the milter for mail arriving over SMTP (from external clients and other mail servers).

sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaSmtpdMilters "inet:localhost:7080"'

Register the milter for locally injected mail (sendmail, cron jobs, local applications). Set it only if you also want internally generated mail filtered.

sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaNonSmtpdMilters "inet:localhost:7080"'

Set the default action Postfix takes when the milter is unavailable. With accept, mail still flows if the milter is down instead of being deferred. Use tempfail instead if you would rather hold mail when the milter is unavailable.

sudo su - zimbra -c 'zmprov ms $(zmhostname) zimbraMtaMilterDefaultAction accept'

Reload the Zimbra MTA so Postfix regenerates its configuration with the milter settings.

sudo su - zimbra -c 'zmmtactl reload'

Verify​

Confirm Postfix on this MTA server picked up the milter after the reload:

sudo su - zimbra -c 'postconf smtpd_milters non_smtpd_milters milter_default_action'

Expected output:

smtpd_milters = inet:localhost:7080, inet:127.0.0.1:7026
non_smtpd_milters = inet:localhost:7080
milter_default_action = accept

The caveats - Zimbra's own milter used for distribution-list access control, and older Zimbra versions - are the same as on a single server, see Caveats.

Once every MTA server is done, list the MTA servers and check that each of them has the milter set:

sudo su - zimbra -c 'zmprov gas mta'
sudo su - zimbra -c 'zmprov gs <mta-host> zimbraMtaSmtpdMilters zimbraMtaNonSmtpdMilters zimbraMtaMilterDefaultAction'

What's next​

  • Repeat this page for the next MTA server
  • Zimlets configuration - let users change their own attributes used in signatures
  • Servers - server states and what to do when a server is not up to date