Example Postfix MX style configuration

Summary^

This is an example configuration of an MX style mailserver (eg Backup MX or upstream policy MX). I am no postfix guru, but i hope i have learned some lessons over the time. The configurations should be seen as a guidelines, not as strict rules. Of course i will not provide and guaranties nor warranties, see my decency disclaimer for that.

It would fit after customization for:

  • An external backup MX which implements policies, queues mail and then delivers to your MTA which delivers to mailboxes.
  • An upstream MX which implements policies and then delivers to your MTA which delivers to mailboxes.
  • You use only relaying, no local (linux) user.

It does not fit for:

  • A MTA for mail clients which want to authenticate and send mails.
  • A MTA which does deliver to a mailbox.

The config^

It is to be read from up to down. You could copy, customize and paste it as your main.cf or use the download link (soon) at the end for a fully documented version of this. I tried to “topic” the settings for easier reading and understanding their purposes.

Header^

#
# MX Server
#   CHANGELOG:
#   * uk, 2010-05-18: initial setup
#

This should be in the head of every main.cf ;) a log of what, who and when.

#
# DEBUGGING {
#

#debug_peer_list =
#debug_peer_level = 2
#smtp_tls_loglevel = 3

#
# DEBUGGING }
#

This is the debugging part. Comes in handy if you have to see something in detail and don’t want to look up any configuration parameters when time is of the essence. Here i also introduce the curly brace notation i use. It is very useful if you use a text editor as VIM or a Desktop editor as gedit, jedit, whatever which supports folding and/or “find closing brace”. You can “jump” very fast through the whole file.

#
# CUSTOM VARIABLES {
#

#
# CUSTOM VARIABLES }
#

This block you can leave out for this config, but i keep it in every main.cf, you might never know what comes .. it is meant to store custom variables which you then can use in your master.cf or within the main.cf itself.

#
# PROXY FOR CACHING LDAP {
#

proxy_read_maps =
    proxy:unix:passwd.byname
    proxy:hash:/etc/aliases
    proxy:hash:/etc/postfix/hash/addresses
    proxy:hash:/etc/postfix/hash/domains
    proxy:hash:/etc/postfix/hash/backupserver-fingerprints
    proxy:hash:/etc/postfix/hash/whitelist-ips
    proxy:hash:/etc/postfix/hash/whitelist-domains
    #proxy:hash:/etc/postfix/hash/relay-transports

#
# PROXY FOR CACHING LDAP }
#

The first “real config” block. It should list all your used database (especially mysql, proxy and so on). Here we declare proxy maps (aka caches) for all the lookup databases we use. This allows postfix to store those results locally in the memory and cache files (bdbs). For external sources such as mysql or ldap this will reduce the network impact a lot, if you already use BDBs (such as btree or hash tables), there is no benefit in doing so (id do use this only for example, it is redundant).

General^

#
# POSTFIX GENERAL {
#

recipient_delimiter = +
smtpd_delay_reject = yes
smtpd_banner = Hello and welcome to our mailserver
biff = no
parent_domain_matches_subdomains =
    debug_peer_list

#
# POSTFIX GENERAL }
#

The general section contains all general and miscelangelous parameters of postfix.

  • recipient_delimiter
    Enables postfix to resolve recipients as “user+something@domain.tld”, if the user “user@domain.tld” really exists. Your user will be able to use indefinite recipient addresses. A common scenario is to use always a +-Email for newsletter subscriptions and so on, which let you tracks the “source” of the mail very easily – and also helps writing mailbox filters. Totally optional. Ah yes, you could use another char as delimiter, but be careful ..
  • smtpd_delay_reject
    This forces postfix to receive the whole SMTP commands until DATA, which allows you to use any smtpd_*_restrictions as if you were in smtpd_recipient_restrictions. Means: you have the recipient (RCPT TO) already in smtpd_client_restrictions, where you “normally” wouldn’t (there would be only the client_address and some other stuff). This is necessary if you want to use decency anywhere before smtpd_recipient_restrictions.
  • smtpd_banner
    Your welcome message for SMTP clients.. write something nice ;)
  • biff
    An “old” unix/linux mail service, which you probably don’t use (if you do, you would know)
  • parent_domain_matches_subdomains
    Makes your domain entries in databases matches all subdomains, also. Eg if you have “domain.tld” in relay_domains, and put relay_domains in this parameter, then everything to “something.domain.tld” will be relayed (hit by this entry), too. I prefer explicit entries.

Network^

#
# OUR NETWORK {
#

append_dot_mydomain = no
myhostname = my-reverse-dns.domain.tld
mydomain = $myhostname
myorigin = $mydomain
mydestination = $myhostname
mynetworks = 127.0.0.0/8
mynetworks_style = host
inet_interfaces = all
inet_protocols = all

