continued writing

This commit is contained in:
Christoph Haas 2025-08-31 16:53:40 +02:00
parent 00185887bd
commit df56646845

View file

@ -31,7 +31,7 @@ Put simply, a mail server will accept an email if…
I have created two illustrations to explain it.
### Outgoing email (without authentication)
### Without authentication = denied
`john@example.org` is on the internet somewhere and wants to send an email to `lisa@example.com` (which is another
domain). Your mail server is not responsible for the _example.com_ domain so it receives Johns email and would have to
@ -62,7 +62,7 @@ Towards the end of the output you will find:
<~* 554 5.7.1 <list@example.com>: Recipient address rejected: Access denied
```
### Outgoing email (with authentication)
### With authentication = accepted
So how does John prove his identity? He needs to use _authenticated_ SMTP. This is similar to the previous case but
Johns email program will also send his username and password.
@ -99,8 +99,7 @@ username and password. Postfix just needs some extra configuration. Run these co
```
postconf smtpd_sasl_type=dovecot
postconf smtpd_sasl_path=private/auth
postconf smtpd_sasl_auth_enable=yes
postconf smtpd_sasl_path=private/dovecot-auth
```
This enables SMTP authentication and tells Postfix that it can talk to Dovecot through a socket file located at
@ -113,45 +112,38 @@ TODO: collapsible on anti-spoofing
## Enable encryption
TODO: move this section to a separate page on TLS encryption
We certainly do not want our users to send their password over the wire wihtout encryption. So the following commands
enable encryption, set the key and certificate paths for Postfix. Just run these commands:
```
postconf smtp_tls_security_level=may
postconf smtpd_tls_security_level=may
postconf smtpd_tls_auth_only=yes
postconf smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.org/fullchain.pem
postconf smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.org/privkey.pem
postconf smtp_tls_security_level=may
```
What it means:
- **smtp_tls_security_level**: \
Allow encrypted _outgoing_ SMTP connections but do not enforce it.
- **smtpd_tls_security_level**: \
Allow encrypted _incoming_ SMTP connections but do not enforce it.
- **smtpd_tls_cert_file** and **smtpd_tls_key_file**: \
Where to find the private key and certificate for encryption.
Please note that configuration in `main.cf` applies to all Postfix services. So the above configuration enabled
encryption for both server-to-server and human-to-server (`submission`) services. The `smtpd_sasl_auth_enable` will only
be set for the `submission` service to enable authentication there.[^1]
<Aside type="tip" title="smtp or smtpd?">
Look closely. Some settings start with “smtp\_” and others with “smtpd\_”. That is not a typo. “smtp\_” refers to the
SMTP _client_. That is the component that sends out emails **from** Postfix **to** other servers. Whereas “smtpd\_”
means the SMTP _server._ That in turn is the component that **receives** emails **from** other systems either from a
remote mail server or one of your users.
</Aside>
The above settings allow encrypted incoming (smtpd\_) and outgoing (smtp\_) connections. But they do not enforce it. If
a remote mail server does not have encryption enabled we will still accept their emails. You may be tempted to enforce
encryption but that would reject email communication with servers who have been configured without encryption.
However the `smtpd_tls_auth_only=yes` setting makes sure that the users authentication information (email address and
password) are always encrypted between the user and your mail server.
<Aside type="tip" title="Restrictions">
In the past you may have configured your _smtpd\_recipient\_restrictions_ to restrict relaying to authenticated users.
Postfix nowadays has setting called
“[smtpd\_relay\_restrictions](http://www.postfix.org/postconf.5.html#smtpd_relay_restrictions)” that deals with
relaying requests in the “RCPT TO” phase of the SMTP dialog. So essentially it works like the good old
“smtpd\_recipient\_restrictions” but is checked first. _smtpd\_relay\_restrictions_ has a reasonable default so
authenticated relaying works automatically: `smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated
defer_unauth_destination`
Look closely. Some settings start with `smtp_` and others start with with `smtpd_`. That is not a typo. `smtp_` refers
to the SMTP _client_. That is the component that sends out emails **from** Postfix **to** other servers. Whereas
`smtpd_` means the SMTP _server._ That in turn is the component that **receives** emails **from** other systems
either from a remote mail server or one of your users.
</Aside>
<details class="collapsible">
<summary>Click here to learn how SMTP authentication works…</summary>
<summary>Click here if you want to learn how SMTP authentication works…</summary>
Are you curious how SMTP authentication looks on a low level? You probably are not. But lets do it anyway. Just once,
so that you get the idea.
@ -203,7 +195,7 @@ Let me briefly explain what these lines mean:
The remote server is allowed to send emails up to 10 MB large. This has long been a common maximum size for emails. However
nowadays 40 MB or even more are more common sizes because emails have grown larger. Some mail servers even allow 100 MB.
- VRFY
Allows remote servers to verify a given name or email address. For example the remote server could send “VRFY john” and
Allows remote servers to verify a given name or email address. For example the remote server could send "VRFY john" and
your server might respond `250 John Doe <john@example.org>`. It can be used to verify that a certain recipient email address
is deliverable
- ETRN
@ -212,15 +204,15 @@ Let me briefly explain what these lines mean:
your server start sending outstanding emails for that domain. It is rarely used.
- STARTTLS
This tells the remote system that it might start switching from this unencrypted to an encrypted connection by sending
the “STARTTLS” command. It will then start negotiating a TLS-encrypted connection. You could compare it to an HTTP connection
the "STARTTLS" command. It will then start negotiating a TLS-encrypted connection. You could compare it to an HTTP connection
that suddenly switches over to an encrypted HTTPS connection. The advantage is that you can start talking SMTP on TCP port
25 and dont have to open up a second TCP port like 465 which is the “SSMTP” (secure SMTP) port and only accepts encrypted
25 and dont have to open up a second TCP port like 465 which is the "SSMTP" (secure SMTP) port and only accepts encrypted
connections.
- ENHANCEDSTATUSCODES
This enables more three-digit return codes for various conditions. See the [RFC2034](http://tools.ietf.org/html/rfc2034)
if you are curious.
- 8BITMIME
In ancient times SMTP only processed 7-bit characters. You couldnt transfer special characters like “Ä” or “ß” without
In ancient times SMTP only processed 7-bit characters. You couldnt transfer special characters like "Ä" or "ß" without
special encoding. 8BITMIME allows a transmission of emails using 8-bit characters. Still many emails are specially encoded
using ISO8859-1 or UTF-8.
- DSN
@ -254,7 +246,7 @@ And the server replies:
```
However now its getting weird because you would have to speak TLS encryption which is not a language that humans speak.
So lets quit this using the “QUIT” command and do that differently:
So lets quit this using the "QUIT" command and do that differently:
```
QUIT
@ -293,7 +285,7 @@ string with a Base64-encoded password:
AUTH PLAIN AGpvaG5AZXhhbXBsZS5vcmcAc3VtbWVyc3Vu
```
Unless you have changed Johns password to something else than “summersun” the server should accept that authentication:
Unless you have changed Johns password to something else than "summersun" the server should accept that authentication:
```
235 2.7.0 Authentication successful
@ -330,7 +322,7 @@ So for Johns case you can easily create the Base64 string using:
printf '\0john@example.org\0summersun' | base64
```
As a result you will get the exact same string you used above with “AUTH PLAIN”.
As a result you will get the exact same string you used above with "AUTH PLAIN".
</details>
@ -338,50 +330,49 @@ As a result you will get the exact same string you used above with “AUTH PLAIN
Please note that there are two different TCP ports on a mail server that speak SMTP:
| Service name | SMTP | Submission |
| -------------- | -------------------------- | --------------------- |
| TCP Port | 25 | 587 |
| Encryption | Optional | Mandatory |
| Authentication | Optional | Mandatory |
| Meant for | Servers | Humans |
| Used by | Mail transport agent (MTA) | Mail user agent (MUA) |
| Your home ISP | may block this port | will allows this port |
| Service name | SMTP | Submission | Submission TLS |
| -------------------- | ------------------------------------ | ------------------------------------------- | --------------------------- |
| TCP Port | 25 | 587 | 465 |
| Transport encryption | **Off** at first. STARTTLS optional. | **Off**. Then switch to TLS using STARTTLS. | **On** right from the start |
| Authentication | Optional [^1] | Mandatory | Mandatory |
| Meant for | Servers | Humans | Humans |
| Used by | Mail transport agent (MTA) | Mail user agent (MUA) | Mail user agent (MUA) |
| Your home ISP | may block this port | will allows this port | will allow this port |
I hope that makes the distinction a bit clearer. Our human users will have to use the _submission_ service.
[^1]:
In previous guides I recommended to set `smtpd_sasl_auth_enable=yes` globally in the `main.cf`. That would indeed
enable authentication on port 25. For a clearer separation I now rather suggest to leave it out (default is `no`)
and just enable authentication on the `submission` service in the `master.cf` file. A possible scenario that
required this setting to be `on` would be if your server were a relay (_smarthost_) for other servers using
authentication..
All Postfix services are declared in the `/etc/postfix/master.cf` file. Please edit the file and look for the
_submission_ section that is commented out by default. Turn that section into the following. Basically I removed the #
character on all lines of this section and removed the lines with the mua\_\* variables.
I hope that makes the distinction a bit clearer. Our human users will have to use the _submission_ service either on 587
or 465.
## Configure the submission service
All Postfix services are declared in the `/etc/postfix/master.cf` file. Most of them are used by Postfix internally,
like `pickup`, `cleanup`, `rewrite` or `bounce`. But let's another section anywhere in there:
```
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_reject_unlisted_recipient=no
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_relay_restrictions=
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
```
Make sure to start the first line in the first column and indent the following lines.
Some notes on that:
This service uses the “smtpd” daemon (see the last word of the first line) which is the piece of software that responds
if you open an SMTP connection on TCP port 25. But it gets a few extra options set…
- in the /var/log/mail.log log file you will see the connections to the submission port as “postfix/submission” (the
_syslog_name_ setting above)
- enforce encryption on this port (_smtpd_tls_security_level_)
- enable authentication (_smtpd_sasl_auth_enable_)
- enforce encryption during authentication (_smtpd_tls_auth_only_)
- allow sending emails to recipients outside of this mail server (_smtpd_reject_unlisted_recipient_)
- remove special restrictions (_smtpd\_\*\_restrictions_)
- allow relaying if the sender was authenticated (_smtpd_recipient_restrictions_)
- send the string _ORIGINATING_ to milter services (_milter_macro_daemon_name_) you can just leave it like that
- Lines with options (`-o`) need to be indented.
- The word `submission` refers to the definition of the service in the `/etc/services` file. So it translates to TCP
port 587.
- The `smtpd` at the end tells Postfix that connections should be handled as incoming SMTP connections.
- **syslog_name** is adding `postfix/submission` to log files so that you can distinguish it from other connections.
- **smtpd_sasl_auth_enable** enables authentication on this port. SASL is short for _Simple Authentication and Security
Layer_.
- **smtpd_recipient_restrictions** define that only authenticated connections are permitted. Without a username and a
password your connection will be rejected.
Restart the Postfix server:
@ -396,30 +387,37 @@ Feel free to send a test mail using the submission port and encryption. You will
package first, so that SWAKS can speak TLS:
```
apt install -y libnet-ssleay-perl
swaks --server localhost --to john@example.org \
swaks --server localhost:587 \
--from john@example.org \
--to lisa@example.com \
--port 587 -tls \
--auth-user john@example.org \
--auth-password summersun
```
<Aside type="tip" title="What is Port 465?">
This TCP port belongs to the “submission over TLS” service. It is used for the submission service but expects an
encrypted connection from the first byte. This port is hardly ever used so you dont have to care about it. The
submission service you just configured is also encrypted but uses the STARTTLS mechanism to switch to a TLS connection
after the welcome message.
<Aside type="caution">
At this point there is still a security flaw. We told Postfix to relay emails only if we authenticate with a valid
username and password. But will it keep us from impersonating an arbitrary sender address?
No, it will not restrict that. We could be authenticating as `john@example.org` but send an email in the name of
`brunhilde@example.org`.
If you want to allow users to send as one of their aliases you could create a new \*.cf file with a mapping query like
this: `SELECT email FROM virtual_users WHERE email='%s' UNION SELECT destination FROM virtual_aliases WHERE source='%s'`
</Aside>
## Protecting against forged sender addresses
A “forged” sender address means that someone claims to be someone else. Lets say that John has authenticated and the
A "forged" sender address means that someone claims to be someone else. Lets say that John has authenticated and the
mail server trusts him. Nothing keeps John from impersonating someone else and sending email in his name? Most email
service providers have restrictions that you can only send emails if the sender matches your actual email address. Lets
do that, too.
Postfix provides a setting called
[smtpd_sender_login_maps](http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps) for that purpose. From the
“maps” part you can deduce that it expects a _mapping_ again. This time the two columns mean…
"maps" part you can deduce that it expects a _mapping_ again. This time the two columns mean…
- Left column: who you claim to be (emails sender address)
- Right column: who you logged in as (user name after authentiation)
@ -466,7 +464,7 @@ rejected. The server should tell you:
```
Sorry if the mixture of example.org and example.com in my examples makes this guide less readable. But I cant use any
real world domains here. Please just note that sometimes its “.com” and sometimes “.org”.
real world domains here. Please just note that sometimes its ".com" and sometimes ".org".
Of course you can test your new security feature in the Roundcube web mail interface as well. In Settings/Identities you
can create another sender email address as in: