Postfix Open Relay Risk: Test It, Prove It, Close It

Was this helpful?

Nothing ages you faster than an email outage where the root cause is “we became an open relay.” One minute you’re shipping a normal release; the next you’re watching your public IP get blocklisted while your CEO wonders why “our mail server is spamming Moldova.”

This is not a theoretical risk. It’s a configuration failure mode with clear, repeatable tests and unambiguous evidence in logs. The fix is boring, precise, and completely within your control. Let’s do it the way production systems demand: test it, prove it, close it, and document it so it doesn’t creep back in.

What “open relay” actually means (and what it doesn’t)

An open relay is an SMTP server that will accept a message from an unauthenticated, untrusted client and forward it to a third-party domain. In plain terms: random internet host connects to your Postfix, hands it a message “from” anyone “to” anyone, and your server agrees to deliver it outward.

That’s the core. Everything else is noise.

  • Not an open relay: a server that accepts mail for its own domains (mydestination, virtual domains) from the internet and delivers locally.
  • Not an open relay: a server that relays outbound mail only for authenticated users (SASL on submission) or trusted networks (mynetworks).
  • Is an open relay: accepts mail from arbitrary sources and relays to arbitrary destinations.

The dangerous misunderstanding: “We’re behind a firewall” or “Port 25 is only for inbound” can turn into “Sure, let’s allow relaying from 10.0.0.0/8” and then someone adds a VPN, a misrouted subnet, or a container network that shouldn’t be trusted. Congratulations, you’ve given your infrastructure the email equivalent of a guest pass that never expires.

Exactly what you must be able to say: “From an untrusted IP, without authentication, Postfix rejects attempts to relay to non-local destinations with a clear policy error.” That’s your proof. Everything else is vibes.

Facts and history: why this keeps happening

Open relays are an old problem, but they keep resurfacing because mail is a compatibility minefield and people treat it like a checkbox.

  1. Early SMTP assumed a friendly network. The original mail ecosystem behaved like a club where everyone knew everyone. That assumption died on the public internet.
  2. In the 1990s, open relays were everywhere. They were often configured to “help” deliver mail from dial-up users or misconfigured small-business networks.
  3. Blocklists became a survival mechanism. Once spam exploded, operators started publishing IPs of known relays and spammers. Deliverability became an operational, not just technical, property.
  4. Postfix was designed to be safer than its predecessors. One reason Postfix got traction is that it aimed to be secure-by-default compared to the “edit this one file and hope” culture.
  5. Submission (587) exists because port 25 is messy. The industry split “server-to-server” SMTP from “user submission” to improve authentication and policy control.
  6. SASL and TLS didn’t “solve” spam, but they changed where you enforce policy. The modern design is: authenticate users on 587, be strict on 25.
  7. NAT and cloud networking created new trust boundaries. “Internal IP” no longer means “trusted operator-controlled host.” It might mean “some container you forgot.”
  8. Misconfiguration is the dominant cause. Open relays are rarely sophisticated breaches. They’re usually “we set mynetworks=0.0.0.0/0 during a test and forgot.”

One quote that belongs on a sticky note near your MTA config: “Hope is not a strategy.” — Gordon R. Dickson

Threat model: how attackers find and exploit relays

Attackers don’t need to “hack” Postfix to abuse it. They just need it to say “OK” at the wrong time.

Discovery

They scan. Constantly. Entire IPv4 ranges get probed for SMTP on 25, 465, 587. The scanning host might be a botnet node, a VPS, or something already blocklisted. You’ll see brief connections, HELO strings that look like a random keyboard smash, and a quick attempt to send to an external domain.

Exploitation

If relaying is allowed, the attacker hands your server a pile of mail. Your system becomes the cannon; your IP becomes the fingerprint. You pay for bandwidth, queue storage, CPU, and reputation.