#
# OUR NETWORK }
#

This part contains all network settings

  • append_dot_mydomain
    If enabled (“yes”), you $mydomain will be appended to any mail which are in the format user@host, whereas host is no fully qualified domain name more..
  • myhostname
    Shall be set to the reverse hostname (RDNS) of your mail server.
  • mydomain
    Defaults to $myhostname, but can be set otherwise. Eg, if your RDNS is mail-123.mycompany.tld and you send mails only from mycompany.tld, use the latter.
  • myorigin
    Should be set to $mydomain, normally. more..
  • mydestination
    In a virtual domain environment, that probably should be set to $myhostname, cause this enables you to reveid mails for *@$myhostname (which you should).
  • mynetworks
    Used in the permit_mynetworks directives. Probably you want to set this to 127.0.0.0/8 which enables you to send mails from localhost (eg via content filter re-inject). Use it with care.
  • mynetworks_style
    host = trust only local machine (ourself), subnet = trust anybody in the subnet (ifconfig), class = don’t use this. Normally, host would be a good choice. more..
  • inet_interfaces
    On what inet interfaces to listen. Eg “eth0″ or “eth0,eth1″ or “all” for any
  • inet_protocols
    There are ipv4, ipv6 and all (both). If you do not have a special reason to elide one or the other enable both.

TLS/SSL^

#
# TLS / SSL {
#

smtpd_use_tls = yes
smtpd_tls_security_level = may
smtpd_tls_received_header = yes
smtpd_tls_ask_ccert = yes
smtpd_tls_cert_file = /etc/postfix/myssl.crt
smtpd_tls_key_file = /etc/postfix/myssql.key
smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache

smtp_use_tls = yes
smtp_tls_security_level = may
smtp_tls_cert_file = /etc/postfix/myssl.crt
smtp_tls_key_file = /etc/postfix/myssl.key
smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache

#
# TLS / SSL }
#
  • smtpd_use_ssl
    If this is enabled (“yes”) your mail server will accept encrypted connections, which is good, cause the mail’s will not be delivered unencryted over the network in between. However, this costs you some CPU.
  • smtpd_tls_security_level
    Probably set to “may” which allows others to encrypt, but does not deny unencrypted. more..
  • smtpd_tls_receive_headers
    Tells postfix to write encryption informations (protocol and cipher) in the “Received”-header. Good idea for debugging purposes.
  • smtpd_tls_ask_ccert
    If you want to work with permit_tls_clientcert in the restriction directives, this is mandatory. If not, you can disable this.
  • smtpd_tls_cert_file, smtpd_tls_key_file
    Contain the public (certificate) and private (key) parts of your certificate.
  • smtpd_tls_session_cache_database
    Speeds up postfix TLS session handling.
  • smtp_tls_*
    Same as smtpd_tls_* BUT: for sending mails. In this case: the mails this mail server relays to the relay target.

Local users^

#
# LOCAL USERS {
#    (root, postmaster, ..)
#

alias_maps = proxy:hash:/etc/aliases
alias_database = proxy:hash:/etc/aliases

#
# LOCAL USERS }
#
  • alias_maps
    Those contain the local user aliases. The should have at least the postmaster user, so people can send mails to postmaster@$mydomain. An example for the aliases:

    root: postmaster
    postmaster: you@yourdomain.tld
  • alias_database
    Takes probably the same value as alias_maps, but could differ (contain only existing local users which .. targets of “sendmail -bi”)

Relaying users^

#
# RELAYING {
#   addresses, domains and targets
#

relay_transport = relay
relayhost = smtp:[hostname.of.your.mailserver]:2525
relay_destination_recipient_limit = 1
relay_domains = proxy:hash:/etc/postfix/hash/domains
relay_recipient_maps = proxy:hash:/etc/postfix/hash/addresses
#transport_maps = proxy:hash:/etc/postfix/ldap/relay-transports.cf
relay_clientcerts =
    hash:/etc/postfix/hash/backupserver-fingerprints

#
# RELAYING }
#
  • relay_transport
    If set to relay, the destination from relayhost will be used.
  • relayhost
    The host to which we will relay all mails. If this is the backup/upstream MX: your “real” mail server.
  • relay_destination_recipient_limit
    This value can be increased if you setup the targeted server and know how many recipients per mail it can handle. Setting it to 1 is a “safe” value, but will increase the transfered mail amount (assuming one sender is addressed to 99 recipients, there will be 99 mails)
  • relay_domains
    A database containing all the domains you plan to relay. For a backup MX mail server that would be all the domains you “real” mail server handles.
  • relay_recipient_maps
    Contains all the mail addresses you plan to relay.
  • transport_maps
    If you have an MX which does relays to multiple mail servers, you can adjust the destination for each address via the transport_maps.
  • relay_clientcerts
    This does come in handy, if you have an upstream MX before this MX which can authenticate himself by his SSL/TLS key.

