127 lines
5.4 KiB
Markdown
127 lines
5.4 KiB
Markdown
+++
|
||
date = '2025-08-01T10:03:15+02:00'
|
||
draft = false
|
||
title = 'Blocking Invalid Recipients Before They Reach Your Exchange Server'
|
||
|
||
|
||
[cover]
|
||
image = "/imgs/email-route.jpg"
|
||
alt = "Email App icon on blue background"
|
||
caption = ""
|
||
+++
|
||
|
||
Recently, I had to deal with a serious problem: **backscatter**.
|
||
One of our mail gateways ended up listed on the **backscatter.org** blacklist for sending bounce messages to forged senders.
|
||
|
||
After checking the logs, I quickly realized that our system wasn't actually protected against backscatter attacks, so I had to do something about it.
|
||
|
||
|
||
|
||
## What Even Is Backscatter?
|
||
|
||
Backscatter is unwanted email that your mail server sends **after** receiving a message, usually in the form of a **non-delivery report (NDR)** or **bounce** to a **forged sender address**.
|
||
|
||
It happens when:
|
||
|
||
- A spammer sends email with a **fake "From" address** (often an innocent third party).
|
||
- Your mail server **accepts** the message first, but later discovers it’s undeliverable.
|
||
- Your server sends a **bounce** to the forged address, hitting an innocent person instead of the spammer.
|
||
|
||
This makes your server appear to be sending spam, even though you're just bouncing bad mail.
|
||
|
||
|
||
|
||
## The Problem in Our Setup
|
||
|
||
Our mail gateway mostly relays mail to multiple internal Exchange Server clusters.
|
||
We have a list of valid domains configured, so Postfix will only accept mail for those domains.
|
||
|
||
**The problem?**
|
||
Postfix didn't know which individual recipients were valid on the downstream Exchange servers.
|
||
That meant it would happily accept messages for **nonexistent users**, only to later bounce them, classic backscatter behavior.
|
||
|
||
|
||
|
||
## The Goal
|
||
|
||
We needed to reject mail **during the SMTP session**, ideally right after the `RCPT TO` command, if the recipient didn't exist on the Exchange servers.
|
||
|
||
That way, the sending server would get the rejection immediately, and we would never have to generate a bounce message.
|
||
|
||
|
||
|
||
## Possible Solutions I Considered
|
||
|
||
### **1. relay_recipient_maps**
|
||
This would require maintaining a **full list of valid recipients** in a Postfix lookup table.
|
||
It works well, but means writing and maintaining a script to **sync the list from Active Directory** on a regular basis.
|
||
|
||
For us, this was too much custom scripting, too fragile, and too messy to maintain.
|
||
|
||
|
||
|
||
### **2. virtual_mailbox_maps with LDAP**
|
||
Another approach would be using **LDAP lookups** directly against Active Directory to verify recipients in real-time.
|
||
|
||
This can work in some setups, but:
|
||
- It adds complexity and dependencies.
|
||
- It can introduce security concerns.
|
||
- It didn’t fit well with our environment.
|
||
|
||
So I ruled it out.
|
||
|
||
|
||
|
||
## The Solution: `reject_unverified_recipient`
|
||
|
||
While reading through the **Postfix Address Verification Howto**, I came across the `reject_unverified_recipient` option - bling! - exactly what I needed.
|
||
|
||
|
||
|
||
### **How It Works**
|
||
|
||
When an incoming SMTP session reaches the `RCPT TO` stage:
|
||
|
||
1. Postfix checks if `reject_unverified_recipient` is enabled for the recipient domain.
|
||
2. If yes, it temporarily **probes the downstream mail system** to see if the recipient address exists.
|
||
3. If the downstream system says:
|
||
- **User exists** → Postfix continues processing.
|
||
- **User does not exist** → Postfix **rejects immediately** with:
|
||
```
|
||
550 5.1.1 <user@example.de>: Recipient address rejected: User unknown
|
||
```
|
||
|
||
Because the rejection happens **during SMTP**, no bounce is generated, and backscatter is avoided entirely.
|
||
|
||
|
||
## Implementation in ISPConfig
|
||
|
||
In our case, the mail gateways run **Postfix** with **ISPConfig** as the management interface.
|
||
I implemented a new configuration option in ISPConfig for **per-domain control** of `reject_unverified_recipient`, along with a **validation server** to specify for the downstream validation server.
|
||
|
||
### Specifying a Validation Server for Recipient Verification
|
||
|
||
One important detail is that Exchange servers cannot validate recipients over the default SMTP transport on port `25`. To enable recipient validation, you need to activate it on the Exchange server and use the Hub Transport service, which by default runs on port `2525`.
|
||
|
||
Make sure to restrict access to this port, as it requires anonymous login specifically for recipient validation, you don't want that exposed broadly.
|
||
|
||
> **Note:** This setup does not affect your regular mail flow. Only the SMTP probes used for verifying recipients are sent to this validation server.
|
||
> Postfix enables this behavior with the `address_verify_transport_maps` option.
|
||
|
||
To optimize performance and reduce unnecessary verification probes for addresses that have already been checked, I configured a local cache on the mail gateway using the `address_verify_map` option. This way, repeated probes for the same recipient are avoided.
|
||
|
||
Now, for domains that need recipient verification, we can enable it in the panel and point Postfix to the appropriate **validation transport**.
|
||
|
||
|
||
## The Result
|
||
|
||
After enabling `reject_unverified_recipient` and pointing it at the Exchange clusters, the backscatter stopped completely.
|
||
We were no longer accepting messages for invalid recipients.
|
||
|
||
## Conclusion
|
||
|
||
If your Postfix server is acting as a relay for Exchange (or any downstream mail system) and you're struggling with **backscatter spam**, enabling `reject_unverified_recipient` can be a clean and effective fix.
|
||
|
||
It avoids maintaining large static recipient maps, works dynamically, and ensures that invalid mail is rejected **before** it ever gets into your system.
|
||
|