Impact

  • Immediate: mail queue growth, disk usage spikes, SMTP worker saturation, legitimate outbound mail delayed or dropped.
  • Secondary: blocklisting; partners reject mail; password reset emails don’t arrive; support tickets multiply.
  • Long tail: deliverability recovery takes time even after the relay is fixed. Reputation is sticky; so are blocklists.

Joke #1: An open relay is like leaving a “free shipping” sign on your loading dock. The internet will not politely take just one box.

Fast diagnosis playbook (first/second/third checks)

If you suspect open relay exposure—or you’re responding to “why is the mail queue exploding”—don’t wander around config files like you’re searching for a lost key. Run the same three checks every time.

First: Confirm the symptom (are we relaying?)

  • Look for status=sent to external domains originating from suspicious IPs.
  • Look for Relay access denied (good) versus successful relay (bad).
  • Check whether the influx is on port 25 (server-to-server) or 587 (submission).

Second: Identify the policy gate that failed

  • Inspect smtpd_recipient_restrictions, smtpd_relay_restrictions, mynetworks, and SASL settings.
  • Confirm what Postfix believes is “local” via mydestination and virtual domain maps.

Third: Stop the bleeding safely

  • Temporarily tighten restrictions (reject relaying from everywhere except known networks / auth).
  • Rate-limit and/or reject suspicious clients; consider blocking at the firewall if traffic is extreme.
  • Flush and control the queue (don’t just delete blindly—identify scope first).

Once the fire is out, you can do the more careful work: prove non-relay behavior from outside, validate submission configuration, and update runbooks.

Prove it with tests: commands, outputs, decisions (12+ tasks)

Below are practical tasks you can run on a typical Linux host running Postfix. The point is not to admire the output. The point is to make decisions from it.

Task 1: Confirm Postfix is running and what ports it’s listening on

cr0x@server:~$ systemctl status postfix --no-pager
● postfix.service - Postfix Mail Transport Agent
     Loaded: loaded (/lib/systemd/system/postfix.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-01-03 08:11:32 UTC; 3h 14min ago
       Docs: man:postfix(1)
   Main PID: 1223 (master)
      Tasks: 4 (limit: 18921)
     Memory: 18.4M
        CPU: 1min 12s

What it means: Postfix is active. If it’s not running, you’re not an open relay—you’re just down.

Decision: Proceed to port checks. If inactive, restore service first, then validate configs before reopening firewall.

cr0x@server:~$ ss -lntp | egrep ':(25|465|587)\s'
LISTEN 0      100          0.0.0.0:25        0.0.0.0:*    users:(("master",pid=1223,fd=13))
LISTEN 0      100          0.0.0.0:587       0.0.0.0:*    users:(("master",pid=1223,fd=15))

What it means: Port 25 and 587 are open on all interfaces. That’s normal for many servers; it’s also why you must be strict with relay policy.

Decision: If this host should not accept public SMTP, bind to specific interfaces or close ports at the firewall/security group.

Task 2: See Postfix’s effective configuration (not what you think you set)

cr0x@server:~$ postconf -n
myhostname = mx1.example.net
mydestination = $myhostname, localhost.example.net, localhost
mynetworks = 127.0.0.0/8 [::1]/128 10.10.0.0/16
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_tls_security_level = may

What it means: This is the effective non-default config. You care most about mynetworks, smtpd_relay_restrictions, and where reject_unauth_destination appears.

Decision: If defer_unauth_destination or reject_unauth_destination is missing, you are in dangerous territory. Fix immediately.

Task 3: Verify mynetworks isn’t “the internet in disguise”

cr0x@server:~$ postconf mynetworks
mynetworks = 127.0.0.0/8 [::1]/128 10.10.0.0/16

What it means: Trust is granted to 10.10.0.0/16. That’s acceptable only if you fully control that network and can vouch for every host that can reach port 25/587.

Decision: If you see 0.0.0.0/0, 10.0.0.0/8 “because it’s internal,” or cloud VPC CIDRs that include third-party or transient workloads, tighten it. Prefer explicit subnets for mail clients, or avoid permit_mynetworks for relaying at all.

Task 4: Confirm the relay restrictions are in the right place (Postfix version nuance)

cr0x@server:~$ postconf smtpd_relay_restrictions
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

What it means: Postfix will permit relaying for trusted networks and authenticated clients, and defer unauthorized destinations (effectively “not allowed”). Modern Postfix expects relay policy here.

Decision: If smtpd_relay_restrictions is empty or overly permissive, you’re a candidate for open relay. Fix it before you do anything else.

Task 5: Test relay behavior from the server itself (baseline)

This test is not enough by itself (because localhost may be in mynetworks), but it confirms your tooling and DNS basics.

cr0x@server:~$ swaks --server 127.0.0.1 --from test@external.example --to someone@gmail.com
=== Trying 127.0.0.1:25...
=== Connected to 127.0.0.1.
<--  220 mx1.example.net ESMTP Postfix
 --> EHLO server
<--  250-mx1.example.net
<--  250 PIPELINING
 --> MAIL FROM:<test@external.example>
<--  250 2.1.0 Ok
 --> RCPT TO:<someone@gmail.com>
<--  554 5.7.1 <someone@gmail.com>: Relay access denied

What it means: The server refused relaying to Gmail. That’s good. If it accepted RCPT and proceeded to DATA, that’s a red alert.

Decision: Even if this looks good, you must test from an untrusted host outside mynetworks.

Task 6: Test relay behavior from an external host (the proof)

Run from a separate machine on the internet that is not in your trusted networks and not authenticated. Use a destination domain you do not control.

cr0x@server:~$ swaks --server mx1.example.net --from probe@external.example --to someone@yahoo.com --timeout 15
=== Trying 203.0.113.10:25...
=== Connected to 203.0.113.10.
<--  220 mx1.example.net ESMTP Postfix
 --> EHLO probehost
<--  250-mx1.example.net
<--  250 STARTTLS
 --> MAIL FROM:<probe@external.example>
<--  250 2.1.0 Ok
 --> RCPT TO:<someone@yahoo.com>
<--  554 5.7.1 <someone@yahoo.com>: Relay access denied

What it means: This is your evidence. The server rejects relaying for unauthenticated external clients.

Decision: Save this output (ticket, incident notes, audit evidence). If it doesn’t reject, treat it as an incident.

Task 7: Confirm it still accepts inbound mail for local domains

cr0x@server:~$ swaks --server mx1.example.net --from probe@external.example --to postmaster@example.net --timeout 15
=== Trying 203.0.113.10:25...
=== Connected to 203.0.113.10.
<--  220 mx1.example.net ESMTP Postfix
 --> EHLO probehost
<--  250-mx1.example.net
 --> MAIL FROM:<probe@external.example>
<--  250 2.1.0 Ok
 --> RCPT TO:<postmaster@example.net>
<--  250 2.1.5 Ok
 --> DATA
<--  354 End data with <CR><LF>.<CR><LF>

What it means: Your server is not “closed for business.” It accepts mail to local recipients while refusing relay to third parties.

Decision: If local delivery fails, check mydestination, virtual domain maps, and recipient tables before you touch relay rules again.

Task 8: Inspect mail logs for relay decisions (what Postfix actually did)

cr0x@server:~$ sudo grep -E "Relay access denied|reject_unauth_destination|status=sent" /var/log/mail.log | tail -n 20
Jan 03 10:44:11 mx1 postfix/smtpd[18844]: NOQUEUE: reject: RCPT from unknown[198.51.100.77]: 554 5.7.1 <someone@yahoo.com>: Relay access denied; from=<probe@external.example> to=<someone@yahoo.com> proto=ESMTP helo=<probehost>
Jan 03 10:44:42 mx1 postfix/smtpd[18845]: NOQUEUE: reject: RCPT from unknown[198.51.100.78]: 554 5.7.1 <random@gmail.com>: Relay access denied; from=<payroll@external.example> to=<random@gmail.com> proto=ESMTP helo=<workstation>

What it means: NOQUEUE and “reject at RCPT” is what you want: cheap rejection before accepting message data.

Decision: If you see successful relays (status=sent) for clients you don’t recognize, start containment and config review now.

Task 9: Check the queue size and whether it’s runaway spam

cr0x@server:~$ mailq | head -n 20
-Queue ID-  --Size-- ----Arrival Time---- -Sender/Recipient-------
A1B2C3D4E5     1423 Tue Jan  3 10:49:02  spammer@fake.example
                                         victim1@gmail.com

F6G7H8I9J0     1501 Tue Jan  3 10:49:03  spammer@fake.example
                                         victim2@yahoo.com

What it means: External recipients and suspicious senders are a classic relay-abuse pattern. The queue will eat disk and IOPS until your box becomes a very expensive space heater.

Decision: If queue growth is active, contain: tighten relay rules, block abusive IPs, and consider temporary rate limits. Then decide whether to purge queued spam after you preserve evidence.

Task 10: Inspect a specific queued message (scope and origin)

cr0x@server:~$ postcat -q A1B2C3D4E5 | sed -n '1,60p'
*** ENVELOPE RECORDS ***
message_size: 1423
message_arrival_time: Tue Jan  3 10:49:02 2026
sender: spammer@fake.example
recipient: victim1@gmail.com
*** MESSAGE CONTENTS ***
Received: from unknown (unknown [198.51.100.77])
        by mx1.example.net (Postfix) with ESMTP id A1B2C3D4E5
        for <victim1@gmail.com>; Tue,  3 Jan 2026 10:49:02 +0000 (UTC)
Subject: Urgent invoice

What it means: It shows a connection from an untrusted IP. That’s your smoking gun if relay was accepted.

Decision: If you confirm abuse, preserve a sample of headers plus log lines. Then purge spam and rotate credentials if any auth was involved.

Task 11: Check master.cf for unintended services (submission, smtps, policy overrides)

cr0x@server:~$ sudo postconf -M | egrep '^(smtp|submission|smtps)/'
smtp/inet/smtp
submission/inet/submission

What it means: Services exist. That’s fine. The danger is per-service overrides that accidentally permit relay broadly.

Decision: Inspect submission and smtps stanzas for -o overrides that weaken restrictions.

cr0x@server:~$ sudo sed -n '1,160p' /etc/postfix/master.cf
smtp      inet  n       -       y       -       -       smtpd
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject

What it means: Submission requires TLS and authentication, and rejects otherwise. That’s good: it prevents “anonymous submission.”

Decision: If you see permit_mynetworks on submission without tight mynetworks, you’re trusting the wrong layer.

Task 12: Confirm SASL is actually working on 587 (or you’ll punch holes elsewhere)

cr0x@server:~$ swaks --server mx1.example.net --port 587 --tls --auth LOGIN --auth-user 'user@example.net' --auth-password 'wrongpass' --from user@example.net --to someone@gmail.com
=== Trying 203.0.113.10:587...
=== Connected to 203.0.113.10.
<--  220 mx1.example.net ESMTP Postfix
 --> EHLO probehost
<--  250-AUTH PLAIN LOGIN
 --> AUTH LOGIN
<--  334 VXNlcm5hbWU6
 --> dXNlckBleGFtcGxlLm5ldA==
<--  334 UGFzc3dvcmQ6
 --> d3JvbmdwYXNz
<--  535 5.7.8 Error: authentication failed: authentication failure

What it means: Auth is enforced. Wrong password fails. Good.

Decision: If auth is “off” or not advertised, fix that rather than expanding mynetworks as a workaround.

Task 13: Validate the server’s local domains and avoid accidental “local = relay” confusion

cr0x@server:~$ postconf mydestination
mydestination = $myhostname, localhost.example.net, localhost

What it means: Only these domains are treated as local for classic local delivery. If you host example.net as an inbound domain, it should appear via virtual_mailbox_domains or similar, not necessarily mydestination.

Decision: Keep local domain definitions precise. Overbroad “local domains” can cause routing loops and policy surprises.

Task 14: Confirm what IP Postfix thinks the client is (proxy and load balancer traps)

cr0x@server:~$ postconf smtpd_upstream_proxy_protocol
smtpd_upstream_proxy_protocol =

What it means: No proxy protocol. If you’re behind a TCP load balancer or reverse proxy for SMTP and you’ve misconfigured client IP preservation, Postfix might treat every client as “internal” (the proxy’s IP), accidentally granting relay.

Decision: If you’re fronting SMTP with a proxy, design carefully: preserve client IP and restrict based on real sources, not the proxy.

Postfix config mental model: the knobs that matter

Postfix can look like a bag of parameters until you learn the shape of the decision tree.

The key distinction: accepting mail vs relaying mail

SMTP policy is mostly decided at the RCPT TO stage:

  • If the recipient is local (a domain you host), Postfix can accept it from the internet. That is normal inbound mail.
  • If the recipient is not local, then accepting it is relaying. Relaying must be restricted to authenticated users or explicitly trusted networks.

reject_unauth_destination is the bouncer

The single most important anti-open-relay control in Postfix is reject_unauth_destination (or its defer variant). It rejects attempts to send to non-local domains unless the client is trusted by earlier rules.

In modern Postfix, you typically place this in smtpd_relay_restrictions. In older configurations, it was common in smtpd_recipient_restrictions. You can run with both, but you should understand exactly which list is evaluated for what. What you must not do is delete it because “it broke forwarding once.” That’s not fixing; that’s removing the brakes because the car squeaks.

mynetworks: the easiest way to shoot yourself in the foot

permit_mynetworks is convenient and dangerous. It says: “if you are in these source IP ranges, you are trusted to relay.” This is fine for a small, controlled environment. It is risky in modern networks where “internal” includes laptops, BYOD, Kubernetes nodes, and ephemeral instances created by CI.

Good patterns:

  • Keep mynetworks limited to loopback and the specific subnet where your authenticated mail clients live (if you must).
  • Prefer authenticated submission (587) for user mail.
  • If applications need relay, authenticate them or restrict by IP with surgical precision.

mydestination and “local domains” define what is not relaying

If Postfix thinks a domain is local, it will accept mail for it (subject to other restrictions). If Postfix thinks a domain is non-local, accepting it is relaying. Misdefining local domains causes two common problems:

  • Rejecting legitimate inbound mail (“user unknown” or “relay access denied” for your own domains).
  • Accidentally accepting mail that should never be handled by this server (routing loops, mail storms, weird compliance exposure).

Submission is where you want to be generous (with authentication)

Port 587 is the place to allow your users and applications to send mail outward, with:

  • TLS enforced
  • SASL authentication enforced
  • Explicit restrictions to avoid turning submission into a backdoor relay

Close it: safe patterns for production Postfix

Let’s be opinionated. If your Postfix instance is internet-facing on port 25, the default posture should be: accept mail only for your hosted domains; do not relay for strangers; provide outbound mail via authenticated submission.

Pattern A: Internet MX for inbound mail, no public outbound relay

This is the “classic” MX role. You accept inbound mail for your domains. You do not let random clients relay outward.

Core settings (illustrative, not copy-paste gospel):

  • smtpd_relay_restrictions: must end with defer_unauth_destination or reject_unauth_destination
  • mynetworks: loopback only, unless you have a controlled internal relay use-case
  • mydestination/virtual domains: exactly your hosted domains

Pattern B: Outbound relay for internal apps (recommended: authenticated)

Your applications want to send mail. You do not want to make IP-based trust decisions across complicated networks.

Use submission with SASL. If your app can’t do SASL, that’s a product problem; treat it like one. If you absolutely must use IP-based trust, isolate it: separate listener bound to an internal interface only, strict firewall, and minimal CIDRs.

Pattern C: Smart host / relayhost (avoid accidental transit)

Some Postfix hosts should never talk directly to the internet. They should send everything via a corporate relayhost or an email security gateway.

This is safer for egress control, but don’t confuse it with open relay prevention. A system can relay all its mail to a smart host and still be an open relay if it accepts unauthenticated external mail and forwards it onward.

Two practical guardrails you should implement

  1. Make “prove not an open relay” a deployment gate. A simple external swaks test in CI/CD or at least in change management.
  2. Reduce blast radius with network controls. Security groups/firewalls restricting who can reach 587, and in some designs even 25 (if you’re not an MX).

Joke #2: Postfix will do exactly what you tell it to do, which is why it sometimes behaves like a very polite but disastrous intern.

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

They migrated mail services from a legacy VM cluster to a cloud environment. The old design relied on “internal network = trusted.” It worked for years because “internal” meant a handful of app servers on a static VLAN. Then the company modernized.

In the new VPC, “internal” included Kubernetes nodes, build agents, jump boxes, and contractor VPN clients landing on the same broad address space. Nobody had the full map in their head. The Postfix config did what it always did: permit_mynetworks for relaying, with mynetworks set to a large RFC1918 range.

The first sign wasn’t spam complaints. It was disk alerts. The mail queue grew fast enough to trip filesystem thresholds. The on-call engineer saw a lot of outbound deliveries to free webmail domains. At first they assumed “credential leak” on the submission port.

The twist was simpler: there was no auth involved. A compromised CI runner inside the VPC was connecting to port 25 and relaying at will because it matched mynetworks. The runner was short-lived, so the source IP changed and blocked attempts felt like whack-a-mole.

The fix was boring and decisive: remove broad RFC1918 trust, require auth for outbound, and restrict port 25 access to only the inbound mail gateway. They also added an external swaks relay test as part of post-change validation. The incident report’s key sentence: “We treated network location as identity.” That assumption used to be common. Now it’s just expensive.

Mini-story 2: The optimization that backfired

A different company ran Postfix as a combined inbound MX and outbound relay for internal apps. They were tuning for performance because marketing campaigns created huge outbound spikes. They wanted fewer “SMTP rejects” in logs because someone misread them as errors.

So an engineer “optimized” by loosening restrictions and moving checks later in the SMTP conversation. The thinking: accept the message quickly, queue it, then decide what to do. They also permitted a larger internal CIDR in mynetworks to avoid auth failures from odd subnets.

That change did reduce RCPT-time rejects. It also increased the cost of saying no. When abuse started—triggered by a compromised host on a trusted subnet—Postfix accepted message data and queued it before policy rejection kicked in downstream. CPU went up. Disk writes went up. Latency went up. The MTA became a denial-of-service engine against itself.

Deliverability then took a hit because the system spent its resources handling garbage. Their outbound legitimate mail arrived late, which caused password reset flows to fail. The business impact looked like an authentication outage, not a mail issue, until someone checked the mail queue depth.

They rolled back to early rejection at RCPT and tightened trust boundaries. Performance improved because rejecting early is cheaper than receiving spam. The lesson: “less noise in logs” is not an optimization target. It’s a vanity metric with an incident attached.

Mini-story 3: The boring but correct practice that saved the day

A financial services team ran Postfix for internal systems and used a dedicated external email security gateway for internet mail. Their configuration wasn’t clever; it was explicit. Port 25 was firewalled to accept connections only from the gateway’s IPs. Port 587 required TLS and SASL, and it was restricted to office/VPN ranges.

They also had a tiny runbook: after any change, run two swaks tests from an external probe host—one to a local recipient (must succeed), one to an external recipient (must fail with relay denied). The outputs were attached to the change ticket. Auditors loved it, but more importantly, the operators did too: it turned mail policy into evidence.

One day, a network change accidentally expanded inbound access to port 25 from the entire internet. It wasn’t malicious; it was a misapplied security group template. The team’s monitoring caught new connection patterns immediately, but the bigger save was the runbook: the very next routine change included the swaks proof step, which failed the “must reject relay” test because another unrelated config drift had also weakened restrictions.

They fixed it before abuse scaled. No blocklist drama, no queue implosion, no angry vendor calls. The practice wasn’t glamorous. It was a checklist and a proof. In production, boring is a feature.

Common mistakes: symptom → root cause → fix

This section is meant to shorten your incident. Find your symptom, stop guessing, and apply the specific fix.

1) Symptom: External swaks test relays successfully to Gmail/Yahoo

  • Root cause: Missing or misplaced reject_unauth_destination/defer_unauth_destination in relay/recipient restrictions.
  • Fix: Set smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination (or reject). Reload and retest externally.