There is a widely used alternative to relay_domains and relay_recipient_maps, it is called address verification. Please read here for more informations about that. In short: you do not provide a full list of allowed recipients, but tell your MX to check against another mail server (eg your main mail server) whether the recipient addresses is ok or not. This technique demands less configuration (you don’t have to sync your addresses), but comes with either an overhead of connections (verify before each address is accepted..) or the possibility of cache errors (you add a new mail to your main mail server, but the verification process on the MX still thinks the recipient does not exist, because of its cache – or vice versa: you remove an address from your main server, but your MX still thinks it does exist due to it’s cache). It also bears the the possibility of “chained errors” (your main mail servers goes down, all mails will go to your backup MX, but none can be verified and will be rejected with 450 because the main server is down). Decide yourself whether you want to play safe or easy.

Sizes and limits^

#
# SIZES, LIMITS {
#

message_size_limit = 52428800
smtpd_recipient_limit = 100
smtp_connect_timeout = 60s

#
# SIZES, LIMITS }
#
  • message_size_limit
    The maximum size in bytes for mails you allow to be delivered to you. Keep in mind: Encoding has an overhead. If you encode a binary base64 you might end up with a mail multiple times bigger then the decoded size. A good rule of thumb might be: assume a decoded size you deem to allow, then add 30-50% to it.
  • smtpd_recipient_limit
    This is the max amount of recipients a single mail might address. Spammers tend to use large amounts, but your users might as well.
  • smtp_connect_timeout
    This is the timeout YOUR mailserver will use for connecting to other mail servers. This applies mainly for connections to your relay target, but also for bounce targets. If the connection times out, the mail will be deferred. Adjust it according to the connecting latency between your MX and your relay target. A too high value will give you lot’s of open connections, if the target is not reachable.

Access control^

#
# BASIC ACCESS CONTROL {
#

disable_vrfy_command = yes
#smtpd_reject_unlisted_sender = no
smtpd_helo_required = yes
smtpd_error_sleep_time = 5s
smtpd_soft_error_limit = 5
smtpd_hard_error_limit = 15

#
# BASIC ACCESS CONTROL }
#

This topic is about basic access policies.

  • disable_vrfy_command
    Enabling this, will not allow any connecting mail server to use the VRFY command to check whether some recipient exists or not. On the one hand, this will slow down mail address harvesters. On the other hand, this will not allow spam harvesters to verify recipients and you might end up with even more spam on not existing recipients. Has to be enabled on your target relay host, if you work with verification instead of recipient databases (see Relaying users).
  • smtpd_reject_unlisted_sender
    This disables senders to use mail addresses which are in your recipient lists. However, it is better to use reject_authenticated_sender_login_mismatch in rejections (below), cause this might break some forwards.
  • smtpd_helo_required
    A HELO or EHLO is required in the RFC. Setting this to yes enforces this rule. I do this, some people argue it is to strict.
  • smtpd_error_sleep_time, smtpd_soft_error_limit, smtpd_hard_error_limt
    If a connection attempt contains errors (eg sending an unknown SMTP command or such), the error counter per IP will be increased. If it reaches the soft error limit, all further connections will be delayed for error sleep time. If it reaches the hard error limit, the connections will be rejected (for a certain time). Many spammers have malconfigured SMTP servers.

SASL^

#
# SASL {
#   just deactivate everything
#

smtp_sasl_mechanism_filter =
smtpd_sasl_auth_enable = no

#
# SASL }
#
  • smtp_sasl_mechanism_filter, smtpd_sasl_auth_enable
    This example deals with receive-only-MX. So there is no need for sasl. Disable it completely.

Restrictions^

#
# RESTRICTIONS {
#

The smtpd_delay_reject above assures that all restriction classes contains all informations (eg recipient in smtpd_client_restrictions). Therefore, we can view the restriction classes as simple serial policy enforcement with not difference. One important issue: a REJECT in any class will reject the deliver attempt. An OK will forward to the next chain (in the last chain smtpd_recipient_restrictions the OK will accept the mail finally).

Client^

#
# 01 RESTRICT CLIENT (ip) {
#

smtpd_client_restrictions =

#
# 01 RESTRICT CLIENT (ip) }
#

We leave this empty, cause for my opinion one should not use empty classes “in between”.

Helo^

#
# 02 RESTRICT HELO (hostname) {
#

smtpd_helo_restrictions =

#
# 02 RESTRICT HELO (hostname) }
#

Same as for client..

Sender^

#
# 03 RESTRICT SENDER {
#

