Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
email:configure-postfix [2025/03/03 20:28] – [Configuration: postfix] willy | email:configure-postfix [2025/03/13 13:30] (current) – [Configuration: postfix] willy | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Configuration: | + | ====== |
+ | Postfix is the Mail Transfer Agent, the tool that actually **moves** your email messages from a the sender to the destination (recipient). It speak the Simple Mail Transport Protocol (SMTP) and it has some serious responsibilities which are: | ||
+ | * Ensure that mail **for** you is properly delivered to you | ||
+ | * Ensure that mail **from** you is properly sent to whoever need to deliver it | ||
+ | * Filter out all undesired mail (spam) | ||
+ | * Prevent abuse from malicious actors who pretend to be you to send unauthorized mail in your name | ||
+ | * Prevent abuse from spammers to send mail pretending to be others (not act as an open relay) | ||
+ | * Ensure that all good practices are followed to prevent your mail server from being listed in blacklists and flagged down as a spammer. | ||
- | We need to configure Postfix | + | Due to these many tasks, an MTA is not a simple piece of software |
- | Postfix configuration files are located under **/ | + | ===== Mail Delivery Pipeline ===== |
- | * main.cf: contains all the oeprative directives of Postfix | + | |
- | * master.cf: contains the services started by Postfix | + | |
- | * sql/*.cf: the specific SQLite link files. You //need// to create this folder. | + | |
- | The concept of **virtual** delivery comes from the //legacy// idea that the mail server | + | Traditionally, |
- | Please note that Postfix configuration is deeply connected to Dovecot, Spamassassin, | + | Nowadays |
- | Before configuring Postfix, it is recomended to read [[https://wiki.gentoo.org/ | + | The pipeline |
+ | * An SMTP client connect to your Postfix (SMTP server) | ||
+ | * Optional TLS encryption is performed | ||
+ | * The client identifies itself (HELO / EHLO) | ||
+ | * Optional authentication is performed using SASL | ||
+ | * Sender identifies itself (MAIL FROM:) | ||
+ | * Recipient is identified (RCPT TO:) | ||
+ | * Email data is transferred (DATA...) | ||
+ | Along all the steps of this pipeline, Postfix can be configured to perform checks to reject the entire transaction. | ||
+ | |||
+ | === The local delivery === | ||
+ | Emails will be delivered locally to local users on the server. I mean, users of the server, like users that can login on the server via shell and such. In the local delivery concept, there is no difference between user and mailbox. | ||
+ | |||
+ | In our case, local delivery will be routed to virtual delivery, no local server users will be able to receive emails. | ||
+ | |||
+ | === The virtual delivery === | ||
+ | Emails will be delivered to a list of domains and mailboxes, not users. It's called //virtual// because there the domains and the mailboxes are defined in a database and are not linked to //real// users of the server. | ||
+ | |||
+ | For our needs, i will show you how to link PostfixAdmin SQLite database to Postfix to perform domains and mailboxes lookup. Postfix virtual delivery activity will be deferred to Dovecot LMTP (Local Mail Transport) so that the emails are properly stored in to Dovecot management. | ||
+ | |||
+ | === The mail Relay === | ||
+ | So far, i always referred to emails to be delivered to the server (local) or to domains hosted on the server (virtual). What about emails to be delivered to **others**? This is called **email relay** and it happens when the Postfix server accepts an email destination which is not local, nor virtual. | ||
+ | |||
+ | The **relay** should be allowed only to authorized users to prevent spammers to abuse our server, which will cause the server itself to be blacklisted and unable to send any emails at all. | ||
+ | |||
+ | |||
+ | ===== Mail Security ===== | ||
+ | The SMTP protocol by itself is unencrypted and unsecured. This was maybe ok at the beginning of Internet, but not today. Today you need to enable both authentication and encryption. | ||
+ | |||
+ | Encryption is performed using TLS, but for legacy reasons you cannot **require** it. There are three types of SMTP connections: | ||
+ | * Plain, no TLS (port 25) | ||
+ | * STARTTLS, connection starts unencrypted and can be __upgraded__ to a TLS connection if both parties support it (port 587) | ||
+ | * SSL/TLS, connection must be established encrypted (port 465) | ||
+ | |||
+ | Plain must always be provided, but you can at least prohibit authentication on non-encrypted connections. | ||
+ | |||
+ | The [[https:// | ||
+ | |||
+ | Postfix does not provide a SASL authentication, | ||
+ | |||
+ | ===== Postfix documentation resources ===== | ||
+ | |||
+ | Postfix website has tons of great documentation, | ||
+ | |||
+ | On the internet there are lots of wikis and HOWTOs about postfix, including [[https:// | ||
+ | |||
+ | |||
+ | ===== Postfix Configuration ===== | ||
+ | |||
+ | Postfix configuration files are located under **/ | ||
+ | * main.cf: contains all the oeprative directives of Postfix | ||
+ | * master.cf: contains the services started by Postfix | ||
+ | * sql/*.cf: the specific SQLite link files. You //need// to create this folder. | ||
+ | Please note that Postfix configuration is deeply connected to Dovecot, Spamassassin, | ||
- | ===== SQL Link ===== | ||
+ | ===== PostfixAdmin Link ===== | ||
Postfix support all kinds of SQL databases, including SQLite. To integrate it, you need to create a few //cf// files and specify associated directives in the // | Postfix support all kinds of SQL databases, including SQLite. To integrate it, you need to create a few //cf// files and specify associated directives in the // | ||
Line 46: | Line 103: | ||
</ | </ | ||
- | To ensure the above files are used by postfix, the following lines need to be added to the **main.cf** config file: | + | To ensure the above files are used by postfix, the following lines need to be added to the **main.cf** config file as show below. |
- | < | + | |
- | # A list of all virtual domains serviced by this instance of postfix. | + | |
- | virtual_mailbox_domains = sqlite:/ | + | |
- | # Look up the mailbox location based on the email address received. | + | |
- | virtual_mailbox_maps = sqlite:/ | + | |
- | # Any aliases that are supported by this system | + | |
- | virtual_alias_maps = sqlite:/ | + | |
- | </ | + | |
- | + | ||
- | The example files at the bottom of this page will include those lines as well. | + | |
===== main.cf: configuration ===== | ===== main.cf: configuration ===== | ||
- | |||
- | Setting up Postfix properly is not an easy task. I will guide you trough my experience and will try to describe the main stuff you need to setup here. | ||
- | |||
- | I will list and try to describe all the settings in the example file below here: | ||
- | * **compatibility_level**: | ||
- | * **soft_bounce**: | ||
- | * **queue_directory, | ||
- | * **mail_owner**: | ||
- | * **setgid_group**: | ||
- | * **myhostname**: | ||
- | * **mydomain**: | ||
- | * **myorigin**: | ||
- | * **inet_interfaces**: | ||
- | * **mydestination**: | ||
- | * **local_recipient_maps**: | ||
- | * **unknown_lo# | ||
- | * **mynetworks_style**: | ||
- | * **relay_domains**: | ||
- | * **in_flow_delay**: | ||
- | * **home_mailbox**: | ||
- | * **smtpd_banner**: | ||
- | * **html_directory, | ||
- | * **inet_protocols**: | ||
- | * **meta_directory, | ||
- | * **virtual_mailbox_domains, | ||
- | * **virtual_mailbox_base**: | ||
- | * **local_transport**: | ||
- | * **biff**: enable or disable the use of biff to notify local users of incoming email. There are no local users as already stated, so disable it. | ||
- | * **message_size_limit**: | ||
- | * **disable_vrfy_command**: | ||
- | * **local_recipient_maps**: | ||
- | * **virtual_transport**: | ||
- | * **virtual_uid_maps, | ||
- | |||
- | Also check the SASL and TLS related rows, as enabling encryption layer and authentication is mandatory from a security point of view. For the TLS settings, // | ||
- | |||
- | The spam filtering rows require you to have setup spamassassin too. | ||
For more details on all the possible options, see [[http:// | For more details on all the possible options, see [[http:// | ||
- | This is the overall file. **Do not copy it blindly**, double check everything: | + | This is the overall file. **Do not copy it blindly**, double check everything, because there are comments that will cause postfix **not** to start if ignored: |
<file - main.cf> | <file - main.cf> | ||
compatibility_level = 3.9 | compatibility_level = 3.9 | ||
- | soft_bounce = yes | + | |
- | queue_directory = / | + | # Generic stuff |
- | command_directory = /usr/sbin | + | |
- | daemon_directory = / | + | |
- | data_directory = / | + | |
mail_owner = postfix | mail_owner = postfix | ||
myhostname = mydomain.com | myhostname = mydomain.com | ||
- | mydomain = mydomain.com | + | mydomain = mydomain.com |
myorigin = $mydomain | myorigin = $mydomain | ||
inet_interfaces = all | inet_interfaces = all | ||
+ | biff = no | ||
+ | message_size_limit = 0 <- this is unlimited, you might want to set a mail size limit here | ||
+ | disable_vrfy_command = yes | ||
+ | syslog_facility = mail | ||
+ | syslog_name = postfix | ||
+ | |||
+ | # Protection from lost mail | ||
+ | soft_bounce = yes <- switch to no in production | ||
+ | unknown_local_recipient_reject_code = 450 <- switch to 550 in production | ||
+ | |||
+ | # Important to keep as is to avoid delivery loops with the virtual domains | ||
mydestination = localhost.localdomain | mydestination = localhost.localdomain | ||
- | unknown_local_recipient_reject_code = 550 | ||
mynetworks_style = host | mynetworks_style = host | ||
- | in_flow_delay = 1s | + | |
- | home_mailbox | + | # Local delivery is actually managed by virtual delivery |
- | smtpd_banner | + | local_transport |
- | sendmail_path = / | + | local_recipient_maps |
- | newaliases_path = / | + | |
- | mailq_path = / | + | # Manage |
- | setgid_group = postdrop | + | virtual_transport |
- | html_directory = no | + | |
- | readme_directory = no | + | |
- | inet_protocols = ipv4 | + | |
- | meta_directory = /etc/postfix | + | |
- | shlib_directory | + | |
virtual_mailbox_domains = sqlite:/ | virtual_mailbox_domains = sqlite:/ | ||
virtual_mailbox_maps = sqlite:/ | virtual_mailbox_maps = sqlite:/ | ||
virtual_alias_maps = sqlite:/ | virtual_alias_maps = sqlite:/ | ||
- | virtual_mailbox_base = / | + | |
- | local_transport | + | # SMTPD (server) SASL + TLS setup |
- | biff = no | + | smtpd_sasl_auth_enable |
- | message_size_limit = 0 | + | broken_sasl_auth_clients |
- | disable_vrfy_command | + | smtpd_tls_auth_only |
- | local_recipient_maps = $alias_maps $virtual_mailbox_maps | + | |
- | virtual_transport = lmtp: | + | |
- | virtual_uid_maps = static: | + | |
- | virtual_gid_maps = static: | + | |
- | # SASL - The following rows for SASL authentication | + | |
smtpd_sasl_type = dovecot | smtpd_sasl_type = dovecot | ||
smtpd_sasl_path = private/ | smtpd_sasl_path = private/ | ||
- | smtpd_sasl_auth_enable = yes | + | smtpd_sasl_security_options = noanonymous, noplaintext |
- | smtpd_sasl_security_options = noanonymous | + | smtpd_sasl_tls_security_options |
- | smtpd_sasl_local_domain | + | |
- | broken_sasl_auth_clients = no | + | |
- | smtpd_sasl_authenticated_header = yes | + | |
- | # Setup TLS - use Let's Encrypt certificates | + | |
- | smtpd_tls_cert_file = / | + | |
- | smtpd_tls_key_file = / | + | |
- | smtpd_tls_loglevel = 0 | + | |
smtpd_tls_security_level = may | smtpd_tls_security_level = may | ||
- | smtpd_tls_auth_only | + | smtpd_tls_cert_file |
+ | smtpd_tls_key_file = / | ||
+ | smtpd_tls_mandatory_protocols = > | ||
smtpd_tls_session_cache_database = btree:/ | smtpd_tls_session_cache_database = btree:/ | ||
- | # Some ANTISPAM | + | |
- | smtpd_delay_reject | + | # Generic access rules |
+ | strict_rfc821_envelopes | ||
smtpd_helo_required = yes | smtpd_helo_required = yes | ||
- | smtpd_helo_restrictions | + | smtpd_reject_unlisted_sender |
- | smtpd_sender_restrictions = permit_sasl_authenticated, | + | |
- | smtpd_recipient_restrictions = reject_unauth_pipelining, | + | |
- | smtpd_client_restrictions = permit_mynetworks, | + | |
- | #, reject_rbl_client zen.spamhaus.org, | + | |
- | body_checks = regexp:/ | + | |
- | # DKIM, SPF, DMARC connection | + | |
- | smtpd_milters = unix:/ | + | |
- | non_smtpd_milters = unix:/ | + | |
- | # Some lmits and timeouts (adapt as needed) | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # Logging defaults | + | |
- | syslog_facility = mail | + | |
- | syslog_name = postfix | + | |
- | #smtpd_relay_before_recipient_restrictions | + | # Client connection (do not put " |
- | </file> | + | smtpd_client_restrictions |
+ | # HELO / EHLO filtering | ||
+ | smtpd_helo_restrictions = permit_sasl_authenticated, | ||
+ | # MAIL FROM | ||
+ | smtpd_sender_restrictions = permit_sasl_authenticated, | ||
+ | # RCPT TO: (before recipient) | ||
+ | smtpd_relay_restrictions = permit_sasl_authenticated, | ||
+ | # DATA | ||
+ | smtpd_data_restrictions = reject_unauth_pipelining | ||
+ | # R * | ||
+ | | ||
+ | smtpd_recipient_restrictions = permit_sasl_authenticated check_policy_service unix: | ||
- | The following file implements some basic checks to discard spam or worse. It's needed if you specified **body_checks** above. | + | # OpenDKIM & OpenDMARC setup |
+ | smtpd_milters | ||
+ | non_smtpd_milters = unix:/ | ||
+ | milter_default_action = accept | ||
- | File **/ | ||
- | <file - body_checks> | ||
- | /Listed in Razor2/ DISCARD | ||
- | /RBL: Received via a relay in Spamhaus XBL/ DISCARD | ||
- | /Delivered to internal network by a host with no rDNS/ DISCARD | ||
- | /Listed in Pyzor/ DISCARD | ||
- | / | ||
- | / | ||
- | /BODY: Mentions millions of/ DISCARD | ||
- | /BODY: Dear Beneficiary/ | ||
- | /Advance Fee fraud/ DISCARD | ||
- | / | ||
- | /RBL: Received via a relay in PSBL/ DISCARD | ||
- | /Delivered to internal network by a host with no rDNS/ DISCARD | ||
- | /Forged mail pretending to/ DISCARD | ||
- | /Message-Id is not valid, according to RFC 2822/ DISCARD | ||
- | / | ||
</ | </ | ||
Line 209: | Line 188: | ||
===== master.cf: configuration ===== | ===== master.cf: configuration ===== | ||
- | The master.cf contains a list of the services (and ports) managed by Postfix on startup. | + | The master.cf contains a list of the services (and ports) managed by Postfix on startup: |
<file - master.cf> | <file - master.cf> | ||
- | # | + | |
- | # Postfix master process configuration file. For details on the format | + | # Port 25 listener |
- | # of the file, see the master(5) manual page (command: "man 5 master" | + | |
- | # | + | |
- | # Do not forget to execute " | + | |
- | # | + | |
- | # ========================================================================== | + | |
- | # service type private unpriv | + | |
- | # | + | |
- | # ========================================================================== | + | |
smtp inet n | smtp inet n | ||
- | -o content_filter=spamassassin | ||
+ | # Port 587 listener (STARTTLS) | ||
+ | smtps inet n | ||
+ | -o smtpd_tls_wrappermode=yes | ||
+ | |||
+ | # Port 465 listener (pure TLS) | ||
submission inet n | submission inet n | ||
- | -o smtpd_sasl_auth_enable=yes | + | |
- | -o smtpd_tls_security_level=may | + | # SPF inbound check filter |
- | -o smtpd_client_restrictions=permit_sasl_authenticated, | + | policy-spf |
+ | user=nobody argv=/ | ||
+ | |||
pickup | pickup | ||
cleanup | cleanup | ||
Line 252: | Line 228: | ||
anvil | anvil | ||
scache | scache | ||
- | |||
- | |||
- | policy-spf | ||
- | | ||
- | |||
- | spamassassin unix - | ||
- | user=spamd argv=/ | ||
- | / | ||
- | |||
- | |||
- | spamassassin unix - | ||
- | user=spamd argv=/ | ||
- | / | ||
</ | </ | ||
Line 275: | Line 238: | ||
that's it. | that's it. | ||
+ | |||
+ | ===== Testing ===== | ||
+ | |||
+ | To test Postfix you need your Dovecot to be running, so set it up, ensure Dovecot SASL is working, then come back here. | ||
+ | |||
+ | To generate a valid Base64 encoding for authentication see [[https:// | ||
+ | |||
+ | To test your unencrypted SMTP service: | ||
+ | <code bash> | ||
+ | telnet 127.0.0.1 25 | ||
+ | Trying 127.0.0.1... | ||
+ | Connected to 127.0.0.1. | ||
+ | Escape character is ' | ||
+ | 220 gardiol.org ESMTP Postfix | ||
+ | EHLO mydomain.com | ||
+ | 250-gardiol.org | ||
+ | 250-PIPELINING | ||
+ | 250-SIZE | ||
+ | 250-ETRN | ||
+ | 250-STARTTLS | ||
+ | 250-ENHANCEDSTATUSCODES | ||
+ | 250-8BITMIME | ||
+ | 250-DSN | ||
+ | 250-SMTPUTF8 | ||
+ | 250 CHUNKING | ||
+ | quit | ||
+ | 221 2.0.0 Bye | ||
+ | Connection closed by foreign host. | ||
+ | </ | ||
+ | |||
+ | ensure that AUTH is not offered! Make sure there is no **250-AUTH PLAIN LOGIN** in the output! This means you have successfully disabled SASL authentication on plain text connections. | ||
+ | |||
+ | To test SSL/TLS: | ||
+ | <code bash> | ||
+ | openssl s_client -starttls smtp -connect 127.0.0.1: | ||
+ | Connecting to 127.0.0.1 | ||
+ | CONNECTED(00000003) | ||
+ | |||
+ | [ ... long TLS handshake omissis ... ] | ||
+ | |||
+ | EHLO mydomain.com | ||
+ | 250-mydomain.com | ||
+ | 250-PIPELINING | ||
+ | 250-SIZE | ||
+ | 250-ETRN | ||
+ | 250-AUTH PLAIN LOGIN | ||
+ | 250-ENHANCEDSTATUSCODES | ||
+ | 250-8BITMIME | ||
+ | 250-DSN | ||
+ | 250-SMTPUTF8 | ||
+ | 250 CHUNKING | ||
+ | AUTH PLAIN -- never post your Base64 strings on internet! -- | ||
+ | 235 2.7.0 Authentication successful | ||
+ | MAIL FROM:< | ||
+ | 250 2.1.0 Ok | ||
+ | RCPT TO:< | ||
+ | 250 2.1.5 Ok | ||
+ | DATA | ||
+ | 354 End data with < | ||
+ | Subject: My Test Email | ||
+ | Test email body | ||
+ | . | ||
+ | 250 2.0.0 Ok: queued as 194869C1AB | ||
+ | quit | ||
+ | 221 2.0.0 Bye | ||
+ | QUIT | ||
+ | </ | ||
+ | Double check that you have that **250-AUTH PLAIN LOGIN**, this means that you have properly setup SASL authentication using TLS. | ||
+ | |||
+ | To tets TLS without STARTLS, repeat the same command above without //-starttls smtp//, but remember to use port 465 instead of 587! | ||
+ | |||
+ | You should now test that: | ||
+ | * You can send an email from yourself to yourself | ||
+ | * You can send an email from yourself to another mailbox of your domain | ||
+ | * You can **not** send emails to other domains if you do not authenticate with SASL on TLS | ||
+ | |||
+ | That should do. | ||
+ | |||
+ | After you are done, go back to your **main.cf** disable soft_bounce and set 550 as error code instead of 450. | ||
+ | |||
+ | |||