Skip to main content

Block a Sender or a Whole Domain in Zimbra with Sieve

Some senders keep writing long after being asked to stop, and some domains send nothing anyone needs. Sieve can stop their mail at delivery, for one mailbox or for the whole organization. The script is trivial. The interesting decision is how to refuse, because the four options differ in what the sender learns.

Part of the series on Everyday Uses for Sieve Mail Filters in Zimbra.

The filter

require ["envelope"];

if anyof (envelope :domain :is "from" ["spam.example", "marketing.example"],
envelope :is "from" ["persistent@sender.example",
"noreply@oldvendor.example"]) {
discard;
stop;
}

envelope :domain :is blocks an entire domain, envelope :is blocks one exact address, and anyof lets a single rule carry both lists. Add addresses to the lists as you go.

Use envelope rather than address here. The envelope sender is set by the delivering server, so a sender who changes the From: header to get around your block is still caught.

Choosing how to refuse

discard is only one of four ways to end this rule, and they behave very differently.

ActionWhat the sender learnsWhere the message goes
discardNothing at allDeleted silently
fileinto "Junk"NothingJunk folder, still readable
rejectGets a refusal notice backNot stored
erejectGets an SMTP bounceRefused at delivery

discard is the right default. The message disappears without a trace and the sender gets no signal. That last part matters for bulk senders, because a bounce confirms the address is real and being read.

fileinto "Junk" is the safe default. It is the same rule with the delete replaced by a folder, and it gives you a month of evidence that the block is catching what you intended and nothing else. Start here and switch to discard once you trust the list.

require ["envelope", "fileinto"];

if envelope :domain :is "from" "spam.example" {
fileinto "Junk";
stop;
}

reject and ereject tell the sender. Use them when the sender is a person who should know their mail is not getting through - a former supplier still emailing an old contact, for instance - and not for anything resembling spam.

require ["envelope", "ereject"];

if envelope :domain :is "from" "oldvendor.example" {
ereject "This address no longer accepts mail from your organization.";
}

reject accepts the message and then mails the sender a refusal containing your text. It works only while zimbraSieveRejectMailEnabled is TRUE, which is the default. ereject refuses the delivery itself: Zimbra answers Postfix with 550 5.7.1 Message rejected and Postfix bounces it. Your text is not passed on to the sender, so with ereject treat the string as a comment.

Blocking a pattern rather than a list

:matches blocks a family of subdomains in one line, which is useful against senders who rotate through them:

require ["envelope"];

if envelope :domain :matches "from" ["*.spam.example", "*.bulk.example"] {
discard;
stop;
}

Note that *.spam.example matches mail.spam.example but not spam.example itself. List both if you want both.

Resist the urge to go broader than this. A rule like :matches "from" "*marketing*" will eventually eat a message someone needed, and because discard leaves no trace, nobody will ever find out why.

Applying it

Nothing here needs editheader, so the filter works at any level without configuration.

For one user:

sudo su - zimbra
zmprov ma user@example.com zimbraMailSieveScript "$(cat /tmp/block.sieve)"

For a whole domain, as an admin script that runs before anyone's own filters:

zmprov md example.com zimbraAdminSieveScriptBefore "$(cat /tmp/block.sieve)"

# check what is stored
zmprov gd example.com zimbraAdminSieveScriptBefore

In the web client, a user can build the same rule under Preferences > Filters with a condition on the sender and an action of Discard or Move into folder.

The Filters page in the Zimbra web client preferences, with the Incoming Message Filters and Outgoing Message Filters tabs and the Create Filter, Edit Filter, Delete Filter and Run Filter buttons

In the rule builder this is a single condition - From matching the address or the domain - with Discard or Move into folder as the action, and Do not process additional filters ticked to get the effect of stop.

Blocking earlier is usually better

A Sieve block still costs you the whole delivery. Postfix accepts the message, amavis scans it, the mailbox server stores it and then the script throws it away. For one nuisance sender that is fine. For a domain sending thousands of messages a day it is wasted work, and the right place to refuse is Postfix, where the message is turned away during the SMTP session and never enters the system:

# on the Zimbra server, as the zimbra user
zmprov mcf +zimbraMtaRestriction "check_sender_access lmdb:/opt/zimbra/conf/postfix_reject_sender"

MSH Zimbra Rules sits at the same point in the path and can refuse a message during the SMTP session based on rules you manage in a web interface, per domain, without editing Postfix maps by hand.

Use Sieve when the block belongs to one mailbox or one small list. Use the MTA when it belongs to the whole server.

Next in this series

Back to Everyday Uses for Sieve Mail Filters in Zimbra, or on to Sort Mailing Lists and Newsletters Out of the Inbox.