Understanding The Order In Which Restrictions Are Applied There are three parts to restrictions: restriction "stages" restrictions access lists (or maps) Postfix' restriction stages are as follows, and are processed in the following order: smtpd_client_restrictions smtpd_helo_restrictions smtpd_sender_restrictions smtpd_recipient_restrictions smtpd_data_restrictions regardless of the order in which they're listed in main.cf. Processing *within* a restriction stage ends on the first match, with the exception of a "DUNNO" result. What means "DUNNO?" "DUNNO" means "I don't know, somebody else decide." DUNNO is covered in more detail, later. Each restriction stage must evaluate to "OK" or "DUNNO" for processing to continue with the next stage. The default value of the smtpd_recipient_restrictions stage is the result of "permit_mynetworks, reject_unauth_destination". The default for every other stage is empty--the default action for which is "DUNNO". Individual restrictions (within a restriction stage) are evaluated in the order in which they're listed. Items in an access list are are matched depending on the type of access list. Regular expression tables, such as pcre and regexp, are checked in the order in which entries are listed. Indexed table map types such as hash, dbm, btree, etc., use the value being checked as an index key. Here are some simple (and *not* fully-functional!) examples to demonstrate the principles described above: Notes: We'll assume /etc/postfix is Postfix' configuration directory. "mumble:" refers to an indexed map type, such as "hash:," "dbm:," etc. Suppose you have client_checks and sender_checks access maps that look like this: /etc/postfix/client_checks: # Block most clients in 10.* 10 REJECT # But not 10.1.2.3, specifically 10.1.2.3 DUNNO # We specifically white-list 172.16.4.5 - maybe! 172.16.4.5 OK # We specifically black-list 192.168.6.7 192.168.6.7 REJECT /etc/postfix/sender_checks: joe@example.com OK bob@example.com REJECT If placed in separate restriction stages: /etc/postfix/main.cf (partial): ... smtpd_client_restrictions = , check_client_access mumble:/etc/postfix/client_checks, , etc... smtpd_sender_restrictions = , check_sender_access mumble:/etc/postfix/sender_checks, , etc... ... A client address of 192.168.6.7 will result in a REJECT, and that's it. If Joe's email was coming from that address, he's out of luck. Processing will never reach smtpd_sender_restrictions. A client address of 172.16.4.5 will stop further processing of additional client checks - so "" will not be processed. But since that restriction stage evaluated to "OK," smtpd_sender_restrictions will still be processed. A sender of bob@example.com will result in a REJECT. If instead you were to do something like this: /etc/postfix/main.cf (partial): ... smtpd_sender_restrictions = , check_client_access mumble:/etc/postfix/client_checks, check_sender_access mumble:/etc/postfix/sender_checks, , etc... ... The "OK" of 172.16.4.5 would stop further processing of the sender restrictions stage, and good ol' Bob would get through. What do you mean, "DUNNO?" It was noted earlier that a match stops further processing of an access list, and of the restriction stage that "called" it. But what does a restriction return when there's no match? Well, it returns "DUNNO." ("I don't know, somebody else decide.") It can be handy to explicitly specify DUNNO in certain circumstances, such as to suppress further lookups in an access list, but where you don't necessarily want to "OK" something. Postfix lets you do this. In the examples above, a client address of 10.1.2.3 would halt further checks in the client_checks access map, just as if an OK or REJECT were specified, but processing would resume with the next restriction check in that stage. How might this come in handy? Well, let's say: You don't want to accept connections from 10.* IPs Except for example.com, at 10.1.2.3, which you do want But you don't want to hear from Bob. Using the second example of smtpd_sender_restrictions, above: Everything but 10.1.2.3 will result in a REJECT because of the first two client_checks entries. Since 10.1.2.3 resulted in a DUNNO, rather than an OK, processing will resume with the sender checks. Bob will get REJECTed by the entry in the sender checks file. Finally, remember that unless you have set smtpd_delay_reject = no (default is "yes"), no actual rejecting takes place until after RCPT TO in the SMTP exchange. In fact... You can put all restrictions under one restriction class? Yes. As Liviu Daia notes: Any of the smtpd restrictions may contain checks referring to a preceding SMTP stage (check_sender_access may appear [for example] in smtpd_recipient_restrictions), with the exception of smtpd_data_restrictions, which, as a rule of thumb, should not refer to restrictions specific to recipient checks. Victor Duchovni added: With "smtpd_delay_reject = yes" (that is by default), *all* the built-in restriction lists other than "smtpd_data_restrictions" are evaluated for every recipient. So if possible expensive checks on the client ip or name, HELO name, or sender address that are independent of the recipient address should be moved to the "data" restrictions. In this scenario smtpd_recipient_restrictions can be used for just relay control! Note that results of RBL lookups are cached, so these are cheap to evaluate multiple times. So, for performance reasons, one might be tempted to put all restrictions in smtpd_data_restrictions. But note also this comment by Victor Duchovni: Multi-recipient messages do not have a "distinguished" recipient. So all restrictions that look at recipient addresses evaluate to "DUNNO" when used in the data restrictions. This is why they are not generally useful in that context. Acknowledgments This section was created from contributions by Noel Jones, Liviu Daia and Victor Duchovni in the postfix-users mailing list.