Postfix SMTP server
21 September,2023
Postfix is a free and open-source mail transfer agent (MTA) that routes and delivers E-mail.
What is an MTA? Well within the Internet email system, a message transfer agent (MTA), or mail transfer agent, or mail relay is software that transfers e-mail messages from one computer to another using SMTP protocol.
This guide will cover the setup of Postfix server with LDAP backend. This instance will use 2 ports, 587 and 25 for sending and recieving mails. We will setup TLS which will be used to encrypt authentication, will add restrictions for senders and recievers, add DKIM(DomainKeys Identified Mail) which is used to verify senders and Rspamd for blocking spam mails.
- Install package
- Configure Postfix
- DKIM and Rspamd with Postfix
- LetsEncrypt Certificates using Certbot
- Example config
Install package
apt install postfix
Configure Postfix
- The config files are present in /etc/postfix.
- Postfix is configured with main.cf and master.cf files.
- The main.cf configuration file specifies a subset of all the parameters that control the operation of the mail system such as authentication, user lookups, TLS etc.
- The master.cf configuration file defines how a client program connects to a service, and what daemon program runs when a service is requested.
- We will be using 2 ports:
- Port 25(smtp) for recieving mails. This port must remain open as we need to recieve mails from different servers which may or may not be able to use encrypted conncection, hence TLS is optional. Authentication is disabled on this port.
- Port 587(submission) for sending mail. Since authentication is enabled, TLS is enforced.
Authentication
- Postfix supports Dovecot and Cyrus SASL for authentication.
- This guide will use Dovecot SASL. Instructions can be found in [[Dovecot IMAP server tutorial]].
- The below configuration will use Dovecot SASL and force AUTH over TLS.
smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_sasl_authenticated_header = yes # AUTH is enabled only when TLS is enabled smtpd_tls_auth_only = yes
- We also need to enable sender validation i.e the authenticated user must own the email id in FROM field. This will be configured in User lookup and Sender verification.
Mail delivery
- Mails are delivered to users inbox using Dovecot LMTP. Inorder to configure delivery add following line to main.cf
- Instructions for enabling LMTP in Dovecot can be found in [[Dovecot IMAP server]].
- The name of LMTP socket configured in dovecot will be used in main.cf which in this case is dovecot-lmtp. Use the name of socket configured in Dovecot.
mailbox_transport = lmtp:unix:private/dovecot-lmtp
User lookup and Sender verification
Postfix uses lookup tables to store and query information about users, mailboxes, access control, address rewriting and even content filtering. We will create two such tables for LDAP lookup.
- Make directory /etc/postfix/ldap where we will store LDAP lookup tables. We create a seperate directory for lookup tables for ease of access. Tables can be created anywhere of one’s choice.
mkdir /etc/postfix/ldap cd /etc/postfix/ldap
- Generally it’s better to name tables based on function they serve.
- First we will create a table for user lookup. This table will check if the username given for authentication exists in user database. If user exists, it will return result attribute. Since its user lookup, we can name this file ldap_users.cf.
touch ldap_users.cf
- Add following lines to file and edit search_base accordingly.
- search_base takes the value of base DN that postfix will use to search user in LDAP.
- Following table will search LDAP for given mail and if exists, will return the uid attribute of that user.
server_host = localhost:389 bind = no search_base = dc=example,dc=in query_filter = (&(objectClass=inetOrgPerson)(mail=%s)) result_attribute = uid
- postmap command helps us check if the lookup table is working as expected. The following command will return uid if mail “user@example.in” exists. In case the user doesnt exist the command will not return any value
postmap -q user@example.in ldap:/etc/postfix/ldap/ldap_users.cf
- Next, we will create a sender verification table to check if the authenticated user owns/contains the email id given in FROM field.
- Create a file ldap_check_sender.cf
touch ldap_check_sender.cf
- As mentioned earlier the table will check mail and return uid if that mail exists. Even though both these files have exactly same content its better to seperate them as user lookup table can be have more complex queries and which is generally not required for sender verification.
server_host = localhost:389
bind = no
search_base = dc=example,dc=in
query_filter = (&(oddbjectClass=qmailUser)(mail=%s))
result_attribute = uid
- Finally we will configure postfix to use these lookup tables. Add following line in main.cf
virtual_alias_maps = ldap:/etc/postfix/ldap/ldap_users.cf
smtpd_sender_login_maps = ldap:/etc/postfix/ldap/ldap_check_sender.cf
TLS
- TLS in postfix can either be forced or used oppurtunistically. The possible values are
- encrypt - force postfix to use secure TLS enabled connection.
- may - oppurtunistic, use STARTTLS command. checks if other server can use TLS if not it wouldnt use TLS.
STARTTLS is not a protocol but an email protocol command. It’s used to tell an email server that an email client wants to upgrade an existing insecure connection to an encrypted one using TLS. However, if a server doesn’t support encryption or is malicious, running this command can result in clients establishing an insecure connection.
- We can also log hostname of remote server that offers STARTTLS.
smtpd_tls_security_level = encrypt
smtpd_tls_manditory_ciphers = high
smtpd_tls_loglevel = 1
# Block TLS less than v1.2
smtpd_tls_mandatory_protocols = TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtpd_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
# Change certificate location.
smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem
# Following parameter will log servers that offer STARTTLS
smtp_tls_note_starttls_offer = yes
SMTP Restrictions
Postfix allows you to specify lists of access restrictions for each stage of the SMTP conversation. Each restriction list is evaluated from left to right until some restriction produces a result of PERMIT, REJECT or DEFER (try again later). The end of each list is equivalent to a PERMIT result. By placing a PERMIT restriction before a REJECT restriction you can make exceptions for specific clients or users.
- Restrictions are read from left to right.
- Restrictions for sender addresses
- The username and mail id must belong to the same user.
- Only allow authenticated senders.
- Relay restrictions
- Open relay is disabled. An open relay allows servers on internet to use our server to send mails.
- Restrictions for recipient addresses.
- More info about restrictions can be found in postmap(5) man page.
smtpd_sender_restrictions = reject_sender_login_mismatch,
permit_sasl_authenticated,
smtpd_relay_restrictions = permit_mynetworks,
permit_sasl_authenticated,
defer_unauth_destination,
smtpd_recipient_restrictions = permit_mynetworks,
reject_invalid_helo_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
warn_if_reject reject_unknown_helo_hostname,
warn_if_reject reject_unknown_reverse_client_hostname,
reject_unknown_sender_domain,
reject_unknown_recipient_domain
- Additional config parameters
- Disable VRFY command.
- Enforce server to send HELO command before MAIL command.
disable_vrfy_command = yes smtpd_helo_required = yes
Configure master file
- Uncomment and edit master.cf. By default only smtp/Port 25 will be enabled.
- Port 25/smtp
smtp inet n - y - - smtpd
-o smtpd_tls_security_level=may
-o smtpd_sasl_auth_enable=no
- These paramenters are set seperately for smtp port because TLS and AUTH is enabled by default. These two parameters configure smtp to use STARTTLS and disable AUTH.
- Port 587/submission
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_client_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject
- Commented out arguments need not be deleted
DKIM and Rspamd with Postfix
- OpenDKIM
- Installisation
apt install opendkim opendkim-tools mkdir dkimkeys
- Setting up keys. Selector can be any value. Generally something like “Sep2023” is used as selector.
opendkim-genkey -s "youselector" -d "yourdomain" --directory /etc/dkimkeys
- The directory containing key must be owned by opendkim, be sure to chown dkimkeys directory.
chown -R opendkim:opendkim /etc/dkimkeys
- Create DNS record as described in /etc/dkimkeys/youyselector.txt
- Configure Domain,Selector,Keyfile values in /etc/opendkim.conf. An example config is given below
Syslog yes UMask 007 Domain example.in KeyFile /etc/dkimkeys/your_selector.private Selector your_selector Socket inet:8892@localhost PidFile /var/run/opendkim/opendkim.pid OversignHeaders From TrustAnchorFile /usr/share/dns/root.key UserID opendkim
- Installisation
- Rspamd
- Installisation
sudo apt-get install -y lsb-release wget # optional CODENAME=`lsb_release -c -s` sudo mkdir -p /etc/apt/keyrings wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rspamd.gpg > /dev/null echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ $CODENAME main" | sudo tee /etc/apt/sources.list.d/rspamd.list echo "deb-src [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ $CODENAME main" | sudo tee -a /etc/apt/sources.list.d/rspamd.list sudo apt-get update sudo apt-get --no-install-recommends install rspamd
- Create /etc/rspamd/local.d/milter_headers.conf
use = ["authenticated-results", "x-spam-status"]; authenticated_headers = ["authenticated-results"]
- More configuration options can be found at rspamd
- Installisation
- Add following parameters to main.cf to configure DKIM and Rspamd
smtpd_milters = inet:localhost:8892 inet:localhost:11332 milter_default_action = accept non_smtpd_milters = $smtpd_milters milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
Letsencrypt certificates
- Certbot to create letsencrypt certificate
apt install certbot python3-certbot-nginx certbot certonly
- Certbot nginx module can be installed to create certficates from nginx configuration
certbot --nginx
Example config
An example main.cf config file
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 2
# TLS parameters
smtpd_tls_mandatory_ciphers = high
smtpd_tls_security_level = encrypt
smtpd_tls_loglevel = 1
smtpd_tls_eecdh_grade = ultra
smtpd_tls_auth_only = yes
smtpd_tls_cert_file = /etc/letsencrypt/live/example.in/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/example.in/privkey.pem
smtp_tls_note_starttls_offer = yes
smtpd_tls_mandatory_protocols = TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtpd_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
myhostname = example.in
myorigin = $myhostname
mydestination = example.in
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
recipient_delimiter = +
mailbox_size_limit = 0
# message size limit is 50MB
message_size_limit = 52428800
inet_interfaces = all
inet_protocols = all
# User Authentication
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes
# LDAP user table
virtual_alias_maps = ldap:/etc/postfix/ldap/ldap_users.cf
smtpd_sender_login_maps = ldap:/etc/postfix/ldap/ldap_check_sender.cf
# LMTP
mailbox_transport = lmtp:unix:private/dovecot-lmtp
# DKIM keys, Rspamd and mail filters
smtpd_milters = inet:localhost:8892 inet:localhost:11332
milter_default_action = accept
non_smtpd_milters = $smtpd_milters
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
# Policy and restrictions
smtpd_sender_restrictions = reject_sender_login_mismatch,
permit_sasl_authenticated,
smtpd_relay_restrictions = permit_mynetworks,
permit_sasl_authenticated,
defer_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks,
reject_invalid_helo_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
warn_if_reject reject_unknown_helo_hostname,
warn_if_reject reject_unknown_reverse_client_hostname,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
disable_vrfy_command = yes
smtpd_helo_required = yes
strict_rfc821_envelopes = yes