Postfix SMTP client whitelisting

The Problem

I run a Postfix + Dovecot email server for my personal email. A common Postfix setup includes reject_unknown_reverse_client_hostname for SMTP clients to reduce invalid connections attempts, such as botnet spammers attempting to find open mail servers. Typical residential ISP connections have PTR entries for their IPs, whereas many botnet devices may not. It’s not foolproof nor even necessarily particularly effective, but it helps cut down on invalid SMTP connection attempts.

This became a problem for me when my new metro wireless ISP assigned me an IP that has no PTR record, and has been slow addressing the issue. Thus, my mail server rejects my own local email client’s SMTP connections!

Simple Solution

A typical Postfix configuration file1 has SMTP restrictions like this:

smtpd_sender_restrictions =
        reject_unknown_sender_domain,
        reject_unknown_reverse_client_hostname
smtpd_client_restrictions =
        warn_if_reject,
        reject_unknown_reverse_client_hostname

The simplest way to allow a real client to send regardless of their PTR record is by adding permit_sasl_authenticated to the top of the restrictions, assuming smtpd_delay_reject is the default ‘yes’2. The new configuration is simply:

smtpd_sender_restrictions =
        permit_sasl_authenticated,
        reject_unknown_sender_domain,
        reject_unknown_reverse_client_hostname
smtpd_client_restrictions =
        permit_sasl_authenticated,
        warn_if_reject,
        reject_unknown_reverse_client_hostname

This permits any authenticated clients to send email, regardless. Don’t forget to sudo service postfix restart after making changes to the configuration file.

Complex Solution

A more complex solution, also necessary if smtpd_delay_reject = no, is to use a client whitelist lookup. I use a MariaDB3 database for all of my email configuration4, so this whitelist will involve reading allowed clients from a database. It uses check_client_access in lieu of the above permit_sasl_authenticated to whitelist a client’s IP.

First, create a SQL table for listing allowed clients – actions are restricted to a few of Postfix’s access ‘action’ codes:

CREATE TABLE `client_whitelist` (
  `UID` int(10) UNSIGNED NOT NULL,
  `Client` varchar(120) NOT NULL COMMENT 'Single IP',
  `Action` enum('OK','REJECT','DUNNO','WARN') NOT NULL DEFAULT 'REJECT'
) ENGINE=InnoDB DEFAULT COMMENT='SMTP clients that match here bypass sender restrictions';

Then create a configuration file to use with Postfix’s MySQL interface; I used /etc/postfix/mysql-smtp_whitelist.cf:

hosts = <DB host>
user = <DB user>
password = <DB password>
dbname = <DB name>

query = SELECT action FROM client_whitelist WHERE '%s' = client

This simply returns one of the enumerated actions from the table if the client matches. Unfortunately the Client field can only contain IPs, not hostnames: Postfix is only expecting a simple action to be returned, only knows the client IP at this point, and MySQL doesn’t have a built-in hostname resolution function.

The Postfix configuration to execute this query against SMTP clients is simply:

check_client_access proxy:mysql:/etc/postfix/mysql-smtp_whitelist.cf

This uses proxymap to maintain a persistent database connection to the mysql handler that is used for check_client_access. proxy is not required, but reduces database traffic. However, if you do use proxy, you must also add $smtpd_sender_restrictions $smtd_client_restrictions to proxy_read_maps to allow it access!

To test the database configuration file and table contents, try:

sudo postmap -q <your IP> mysql:/etc/postfix/mysql-smtp_whitelist.cf

If this returns OK, then it’s ready to be placed into the Postfix master configuration file:

smtpd_sender_restrictions =
        check_client_access proxy:mysql:/etc/postfix/mysql-smtp_whitelist.cf,
        reject_unknown_sender_domain,
        reject_unknown_reverse_client_hostname
smtpd_client_restrictions =
        check_client_access proxy:mysql:/etc/postfix/mysql-smtp_whitelist.cf,
        warn_if_reject,
        reject_unknown_reverse_client_hostname
...
proxy_read_maps = ... $smtpd_sender_restrictions $smtd_client_restrictions

Restart Postfix via sudo service postfix restart and check the status – clients listed in the client_whitelist table should now be able to send via SMTP regardless of other restrictions.

Footnotes

  1. Usually /etc/postfix/main.cf
  2. This default means that the client and sender restrictions aren’t actually evaluated until a client attempts to send an email, which helps with some noncompliant email clients
  3. Binary-compatible drop-in replacement for MySQL
  4. A future post may possibly cover this

Leave a Reply

Your email address will not be published. Required fields are marked *