Postfix Routing, Relay Domains and Transport Decisions
Postfix must distinguish domains it owns, domains it relays for, and ordinary remote destinations before choosing a delivery transport. This lesson treats Postfix relay and transport routing as one decision point in a longer message path. The configuration is useful only when an operator can show which message entered it, which identity and rule matched, what the next daemon returned, and how a failure is retried or contained.
Prerequisites and inherited lab checkpoint
Lesson 5 must return correct positive and negative virtual-recipient and alias lookups before routing is added.
For the Postfix relay and transport routing lab, use reserved domain example.test, documentation addresses and disposable messages. Preserve postconf -n, postconf -M, package versions, DNS answers and a queue baseline before changing the lab. Keep console access and do not copy credentials, private keys or customer messages into the evidence record.
Install and prepare the required components
sudo dnf info postfix cyrus-sasl-plain
sudo dnf install -y postfix cyrus-sasl-plain
sudo cp -a /etc/postfix /etc/postfix.before-nitwings
rpm -q postfix cyrus-sasl-plain- Confirm package availability and ownership in the enabled RHEL repositories before adding a third-party source. Record the repository, signing key fingerprint, version and support lifecycle.
- A package install creates files and service identities; it does not establish safe relay, authentication, delivery or filtering behavior.
- Back up only the files this lesson changes and record modes, owners and SELinux labels so rollback restores more than text.
- Use
systemctl cat, package file lists and local manual pages to identify paths on the installed build instead of assuming a path from another distribution.
Build the Postfix relay and transport routing configuration and understand every boundary
Route one explicitly relayed domain through a private next hop and validate its recipients at RCPT time.
# /etc/postfix/main.cf
relay_domains = branch.example.test
relay_recipient_maps = hash:/etc/postfix/relay_recipients
transport_maps = hash:/etc/postfix/transport
# /etc/postfix/relay_recipients
[email protected] OK
# /etc/postfix/transport
branch.example.test smtp:[relay.example.test]:25Square brackets suppress MX lookup for the relay hostname. The recipient map prevents acceptance of every address in the relayed domain and the backscatter that would follow downstream rejection.
Verify the working path
sudo postmap /etc/postfix/relay_recipients
sudo postmap /etc/postfix/transport
postmap -q [email protected] hash:/etc/postfix/relay_recipients
postmap -q branch.example.test hash:/etc/postfix/transport
sudo postfix check
sudo postfix reload
sendmail -bv [email protected]- Run the syntax or lookup test before reload. A reload must never be the first parser of a production configuration.
- The positive test proves the intended path. The negative test proves an unauthorized sender, recipient or client is not accidentally accepted.
- Stop the named dependency in the disposable lab and verify temporary failure or controlled bypass matches the documented policy.
- Repeat the accepted path after restart and reboot, then compare effective configuration rather than only source files.
Production decisions before continuing
| Decision | Choose deliberately | Evidence to retain |
|---|---|---|
| Failure policy | A failed next hop must leave mail deferred for retry; database or transport failure must not become permanent unknown-recipient rejection. | SMTP transcript, queue state and dependency alert |
| Trust boundary | Only named domains and recipients are relayed. Client relay authorization remains restricted to narrow networks or authenticated submission. | Matching client, sender, recipient or daemon identity |
| Secrets and data | Restrict credentials, message samples and keys to the minimum service identity. | Owner, mode, label and secret rotation record |
| Activation | Validate, reload, run positive and negative tests, then watch one complete message. | Syntax output, queue ID and linked log events |
| Rollback | Restore the exact files and map/database state changed by this lesson. | Rollback command and repeated acceptance result |
Place Postfix relay and transport routing in the message path
Address class authorizes destination handling. A transport map selects the next delivery mechanism but does not grant arbitrary relay permission.Understand the component before configuring it
| Layer | Question to answer | Evidence |
|---|---|---|
| Input | Envelope recipient after alias processing | SMTP transcript, lookup input or message header |
| Decision | Address class, recipient existence and most-specific transport match | Effective configuration and exact matched rule |
| Output | Named smtp/lmtp/local/virtual transport and next hop | Queue state, delivery status or downstream response |
| Dependency | Recipient map, DNS, next-hop TCP/TLS and remote SMTP response | Socket, timeout, journal and controlled outage test |
| Recovery | Can processing resume without duplicate, loss or unauthorized delivery? | Retained queue ID, backup and repeated acceptance |
Build it step by step
- Draw the path. Mark the connection, envelope, content and authenticated identities available at this stage.
- Inventory the effective state. Capture package version, active service, sockets, Postfix parameters, master services and lookup results.
- Prepare one coherent configuration. Substitute documented lab values and verify ownership, mode and SELinux context.
- Validate before activation. Run component syntax checks, map queries and a non-delivering test where supported.
- Exercise three outcomes. Send an intended message, an intended denial and a message while the named dependency is unavailable.
- Trace one queue ID. Join ingress, policy, filtering, routing and final delivery events without relying on subject text.
- Close the change. Restart or reboot where relevant, repeat tests, check queue age and document rollback.
Operate and inspect the component
postconf mydestination virtual_mailbox_domains relay_domains transport_maps relayhost
postmap -q branch.example.test hash:/etc/postfix/transport
postqueue -p
qshape deferred
postcat -q QUEUE_ID
journalctl -u postfix --since '-15 minutes'- Replace sample hostnames, addresses and queue IDs only after resolving them from the lab. Do not paste production identities into a public command transcript.
postconf -nshows non-default global parameters;postconf -Mandpostconf -Pexpose master service and field overrides.- A successful lookup proves only that input. Test present, absent, disabled and dependency-unavailable results separately.
- Use the queue ID as the correlation key. Message subjects and recipient addresses are not unique and may contain sensitive information.
Evidence and acceptance criteria
| Evidence | Healthy result | Failure meaning |
|---|---|---|
| Syntax | All component validators succeed before activation | The running service would parse an unreviewed or invalid state |
| Positive path | The listed recipient is accepted and delivered once through the bracketed relay. | The intended message cannot complete this decision point |
| Negative path | An unknown branch recipient and unrelated unauthenticated relay destination are rejected during RCPT. | The configuration may relay, authenticate, route or deliver too broadly |
| Dependency failure | Stopping the relay creates a deferred record with a future retry and no message loss. | Messages may be lost, permanently rejected or silently bypass controls |
| Persistence | Effective state and tests agree after restart and reboot | Only transient state was changed |
A catch-all relay domain creates backscatter
A migration edge lists a customer domain in relay_domains but has no recipient truth. It accepts typos, then the internal server rejects them after DATA, producing nondelivery reports toward forged senders. A maintained relay-recipient map moves the rejection to the original RCPT transaction.Troubleshooting by symptom
| Symptom | Inspect first | Defensible next action |
|---|---|---|
| Mail loops | Received chain, transport result and address class on both hosts | Choose one final destination and correct the returning route |
| Transport ignored | Effective map order and exact postmap key | Correct the lookup key and rebuild the indexed map |
| All recipients accepted | relay_recipient_maps output | Return a value only for provisioned recipients |
| Smart-host login fails | TLS port, SASL map and file permissions | Fix outbound authentication without weakening inbound relay policy |
Unsafe operations and recovery boundaries
- Unsafe: a broad relay domain without recipient validation accepts mail that cannot be delivered.
- Unsafe: world-readable relay credentials expose the smart-host account.
- Unsafe: flushing the entire queue destroys legitimate mail and incident evidence.
Rewritten knowledge checks
Cumulative lab checkpoint
- Capture the inherited checkpoint and state the exact sender, recipient, client address and expected SMTP result.
- Install the required package from a recorded source and save the package/file/service inventory.
- Apply the complete lab configuration, including permissions, socket paths, map generation and service ownership.
- Run syntax and lookup validation, then activate without closing the recovery session.
- Complete positive, negative and dependency-outage tests while retaining queue IDs and UTC logs.
- Restart the participating services, repeat the accepted path and confirm no unexplained deferred mail remains.
- Execute rollback once, prove the previous behavior, then reapply the reviewed state as the checkpoint for the next lesson.