2) Symptom: “Relay access denied” for your own domains

  • Root cause: Your domain isn’t considered local (wrong mydestination, missing virtual domain config, wrong transport maps).
  • Fix: Define hosted domains correctly (virtual domains or mydestination). Don’t “fix” by permitting relaying broadly.

3) Symptom: Works internally, fails for roaming users

  • Root cause: Users rely on IP-based trust (mynetworks) instead of submission auth; VPN/offsite IPs aren’t trusted.
  • Fix: Configure submission (587) with TLS + SASL; tell clients to use it. Stop expanding mynetworks to chase roaming IPs.

4) Symptom: Queue grows, disk fills, logs show many outbound deliveries

  • Root cause: Open relay or compromised authenticated sender; rejection happening too late; no rate limiting.
  • Fix: Prove relay behavior from outside, tighten relay restrictions, block abusive sources, and consider policyd/rate limits. Purge spam after scoping.

5) Symptom: Every client appears as the same IP in logs

  • Root cause: SMTP is behind a proxy/load balancer without proper client IP preservation; Postfix trusts the proxy IP.
  • Fix: Use a design that preserves client IP (proxy protocol when supported) or do not rely on mynetworks for trust. Restrict at the network edge.

6) Symptom: Submission port allows mail without auth

  • Root cause: Misconfigured master.cf submission service; missing smtpd_sasl_auth_enable=yes or overly permissive recipient restrictions.
  • Fix: Enforce auth and reject otherwise on submission. Retest with swaks using wrong password and no auth.

