Postfix Per-Client/User/etc. Access Control


Postfix restriction classes

The Postfix SMTP server supports access restrictions such as reject_rbl_client or reject_unknown_client_hostname on the right-hand side of SMTP server access(5) tables. This allows you to implement different junk mail restrictions for different clients or users.

Having to specify lists of access restrictions for every recipient becomes tedious quickly. Postfix restriction classes allow you to give easy-to-remember names to groups of UCE restrictions (such as "permissive", "restrictive", and so on).

The real reason for the existence of Postfix restriction classes is more mundane: you can't specify a lookup table on the right-hand side of a Postfix access table. This is because Postfix needs to open lookup tables ahead of time, but the reader probably does not care about these low-level details.

Example:

/etc/postfix/main.cf:
    smtpd_restriction_classes = restrictive, permissive
    # With Postfix < 2.3 specify reject_unknown_client.
    restrictive = reject_unknown_sender_domain reject_unknown_client_hostname ...
    permissive = permit

    smtpd_recipient_restrictions = 
        # permit_mynetworks and reject_unauth_destination are not
        # needed here if the mail relay policy is specified with
        # smtpd_relay_restrictions (available with Postfix 2.10 and
        # later).
        permit_mynetworks
        reject_unauth_destination
        check_recipient_access hash:/etc/postfix/recipient_access
        ...

/etc/postfix/recipient_access:
    joe@my.domain       permissive
    jane@my.domain      restrictive

With this in place, you can use "restrictive" or "permissive" on the right-hand side of your per-client, helo, sender, or recipient SMTPD access tables.

The remainder of this document gives examples of how Postfix access restriction classes can be used to:

These questions come up frequently, and the examples hopefully make clear that Postfix restriction classes aren't really the right solution. They should be used for what they were designed to do, different junk mail restrictions for different clients or users.

Protecting internal email distribution lists

We want to implement an internal email distribution list. Something like all@our.domain.com, which aliases to all employees. My first thought was to use the aliases map, but that would lead to "all" being accessible from the "outside", and this is not desired... :-)

Postfix can implement per-address access controls. What follows is based on the SMTP client IP address, and therefore is subject to IP spoofing.

/etc/postfix/main.cf:
    smtpd_recipient_restrictions =
        ...
        check_recipient_access hash:/etc/postfix/access
        ...the usual stuff...

/etc/postfix/access:
    all@my.domain   permit_mynetworks,reject
    all@my.hostname permit_mynetworks,reject

Specify dbm instead of hash if your system uses dbm files instead of db files. To find out what map types Postfix supports, use the command postconf -m.

Now, that would be sufficient when your machine receives all Internet mail directly from the Internet. That's unlikely if your network is a bit larger than an office. For example, your backup MX hosts would "launder" the client IP address of mail from the outside so it would appear to come from a trusted machine.

In the general case you need two lookup tables: one table that lists destinations that need to be protected, and one table that lists domains that are allowed to send to the protected destinations.

What follows is based on the sender SMTP envelope address, and therefore is subject to SMTP sender spoofing.

/etc/postfix/main.cf:
    smtpd_recipient_restrictions =
        ...
        check_recipient_access hash:/etc/postfix/protected_destinations
        ...the usual stuff...

    smtpd_restriction_classes = insiders_only
    insiders_only = check_sender_access hash:/etc/postfix/insiders, reject

/etc/postfix/protected_destinations:
    all@my.domain   insiders_only
    all@my.hostname insiders_only

/etc/postfix/insiders:
    my.domain       OK  matches my.domain and subdomains
    another.domain  OK  matches another.domain and subdomains

Getting past this scheme is relatively easy, because all one has to do is to spoof the SMTP sender address.

If the internal list is a low-volume one, perhaps it makes more sense to make it moderated.

Restricting what users can send mail to off-site destinations

How can I configure Postfix in a way that some users can send mail to the internet and other users not. The users with no access should receive a generic bounce message. Please don't discuss whether such access restrictions are necessary, it was not my decision.

Postfix has support for per-user restrictions. The restrictions are implemented by the SMTP server. Thus, users that violate the policy have their mail rejected by the SMTP server. Like this:

554 <user@remote>: Access denied

The implementation uses two lookup tables. One table defines what users are restricted in where they can send mail, and the other table defines what destinations are local. It is left as an exercise for the reader to change this into a scheme where only some users have permission to send mail to off-site destinations, and where most users are restricted.

The example assumes DB/DBM files, but this could also be done with LDAP or SQL.

/etc/postfix/main.cf:
    smtpd_recipient_restrictions =
        ...
        check_sender_access hash:/etc/postfix/restricted_senders
        ...other stuff...

    smtpd_restriction_classes = local_only
    local_only = 
        check_recipient_access hash:/etc/postfix/local_domains, reject

/etc/postfix/restricted_senders:
    foo@domain      local_only
    bar@domain      local_only

/etc/postfix/local_domains:
    this.domain     OK      matches this.domain and subdomains
    that.domain     OK      matches that.domain and subdomains

Specify dbm instead of hash if your system uses dbm files instead of db files. To find out what map types Postfix supports, use the command postconf -m.

Note: this scheme does not authenticate the user, and therefore it can be bypassed in several ways: