Postfix provides the following rules that are assigned restrictions based on client information:
Each one corresponds to a step of the SMTP transaction. At each step, the client provides a piece of information. Using the client-supplied information, Postfix considers one or more restrictions that you assign to each rule. Figure 11-1 shows an SMTP conversation along with the client rule applied at each step. The header_checks and body_checks are discussed later in the chapter.
Let's review the SMTP conversation to see where each of the parameters fits in.
Figure 11-1. SMTP conversation with client rules
11.7.1 The SMTP Conversation (Briefly)
The SMTP conversation in Figure 11-1 should be familiar to you from Chapter 2. Example 11-1 shows the log entries for the transaction. First, an SMTP client connects to Postfix over a socket. Because of the way sockets function, Postfix learns the IP address of the client when it establishes the connection. You don't see the client IP address in the figure, but it is logged by Postfix. You can accept or reject a message based on the client hostname or IP address, thus blocking specific hostnames or IP and network addresses.
Example 11-1. SMTP logging
1. postfix/smtpd[866062]: connect from mail.ora.com[10.143.23.45] 2. postfix/smtpd[866062]: D694B20DD5B: client=[10.143.23.45] 3. postfix/cleanup[864868]: D694B20DD5B: message-id=<20030106185403.D694B20DD5B@smtp.example.com> 4. postfix/qmgr[861396]: D694B20DD5B: from=, size=486, nrcpt=1 (queue active) 5. postfix/local[864857]: D694B20DD5B: to=, relay=local, delay=98, status=sent (mailbox) 6. postfix/smtpd[866062]: disconnect from mail.ora.com[10.143.23.45]
Once connected, the client sends a HELO command with an identifying hostname. The hostname provided can be used to accept or reject a message using smtpd_helo_restrictions.
In the next step, the client issues a MAIL FROM command to indicate the sender's email address, followed by a RCPT TO command to indicate the recipient's email address.
If everything is acceptable up to the point of the DATA command, the client is permitted to send the contents of the message, which consist of message headers followed by the message body. Postfix provides another opportunity to reject the message based on its contents (see Section 11.9 later in this chapter). If the final header and body checks are acceptable, the message is delivered.
Postfix indicates to the client that it has rejected a message by sending reply codes. Standard reply codes are described in Chapter 2. In this chapter, we consider codes in the 4xx and 5xx range. More information appears in a sidebar later in this chapter.
11.7.2 Listing Restrictions
When you assign restrictions to Postfix UBE rules, it is not necessary to use all of the rules. You can define restrictions for the ones you need and leave out the others. The default setting if no rules are set in main.cf looks like the following:
smtpd_client_restrictions = smtpd_helo_restrictions = smtpd_sender_restrictions = smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
This prevents your system from being an open relay by allowing any computer on your network to relay while rejecting all others unless they are sending messages destined for one of your users.
There are many restrictions available. Table 11-1 lists each one along with the client information it operates on. One important concept that confuses many people at first is that any of these restrictions can be used in any rule. While it may seem logical that check_helo_access should be assigned to smtpd_helo_restrictions, it could equally be assigned to smptd_sender_restrictions or any of the others. This gives you a lot of flexibility in ordering your restrictions when deciding what to accept and what to block.
Restrictions |
Client-supplied information |
---|---|
check_client_access maptype:mapname |
Client IP address or hostname |
reject_rbl_client |
|
reject_rhsbl_client |
|
reject_unknown_client |
|
check_helo_access maptype:mapname |
HELO hostname |
permit_naked_ip_address |
|
reject_invalid_hostname |
|
reject_non_fqdn_hostname |
|
reject_unknown_hostname |
|
check_sender_access maptype:mapname |
MAIL FROM address |
reject_non_fqdn_sender |
|
reject_rhsbl_sender |
|
reject_unknown_sender_domain |
|
check_recipient_access maptype:mapname |
RCPT TO address |
permit_auth_destination |
|
permit_mx_backup |
|
reject_non_fqdn_recipient |
|
reject_unauth_destination |
|
reject_unknown_recipient_domain |
|
reject_unauth_pipelining |
DATA command |
You'll notice from Table 11-1 that some rules take an argument of the form maptype:mapname. The mapname refers to a normal Postfix lookup table whose lefthand key is matched against the piece of client information, and the righthand value is the action to perform. Access maps are discussed in Restriction Definitions following.
11.7.2.1 How restrictions work
Each of the nonaccess map restrictions evaluates to or returns one of three possible values that determine what action Postfix takes with the message: OK, REJECT, and DUNNO. (Access maps can also return the same values, but they allow additional actions as well.) The restrictions are evaluated in the order you list them. During processing, if a rule returns an explicit REJECT, the message is immediately rejected. If a rule returns an explicit OK, the processing stops for that parameter but continues on to the next until all of the assigned rules have been evaluated or Postfix encounters a rejection. It's important to note that a rule might explicitly accept a message, but it can still be rejected by another rule's restrictions. If the set of rules comes to no definite conclusion (all DUNNOs), the default action is to accept the message. Any single parameter can reject a message, but all of them must accept it in order for it not to be rejected. There are generic restrictions such as permit and reject that return explicit OK or REJECT values without considering any of the client information.
When a rule evaluates to REJECT, by default Postfix does not actually reject the message until after the client has sent the RCPT TO command. Even though it may know at the HELO command that it's going to reject this client, it waits until after it receives the RCPT TO command before returning the reject code. The reason for this default is that some SMTP clients do not check that they have been rejected during the transaction and continue trying to deliver the message. In such a case, you end up with connections that last longer than they should and several warning messages in your log file. Another advantage to the default is that you get more complete information in your log. If you want to change the default to have a rejection take effect as soon as possible, set the parameter smtpd_delay_reject in main.cf:
smtpd_delay_reject = no
You might want to do this in a controlled environment where you know all of the connecting SMTP clients are well-behaved; otherwise, the default makes sense for most situations.
11.7.2.2 Testing new restrictions
A useful parameter for testing new restrictions is soft_bounce:
soft_bounce = yes
When it is set, hard reject responses (5xx) are converted to soft reject responses (4xx). When you add a new restriction that you're not sure about, you might want to turn soft_bounce on and then watch your logs for what's rejected so that you can fine-tune your settings by the time another delivery attempt is made.
Another useful option for testing restrictions is the warn_if_reject qualifier. Simply precede any restriction with it to have that restriction log a warning instead of rejecting a message. If you're not sure what effect a new restriction will have in your environment, you can try it out with warn_if_reject, and then implement it completely only if it works as you expect:
smtpd_recipient_restrictions = permit_mynetworks reject_unauth_destination warn_if_reject reject_invalid_hostname reject_unknown_recipient_domain reject_non_fqdn_recipient
In this example, if a client uses an invalid HELO hostname when delivering a message, Postfix logs a warning but still delivers the message (assuming it's not blocked for other reasons).
11.7.2.3 A simple example
Before moving on to the restriction definitions, let's consider a simple example:
smtpd_recipient_restrictions = permit_mynetworks reject_unauth_destination reject_invalid_hostname reject_unknown_sender_domain
This example expands on the default configuration with two additional restrictions. When a client connects, if it's from your own network, permit_mynetworks returns OK, so it is allowed to send mail. The other restrictions are not checked. If the client is from outside your network, permit_mynetworks does not return OK and does not return REJECT, so it returns DUNNO. Postfix then checks reject_unauth_destination.
If the message is not addressed to somebody at one of your destination domains, it returns REJECT; otherwise, it returns DUNNO. Assuming it returns DUNNO, Postfix then checks reject_invalid_hostname, which says to return REJECT if the hostname supplied with the HELO command is not valid. Otherwise, it returns DUNNO. Finally, Postfix checks reject_unknown_sender_domain, which returns REJECT if the domain name of the address supplied with the MAIL FROM command does not have a valid DNS entry. If none of the restrictions has rejected the message, Postfix accepts it for delivery.
11.7.3 Restriction Definitions
There are six types of restrictions introduced below. Each of the restrictions are defined in the sections that follow.
Access maps for client checking
Restrictions of the form check_*_access point to lookup tables that might list IP addresses, hostnames, or email addresses (depending on the parameter) that should be accepted or rejected by Postfix.
Other client checks
Other client restrictions compare the client information to general configuration information instead of access tables. An example is permit_mynetworks, which you saw earlier.
Strict syntax checking
Some restrictions tell Postfix to enforce SMTP standards very strictly. Since spammers often misconfigure or use poorly implemented software, you can stop a lot of spam by making sure that connecting clients follow the rules.
DNS checking
DNS-checking rules ensure that DNS information is correct. Spammers often work from networks that do not configure DNS correctly. Unfortunately, rules of this type are appropriate only for a very aggressive anti-spam stance because of the number of legitimate sites that also do not configure their DNS correctly.
Real-time blacklist checking
Real-time blacklists are services listing suspected spamming clients. Postfix can check with real-time blacklist services and reject clients based on their listing.
Generic
Generic rules explicitly reject or accept a message. They usually specify your default stance if a message isn't explicitly accepted or rejected elsewhere. Since these rules will always accept or reject a message, they should come last in your list of rules.
11.7.3.1 Access maps
Restrictions in the client-checking category all point to access map files. Access maps are simply a type of Postfix lookup table (see Chapter 4 for more information about lookup tables). In the lookup table, you specify the client information as a key and the action to take (accept or reject) as the value:
check_client_access maptype:mapname
The check_client_access restriction points to an access table containing entries with IP addresses, network addresses, hostnames, and parent domains to match against the client IP address. (Postfix performs a reverse lookup on the IP address to obtain a hostname to compare host and parent domain name information.) Each entry includes an action to take when the IP address matches a key.
check_helo_access maptype:mapname
The check_helo_access restriction points to an access table containing hostnames and parent domains to match against the host information supplied with the HELO command. Each entry includes an action to take when the supplied host information matches a key.
check_recipient_access maptype:mapname
The check_recipient_access restriction points to an access table containing entries with email addresses, domains, and local parts to match against the address specified with the RCPT TO command. Each entry includes an action to take when the supplied address matches a key.
check_sender_access maptype:mapname
The check_sender_access restriction points to an access table containing entries with email addresses, domains, and local parts to match against the address specified with the MAIL FROM command. Each entry includes an action to take when the supplied address matches a key.
The restrictions check_sender_access and check_recipient_access both check a supplied email address. For them, the key in your index file can be an email address (user@example.com) to match a specific address, a domain name (example.com) to match the domain name portion or subdomains of the address, or the local part of an email address (user@) to match all addresses using the specified local part.
The rules check_client_access and check_helo_access compare the key to a supplied hostname or IP address. The index file pattern can be a hostname, an IP address (192.168.143.23), or a network address specified by the initial octets of the address (10 or 10.12 or 10.12.154).
Actions can be indicated as follows:
OK
Accept the item. Processing for the current rule stops. Postfix moves on to the next restriction rule.
REJECT
Reject the item. You can optionally specify a short string of text to be used in the reply and with logging for this message; otherwise, Postfix uses the general reply code and text configured for the restriction. The parameter access_map_reject_code contains the default reply code for the check_*_access rules and maps_rbl_reject_code contains the default reply code for reject_maps_rbl. If you don't specify a value, they both default to 554.
DUNNO
Stop checking entries for the lookup table. Postfix moves on to the next restriction for the current rule.
FILTER
Redirect the message to a content filter. You must specify a transport and next hop as you would in a transport table.
HOLD
Place the message in the hold queue. You can optionally specify a short string of text to be logged; otherwise, Postfix logs a generic message.
DISCARD
Report a successful delivery to the client, but drop the message. You can optionally specify a short string of text to be logged; otherwise, Postfix logs a generic message. Don't use this action unless you have carefully considered the ramifications. Silently dropping messages runs counter to the expected behavior of email systems. When dealing with spam, dropping messages might be the best course of action, but discarding any legitimate mail can affect the overall perceived reliability of Internet email.
4xx message text
Reject the message. The response sent to the client is the numerical code you specify. A response in the 4xx range tells the client there is a temporary problem; queue the message and try delivery later. (See sidebar.)
5xx message text
Reject the message. The response sent to the client is the numerical code you specify. A response in the 5xx range tells the client there is a permanent problem; send a bounce notification to the original sender. (See sidebar.)
You can also set up regular expression tables for access maps. In most cases, it probably doesn't make sense to use a regular expression table for your access lists. Postfix already breaks up email addresses, domains, and IP addresses into the individual pieces to make its comparisons, so you really don't gain much through regular expressions here. On the other hand, regular expression tables work very well for header and body checks, which are discussed later in this chapter.
Let's expand the configuration example with some access maps:
smtpd_client_restrictions = check_client_access hash:/etc/postfix/client_access smtpd_sender_restrictions = check_sender_access hash:/etc/postfix/sender_access smtpd_recipient_restrictions = permit_mynetworks reject_unauth_destination reject_invalid_hostname reject_unknown_sender
We've now added restrictions to consult the lookup tables client_access and sender_access.
The client_access file can have entries like the following:
10.157 REJECT 192.168.76.23 REJECT currentmail.com REJECT
and the sender_access file can have entries like the following:
hardsell@example.com REJECT marketing@ REJECT specials.digital-letter.com REJECT
11.7.3.2 Other client-checking restrictions
The following client restrictions make their decisions by comparing client-supplied information to the local Postfix configuration. The default rules fall under this category.
permit_auth_destination
Permits a request if the resolved destination address matches a hostname or subdomain where the Postfix system is the final destination for the message or a relay for the final destination. Final destinations are listed in mydestination, inet_interfaces, virtual_alias_maps, or virtual_mailbox_maps, and relays are listed in relay_domains. Furthermore, the address must not contain any sender-specified routing (e.g., user@example.com@example.net). If permit_auth_destination does not find a match, it returns DUNNO rather than REJECT. Postfix continues to check all subsequent restriction rules.
permit_mynetworks
Allows a request if the client IP address matches any of the addresses listed in the mynetworks parameter. You normally use this restriction to exclude local clients from other UBE restrictions and to allow them to relay through your SMTP server.
reject_unauth_destination
Rejects a request if the Postfix system is not the final resolved destination email address or a relay for the final destination. Final destinations are listed in mydestination, inet_interfaces, virtual_alias_maps, or virtual_mailbox_maps, and relays are listed in relay_domains. Addresses must not contain any sender-specified routing (e.g., user@example.com@example.net). The relay_domains_reject_code parameter specifies the response code for rejected requests. The default is 554.
11.7.3.3 Strict syntax restrictions
Restrictions in the strict syntax category check for misconfigured clients and reject mail when they don't comply with the standards. These rules can detect a lot of spam, but they might also reject legitimate clients. You should study the nature of your spam and real messages to see which rules will benefit you most without rejecting real messages. You can use access maps with OK actions to whitelist known senders that would otherwise be rejected.
reject_invalid_hostname
Rejects a request if the hostname supplied with the HELO command is not a valid hostname. The invalid_hostname_reject_code parameter specifies the response code for rejected requests. The default is 501. Most legitimate senders use valid hostnames.
reject_non_fqdn_hostname
Rejects a request if the hostname supplied with the HELO command is not in the fully qualified form, as required by the RFC. The non_fqdn_reject_code parameter specifies the response code for rejected requests. The default is 504.
reject_non_fqdn_recipient
Rejects a request if the address supplied with the RCPT TO command is not in the fully qualified form, as required by the RFC. The non_fqdn_reject_code parameter specifies the response code for rejected requests. The default is 504. Most legitimate senders use fully qualified domain names.
reject_non_fqdn_sender
Rejects a request if the address supplied with the MAIL FROM command is not in the fully qualified form, as required by the RFC. The non_fqdn_reject_code parameter specifies the response code for rejected requests. The default is 504.
reject_unauth_pipelining
Pipelining is a technique supported by Postfix to speed up bulk mail deliveries by sending multiple SMTP commands at once. The protocol requires that clients first check that the server supports pipelining. Some clients incorrectly begin pipelining before they confirm that Postfix actually supports it. The rule reject_unauth_pipelining immediately rejects such requests. There is no more processing, and the message is rejected.
11.7.3.4 DNS restrictions
The DNS checking rules make sure that clients and email envelope addresses are sent from domains that have valid DNS information. It would be a great improvement to email in general if postmasters could always require valid DNS information because it would be harder for spammers to hide. Unfortunately, there are too many legitimate domains that do not configure their DNS correctly for such strictness to be practical. You should study the nature of your spam and real messages to see which will benefit you most without rejecting false-positives. You can use access maps with OK actions to whitelist known senders that would otherwise be rejected.
reject_unknown_client
Rejects a request if the client IP address has no DNS PTR record or if a follow-up lookup on the hostname listed in the PTR record does not match the connecting IP address. The unknown_client_reject_code parameter specifies the response code for rejected requests. The default is 450. If you change the default, the reply code you specify is returned except when there is a temporary DNS error. In this case, your change is overridden and Postfix returns 450. This rule tends to find many false-positives for spam because it seems to be very common to have PTR records misconfigured or not configured at all.
reject_unknown_hostname
Rejects a request if the hostname supplied with the HELO command doesn't have either a DNS A or MX record. The unknown_hostname_reject_code parameter specifies the response code for rejected requests. The default is 450. If you change the default, the reply code you specify is returned except when there is a temporary DNS error. In this case, your change is overridden and Postfix returns 450. Many clients do not use a fully qualified hostname and would be rejected by this restriction.
reject_unknown_recipient_domain
Rejects a request if the domain name of the address supplied with the RCPT TO command doesn't have either a DNS A or an MX record. The unknown_address_reject_code parameter specifies the response code for rejected requests. The default is 450. If you change the default, the reply code you specify is returned except when there is a temporary DNS error. In this case, your change is overridden and Postfix returns 450.
reject_unknown_sender_domain
Rejects a request if the domain name of the address supplied with the MAIL FROM command has neither an A nor an MX record in DNS. The unknown_address_reject_code parameter specifies the response code for rejected requests. The default is 450. If you change the default, the reply code you specify is returned except when there is a temporary DNS error. In this case, your change is overridden and Postfix returns 450.
Since the MAIL FROM address is the address that bounce notifications must be sent to, it makes sense to require a known domain name. It is highly recommended that you include this rule in your restrictions.
11.7.3.5 Real-time blacklists
Restrictions for real-time blacklists cause Postfix to perform DNS lookups using client information with domains you specify to determine if a client is listed with one of the DNSBL services:
reject_rbl_client domain name
Rejects a request if a DNS lookup of a hostname composed of the octets of the client IP address in reverse in the specified domain lists an A record.
reject_rhsbl_client domain name
Rejects a request if the client hostname has an A record under the specified domain.
reject_rhsbl_sender domain name
Rejects a request if the domain of the sender address has an A record under the specified domain.
11.7.3.6 Generic restrictions
There are two generic restriction rules that explicitly accept or reject a message:
permit
Immediately permits a message. Processing for the current restriction parameter stops, but Postfix continues checking the other restriction parameters.
reject
Immediately rejects a request. There is no more processing, and the message is rejected.
11.7.4 Tracing a Restriction List
With what we know so far, let's trace what happens with some simple HELO restrictions. Consider that smtpd_helo_restrictions is assigned the following rules:
smtpd_helo_restrictions = check_helo_access hash:/etc/postfix/helo_access reject_invalid_hostname
and helo_access contains the following entries:
greatdeals.example.com REJECT oreillynet.com OK
Let's follow four different scenarios when clients connect with different HELO commands:
HELO example
Postfix first encounters the check_helo_access rule pointing to the helo_access lookup table. In checking the lookup table, it does not find the specified hostname example, so it moves on to the reject_invalid_hostname rule. Since example is not a complete hostname as required by the standard, Postfix rejects the message.
HELO greatdeals.example.com
Postfix first encounters the check_helo_access rule pointing to the helo_access lookup table. In checking the lookup table, it finds an entry for greatdeals.example.com with an action of REJECT. Postfix, therefore, rejects the message.
HELO oreillynet.com
Postfix first encounters the check_helo_access rule pointing to the helo_access lookup table. In checking the lookup table, it finds an entry for oreillynet.com with an action of OK. Postfix stops processing for the smtpd_helo_restrictions parameter without considering any of the other restrictions and moves on to smtpd_sender_restrictions if specified.
HELO mail.ora.com
Postfix first encounters the check_helo_access rule pointing to the helo_access lookup table. In checking the lookup table, it does not find the specified host mail.ora.com, so it moves on to the reject_invalid_hostname rule. Since mail.ora.com conforms to the format required by the standard, Postfix continues to the smtpd_sender_restrictions if specified.
Introduction
Prerequisites
Postfix Architecture
General Configuration and Administration
Queue Management
Email and DNS
Local Delivery and POP/IMAP
Hosting Multiple Domains
Mail Relaying
Mailing Lists
Blocking Unsolicited Bulk Email
SASL Authentication
Transport Layer Security
Content Filtering
External Databases
Appendix A. Configuration Parameters
Appendix B. Postfix Commands
Appendix C. Compiling and Installing Postfix
Appendix D. Frequently Asked Questions