7) Symptom: Some internal hosts can relay, others cannot, and nobody knows why

  • Root cause: Inconsistent routing/NAT or overlapping CIDRs; mynetworks mismatch; multiple Postfix instances with different configs.
  • Fix: Identify real source IPs, tighten trust lists, standardize config with config management, and add external proof tests.

Checklists / step-by-step plan

Checklist A: “Are we an open relay?” (15 minutes, evidence-driven)

  1. From an external host, run swaks to a third-party recipient. Expect Relay access denied.
  2. From the same external host, run swaks to a local domain recipient. Expect 250 Ok and DATA acceptance.
  3. On the Postfix host, capture postconf -n and confirm smtpd_relay_restrictions includes unauth destination rejection.
  4. Check mail logs for recent relay denials and any suspicious successful relays.
  5. Attach the swaks output + relevant log lines to your ticket/change record.

Checklist B: Closing an open relay safely (incident mode)

  1. Contain: Immediately fix relay restrictions; if traffic is overwhelming, block abusive sources at the firewall.
  2. Preserve evidence: Save log excerpts and a few message headers (postcat -q) to confirm origin.
  3. Validate externally: Re-run external swaks relay test until it fails with relay denied.
  4. Queue triage: Inspect queue; identify whether queued mail is legitimate or spam.
  5. Cleanup: Remove spam queue entries after confirming scope; avoid deleting legitimate mail.
  6. Recover deliverability: If you were blocklisted, coordinate delisting and communicate with stakeholders.
  7. Prevent recurrence: Shrink mynetworks, enforce submission auth, and add a recurring proof test.

