Postfix SMTP relay and access control


Introduction

The Postfix SMTP server receives mail from the network and is exposed to the big bad world of junk email and viruses. This document introduces the built-in and external methods that control what SMTP mail Postfix will accept, what mistakes to avoid, and how to test your configuration.

Topics covered in this document:

Relay control, junk mail control, and per-user policies

In a distant past, the Internet was a friendly environment. Mail servers happily forwarded mail on behalf of anyone towards any destination. On today's Internet, spammers abuse servers that forward mail from arbitrary systems, and abused systems end up on anti-spammer denylists. See, for example, the information on https://www.spamhaus.org/ and other websites.

By default, Postfix has a moderately restrictive approach to mail relaying. Postfix forwards mail only from clients in trusted networks, from clients that have authenticated with SASL, or to domains that are configured as authorized relay destinations. For a description of the default mail relay policy, see the smtpd_relay_restrictions parameter in the postconf(5) manual page, and the information that is referenced from there.

NOTE: Postfix versions before 2.10 did not have smtpd_relay_restrictions. They combined the mail relay and spam blocking policies, under smtpd_recipient_restrictions. This could lead to unexpected results. For example, a permissive spam blocking policy could unexpectedly result in a permissive mail relay policy. An example of this is documented under "Dangerous use of smtpd_recipient_restrictions".

Most of the Postfix SMTP server access controls are targeted at stopping junk email.

Unfortunately, all junk mail controls have the possibility of falsely rejecting legitimate mail. This can be a problem for sites with many different types of users. For some users it is unacceptable when any junk email slips through, while for other users the world comes to an end when a single legitimate email message is blocked. Because there is no single policy that is "right" for all users, Postfix supports different SMTP access restrictions for different users. This is described in the RESTRICTION_CLASS_README document.

Restrictions that apply to all SMTP mail

Besides the restrictions that can be made configurable per client or per user as described in the next section, Postfix implements a few restrictions that apply to all SMTP mail.

Getting selective with SMTP access restriction lists

Postfix allows you to specify lists of access restrictions for each stage of the SMTP conversation. Individual restrictions are described in the postconf(5) manual page.

Examples of simple restriction lists are:

/etc/postfix/main.cf:
    # Allow connections from trusted networks only.
    smtpd_client_restrictions = permit_mynetworks, reject

    # Don't talk to mail systems that don't know their own hostname.
    # With Postfix < 2.3, specify reject_unknown_hostname.
    smtpd_helo_restrictions = reject_unknown_helo_hostname

    # Don't accept mail from domains that don't exist.
    smtpd_sender_restrictions = reject_unknown_sender_domain

    # Spam control: exclude local clients and authenticated clients
    # from DNSBL lookups.
    smtpd_recipient_restrictions = permit_mynetworks, 
        permit_sasl_authenticated,
        # reject_unauth_destination is not needed here if the mail
        # relay policy is specified under smtpd_relay_restrictions
        # (available with Postfix 2.10 and later).
        reject_unauth_destination
        reject_rbl_client zen.spamhaus.org,
        reject_rhsbl_reverse_client dbl.spamhaus.org,
        reject_rhsbl_helo dbl.spamhaus.org,
        reject_rhsbl_sender dbl.spamhaus.org

    # Relay control (Postfix 2.10 and later): local clients and
    # authenticated clients may specify any destination domain.
    smtpd_relay_restrictions = permit_mynetworks, 
        permit_sasl_authenticated,
        reject_unauth_destination

    # Block clients that speak too early.
    smtpd_data_restrictions = reject_unauth_pipelining

    # Enforce mail volume quota via policy service callouts.
    smtpd_end_of_data_restrictions = check_policy_service unix:private/policy

Each restriction list is evaluated from left to right until some restriction produces a result of PERMIT, REJECT or DEFER (try again later). The end of each list is equivalent to a PERMIT result. By placing a PERMIT restriction before a REJECT restriction you can make exceptions for specific clients or users. This is called allowlisting; the smtpd_relay_restrictions example above allows mail from local networks, and from SASL authenticated clients, but otherwise rejects mail to arbitrary destinations.

The table below summarizes the purpose of each SMTP access restriction list. All lists use the exact same syntax; they differ only in the time of evaluation and in the effect of a REJECT or DEFER result.

Restriction list name Version Status Effect of REJECT or DEFER result
smtpd_client_restrictions All Optional Reject all client commands
smtpd_helo_restrictions All Optional Reject HELO/EHLO information
smtpd_sender_restrictions All Optional Reject MAIL FROM information
smtpd_recipient_restrictions ≥ 2.10 Required if smtpd_relay_restrictions does not enforce relay policy Reject RCPT TO information
< 2.10 Required
smtpd_relay_restrictions ≥ 2.10 Required if smtpd_recipient_restrictions does not enforce relay policy Reject RCPT TO information
< 2.10 Not available
smtpd_data_restrictions ≥ 2.0 Optional Reject DATA command
smtpd_end_of_data_restrictions ≥ 2.2 Optional Reject END-OF-DATA command
smtpd_etrn_restrictions All Optional Reject ETRN command

Delayed evaluation of SMTP access restriction lists

Early Postfix versions evaluated SMTP access restrictions lists as early as possible. The client restriction list was evaluated before Postfix sent the "220 $myhostname..." greeting banner to the SMTP client, the helo restriction list was evaluated before Postfix replied to the HELO (EHLO) command, the sender restriction list was evaluated before Postfix replied to the MAIL FROM command, and so on. This approach turned out to be difficult to use.

Current Postfix versions postpone the evaluation of client, helo and sender restriction lists until the RCPT TO or ETRN command. This behavior is controlled by the smtpd_delay_reject parameter. Restriction lists are still evaluated in the proper order of (client, helo, etrn) or (client, helo, sender, relay, recipient, data, or end-of-data) restrictions. When a restriction list (example: client) evaluates to REJECT or DEFER the restriction lists that follow (example: helo, sender, etc.) are skipped.

Around the time that smtpd_delay_reject was introduced, Postfix was also changed to support mixed restriction lists that combine information about the client, helo, sender and recipient or etrn command.

Benefits of delayed restriction evaluation, and of restriction mixing:

Dangerous use of smtpd_recipient_restrictions

By now the reader may wonder why we need smtpd client, helo or sender restrictions, when their evaluation is postponed until the RCPT TO or ETRN command. Some people recommend placing ALL the access restrictions in the smtpd_recipient_restrictions list. Unfortunately, this can result in too permissive access. How is this possible?

The purpose of the smtpd_recipient_restrictions feature is to control how Postfix replies to the RCPT TO command. If the restriction list evaluates to REJECT or DEFER, the recipient address is rejected; no surprises here. If the result is PERMIT, then the recipient address is accepted. And this is where surprises can happen.

The problem is that Postfix versions before 2.10 did not have smtpd_relay_restrictions. They combined the mail relay and spam blocking policies, under smtpd_recipient_restrictions. The result is that a permissive spam blocking policy could unexpectedly result in a permissive mail relay policy.

Here is an example that shows when a PERMIT result can result in too much access permission:

1 /etc/postfix/main.cf:
2     smtpd_recipient_restrictions = 
3         permit_mynetworks
4         check_helo_access hash:/etc/postfix/helo_access
5         reject_unknown_helo_hostname
6         reject_unauth_destination
7 
8 /etc/postfix/helo_access:
9     localhost.localdomain PERMIT

Line 5 rejects mail from hosts that don't specify a proper hostname in the HELO command (with Postfix < 2.3, specify reject_unknown_hostname). Lines 4 and 9 make an exception to allow mail from some machine that announces itself with "HELO localhost.localdomain".

The problem with this configuration is that smtpd_recipient_restrictions evaluates to PERMIT for EVERY host that announces itself as "localhost.localdomain", making Postfix an open relay for all such hosts.

With Postfix before version 2.10 you should place non-recipient restrictions AFTER the reject_unauth_destination restriction, not before. In the above example, the HELO based restrictions should be placed AFTER reject_unauth_destination, or better, the HELO based restrictions should be placed under smtpd_helo_restrictions where they can do no harm.

1 /etc/postfix/main.cf:
2     smtpd_recipient_restrictions = 
3         permit_mynetworks
4         reject_unauth_destination
5         check_helo_access hash:/etc/postfix/helo_access
6         reject_unknown_helo_hostname
7 
8 /etc/postfix/helo_access:
9     localhost.localdomain PERMIT

The above mistake will not happen with Postfix 2.10 and later, when the relay policy is specified under smtpd_relay_restrictions, and the spam blocking policy under smtpd_recipient_restrictions. Then, a permissive spam blocking policy will not result in a permissive mail relay policy.

SMTP access rule testing

Postfix has several features that aid in SMTP access rule testing:

soft_bounce

This is a safety net that changes SMTP server REJECT actions into DEFER (try again later) actions. This keeps mail queued that would otherwise be returned to the sender. Specify "soft_bounce = yes" in the main.cf file to prevent the Postfix SMTP server from rejecting mail permanently, by changing all 5xx SMTP reply codes into 4xx.

warn_if_reject

When placed before a reject-type restriction, access table query, or check_policy_service query, this logs a "reject_warning" message instead of rejecting a request (when a reject-type restriction fails due to a temporary error, this logs a "reject_warning" message for any implicit "defer_if_permit" actions that would normally prevent mail from being accepted by some later access restriction). This feature has no effect on defer_if_reject restrictions.

XCLIENT

With this feature, an authorized SMTP client can impersonate other systems and perform realistic SMTP access rule tests. Examples of how to impersonate other systems for access rule testing are given at the end of the XCLIENT_README document.
This feature is available in Postfix 2.1.