smtpd_sender_restrictions =

    #
    # 03.01 PERMIT {
    #

    permit_mynetworks
    permit_tls_clientcerts
    check_client_access proxy:hash:/etc/postfix/hash/whitelist-ips

    #
    # 03.01 PERMIT }
    #

    #
    # 03.02 DENY MALFORMED {
    #

    reject_non_fqdn_sender
    reject_non_fqdn_recipient
    reject_invalid_helo_hostname
    reject_non_fqdn_helo_hostname
    reject_unknown_sender_domain
    reject_unknown_recipient_domain
    reject_unauth_pipelining

    #
    # 03.02 DENY MALFORMED }
    #

    #
    # 03.03 DENY MISGUIDED {
    #
    reject_unlisted_recipient
    reject_unknown_reverse_client_hostname

    #
    # 03.03 DENY MISGUIDED }
    #

    #
    # 03.04 PERMIT GLOBAL DOMAIN WHITELIST {
    #

    check_client_access proxy:hash:/etc/postfix/hash/whitelist-domains

    #
    # 03.04 PERMIT GLOBAL DOMAIN WHITELIST }
    #

    #
    # 03.05 DECENCY: POLICIES {
    #

    check_policy_service inet:127.0.0.1:15000

    #
    # 03.05 DECENCY: POLICIES }
    #

#
# 03 RESTRICT SENDER }
#

This restriction class enforces most of our policies.

  • permit_mynetworks
    Allows you to send from localhost
  • permit_tls_clientcerts
    This enables all servers using the certs from relay_clientcerts to send from this MX. If you have no other servers which might pipe their mails through, do not set it.
  • check_client_access
    Also some whitelists per sender IP, if required. If not -> do not set it. You can put those also in your mynetworks, but it is considered bad style. This list for a hash or btree database would look like this:

    123.123.123.123 OK
    234.234.234.234 OK
  • reject_non_fqdn_sender, reject_non_fqdn_recipient
    Those enforce correct FQDN forms of sender and recipient domains/addresses. You cannot receive them, so why bother with them ?
  • reject_invalid_helo_hostname
    If the sender provides an invalid (syntax) hostname in HELO or EHLO, he will be rejected. Spamers often use something like “???” or alike.. Any mail server admin should be able to set a correct HELO.
  • reject_non_fqdn_hostname
    Close to above, but handles hostnames which are syntactically correct, but not in the FQDN form.
  • reject_unkwown_sender_domain, reject_unknown_recipient_domain
    Unknown means: has no A or MX record. The latter (.._recipient_domain) is probably not necessary, cause they will reject any recipient which is not in our database later on.
  • reject_unauth_pipelining
    This basically means, the sender has to provide the MAIL FROM before the RCPT TO or any other SMTP command in the wrong order. Again: no mail server does this, if he is correctly configured.
  • reject_unlisted_recipient
    This safes us from being an open relay. We only accept mails which we are the final destination for.
  • reject_unknown_reverse_client_hostname
    This rule is somewhat discussed. For some, it seems to harsh, other even prefer reject_unknown_client_hostname. The criticts argument concerns many small, dial-up but non-spammer mail servers which cannot set a reverse hostname. However, you can get easily a static IP nowadays and a virtual relay server at any hoster is very cheap, too. So, if you really want to setup you own mail server on a dial-up with no reverse hostname: no pitty from me.

    • reject_unknown_client_hostname applies if any of the following is true:
      • Sender IP -> hostname mapping failes
      • Sender hostname -> IP mapping failes
      • Resolution of sender hostname does not match IP
    • reject_unknown_reverse_client_hostname applies only if sender IP -> hostname mapping failes (in short: the sender has to have a reverse DNS record)
  • check_client_access again
    This client access whitelists domains. It should be “that late”, because we first have to verify the sender’s validity at least a little bit. However, it is never a good idea to have a domain-name based whitelist in any case. I put it there to assure that anyone considering this at least does some basic checks before. It is very, very easy to fake the sender! Here is an example of the database:

    friend.tld OK
    trusted.tld OK
  • check_policy_service
    So this is when decency comes in. After the basic policies, all more complex rules, which postfix cannot implement himself (or only to harsh, as for DNSBL) will be applied.

Recipient^

#
# 04 RESTRICT RECIPIENT {
#

smtpd_recipient_restrictions =
    reject_unauth_destination
    permit

#
# 04 RESTRICT RECIPIENT }
#

#
# RESTRICTIONS }
#
  • reject_unauth_destination
    This finally rejects any mail passed until here, which is not destinated at us (means: in relay maps or in $mydestination). Even whitelisted senders are not allowed to send anything not on our list. However, you might want to repeat the permit* and check* settings (besides the check_policy_service) from the above class, to allow your whitelisted senders to send to anyone.
  • permit
    Ok, anything that reaches this point is (hopefully) no spam, but real mail.

Leave a Reply

CAPTCHA image