Checklist C: Hardening changes you can standardize

  1. Restrict port 25 exposure to only what you need (MX traffic or gateway IPs).
  2. Require TLS + SASL on port 587. Don’t make 587 a convenience relay.
  3. Make mynetworks small and reviewed like firewall rules.
  4. Reject early at RCPT stage. Don’t accept DATA unless you mean it.
  5. Add monitoring for queue depth, deferred mail, and unusual outbound volume.

FAQ

1) How do I definitively prove Postfix is not an open relay?

Run an SMTP transaction from an external, untrusted host to a third-party domain. If RCPT is rejected with “Relay access denied,” you have proof. Save the output.

2) Why isn’t testing from localhost enough?

Because localhost is usually trusted via mynetworks. A relay that’s wide open to the internet can still look “secure” when tested from 127.0.0.1.

3) What’s the difference between smtpd_recipient_restrictions and smtpd_relay_restrictions?

smtpd_relay_restrictions is the modern place for relay policy decisions. smtpd_recipient_restrictions controls recipient acceptance more generally. Misplacing rules can weaken your relay controls.

4) Should I use reject_unauth_destination or defer_unauth_destination?

Either prevents open relay. reject is immediate and clear. defer can be used in some policy flows, but it’s easier to reason about reject when hardening.

5) Can I rely on mynetworks alone for internal apps?

You can, but you probably shouldn’t. “Internal” networks are often sprawling and porous. Prefer authenticated submission for apps; if you must use IP trust, isolate it tightly.

6) If port 25 is firewalled to the world, can I ignore open relay risk?

No. If any untrusted network can reach port 25 (including VPNs, peered VPCs, or misconfigured security groups), you can still be abused. Also, config drift happens; proof tests catch it.

7) Why did we get blocklisted if we fixed the relay quickly?

Blocklists react to observed spam volume and behavior, and reputation lags reality. Fixing the relay stops the bleeding; delisting and reputation recovery can take longer.

8) How do I distinguish “open relay” from “compromised mailbox sending spam”?

Open relay spam originates from unauthenticated connections (often port 25) and many source IPs. Compromised accounts usually show SASL-authenticated submissions on 587 from specific user identities and fewer source IPs.

9) Does enabling TLS prevent open relay?

No. TLS encrypts the connection; it doesn’t enforce who is allowed to relay. You still need relay restrictions and/or authentication.

10) What’s the safest minimal configuration goal?

On port 25: accept mail only for domains you host; reject unauthorized relaying. On port 587: require TLS and SASL; allow authenticated outbound.

Next steps you should actually do

Do not wait for a blocklist email to learn whether you’re an open relay. Make it a routine proof.

  1. Run the external swaks relay test today (Task 6). Save the output somewhere auditors and future-you can find it.
  2. Review postconf -n and confirm smtpd_relay_restrictions ends in unauth destination rejection.
  3. Shrink mynetworks to what you can defend in a meeting. Loopback is a fine starting point.
  4. Make submission (587) the official outbound path with TLS + SASL, and stop solving auth problems with CIDRs.
  5. Add one monitoring panel: queue size, deferred count, and outbound volume. It’s amazing how often this catches “mail is weird” before users do.
  6. Turn proof into process: every mail-related change includes the two swaks tests (local accept, external relay deny). Attach the evidence. Repeat forever.
← Previous
WordPress admin-ajax.php 400/403: What Blocks AJAX and How to Fix It
Next →
GPUs after 2026: five future scenarios—from “AI paints it all” to “classic rendering returns”

Leave a comment