improve relaing page

This commit is contained in:
Christoph Haas 2025-09-03 01:11:13 +02:00
parent e2e10f234b
commit 21a0a1aa8c
4 changed files with 123 additions and 58 deletions

View file

@ -9,10 +9,10 @@ sidebar:
import { Aside } from "@astrojs/starlight/components"; import { Aside } from "@astrojs/starlight/components";
Your mail server is almost ready for use. But one puzzle piece is missing. Your users can receive emails but they cannot Your mail server is almost ready for use. But one puzzle piece is missing. Your users can receive emails but they cannot
send them yet. That is easy to configure but it needs some important precautions. send them yet. Actually this is simple to setup. The security implications make it a bit more complex. So this part of
the guide is a bit longer.
Please note that there is a difference on how **users** send emails versus how **mail servers** send emails. For Let's begin with the difference of how **users** send emails versus how **mail servers** send emails. For comparison:
comparison:
- A **mail server** fetches the _MX_ record for the destination domain of the recipients email address from DNS. That - A **mail server** fetches the _MX_ record for the destination domain of the recipients email address from DNS. That
tells it which mail server to talk to. Then it opens an SMTP connection (TCP port 25) and sends the email. tells it which mail server to talk to. Then it opens an SMTP connection (TCP port 25) and sends the email.
@ -50,7 +50,10 @@ indeed John, your server must reject the email.
Try for yourself: Try for yourself:
```sh ```sh
swaks --server localhost:587 --from john@example.org --to list@example.com -tls swaks --server localhost:587 \
--from john@example.org \
--to list@example.com \
-tls
``` ```
Towards the end of the output you will find: Towards the end of the output you will find:
@ -74,7 +77,12 @@ We are making sure that his authentication happens over an encrypted connection
Try for yourself and provide a valid user name and password: Try for yourself and provide a valid user name and password:
```sh ```sh
swaks --server localhost:587 --from john@example.org --to list@example.com -tls --auth-user john@example.org --auth-password summersun swaks --server localhost:587 \
--from john@example.org \
--to list@example.com \
-tls \
--auth-user john@example.org \
--auth-password summersun
``` ```
Towards the end of the output you will find: Towards the end of the output you will find:
@ -330,14 +338,14 @@ 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: Please note that there are two different TCP ports on a mail server that speak SMTP:
| Service name | SMTP | Submission | Submission TLS | | Service name | SMTP | Submission | Submission TLS |
| -------------------- | ------------------------------------ | ------------------------------------------- | --------------------------- | | -------------------- | ------------------------------------ | ---------------------------- | --------------------- |
| TCP Port | 25 | 587 | 465 | | TCP Port | 25 | 587 | 465 |
| Transport encryption | **Off** at first. STARTTLS optional. | **Off**. Then switch to TLS using STARTTLS. | **On** right from the start | | Transport encryption | **Off** at first. STARTTLS optional. | **Off**. STARTTLS mandatory. | TLS all the time |
| Authentication | Optional [^1] | Mandatory | Mandatory | | Authentication | Optional [^1] | Mandatory | Mandatory |
| Meant for | Servers | Humans | Humans | | Meant for | Servers | Humans | Humans |
| Used by | Mail transport agent (MTA) | Mail user agent (MUA) | Mail user agent (MUA) | | 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 | | Your home ISP | may block this port | will allow this port | will allow this port |
[^1]: [^1]:
In previous guides I recommended to set `smtpd_sasl_auth_enable=yes` globally in the `main.cf`. That would indeed In previous guides I recommended to set `smtpd_sasl_auth_enable=yes` globally in the `main.cf`. That would indeed
@ -352,7 +360,20 @@ or 465.
## Configure the submission service ## Configure the submission service
All Postfix services are declared in the `/etc/postfix/master.cf` file. Most of them are used by Postfix internally, 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: like `pickup`, `cleanup`, `rewrite` or `bounce`. But let's another section anywhere in there. Run these commands:
```sh
postconf -M submission/inet="submission inet n - y - - smtpd"
postconf -P "submission/inet/syslog_name=postfix/submission"
postconf -P "submission/inet/smtpd_tls_security_level=encrypt"
postconf -P "submission/inet/smtpd_sasl_auth_enable=yes"
postconf -P "submission/inet/smtpd_recipient_restrictions=permit_sasl_authenticated,reject"
postconf -P "submission/inet/smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject"
```
That looked confusing, right? What it did was define the `submission` service in the `/etc/postfix/master.cf` service
for you using the `postconf` command. You could have achieved the same thing by editing that file and adding this
section:
``` ```
submission inet n - y - - smtpd submission inet n - y - - smtpd
@ -364,7 +385,7 @@ submission inet n - y - - smtpd
Some notes on that: Some notes on that:
- Lines with options (`-o`) need to be indented. - Lines with options (`-o`) need to be indented so that Postfix understands that they belong to the same service.
- The word `submission` refers to the definition of the service in the `/etc/services` file. So it translates to TCP - The word `submission` refers to the definition of the service in the `/etc/services` file. So it translates to TCP
port 587. port 587.
- The `smtpd` at the end tells Postfix that connections should be handled as incoming SMTP connections. - The `smtpd` at the end tells Postfix that connections should be handled as incoming SMTP connections.
@ -372,7 +393,23 @@ Some notes on that:
- **smtpd_sasl_auth_enable** enables authentication on this port. SASL is short for _Simple Authentication and Security - **smtpd_sasl_auth_enable** enables authentication on this port. SASL is short for _Simple Authentication and Security
Layer_. Layer_.
- **smtpd_recipient_restrictions** define that only authenticated connections are permitted. Without a username and a - **smtpd_recipient_restrictions** define that only authenticated connections are permitted. Without a username and a
password your connection will be rejected. password your connection will be rejected. The **recipient** keyword tells us that this restricts who the recipient
can be.
- **smtpd_sender_restrictions** deals with the **sender**. I will explain that in the next section.
While you are at it, create the `submissions` (with an "s" at the end) service as well:
```sh
postconf -M submissions/inet="submissions inet n - y - - smtpd"
postconf -P "submissions/inet/syslog_name=postfix/submissions"
postconf -P "submissions/inet/smtpd_tls_wrappermode=yes"
postconf -P "submissions/inet/smtpd_sasl_auth_enable=yes"
postconf -P "submissions/inet/smtpd_recipient_restrictions=permit_sasl_authenticated,reject"
postconf -P "submissions/inet/smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject"
```
The only difference to the `submission` (without the "s" at the end) is the `smtpd_tls_wrappermode` that switches on TLS
encryption right from the start of a connection.
Restart the Postfix server: Restart the Postfix server:
@ -383,78 +420,107 @@ systemctl restart postfix
Your users can now use the *submission* port to send email. They just use the port 587 in their mail clients instead of Your users can now use the *submission* port to send email. They just use the port 587 in their mail clients instead of
port 25. port 25.
Feel free to send a test mail using the submission port and encryption. You will need to install the libnet-ssleay-perl Feel free to send a test mail using the `submission` port and encryption (using STARTTLS):
package first, so that SWAKS can speak TLS:
``` ```
swaks --server localhost:587 \ swaks --server localhost:587 \
--from john@example.org \ --from john@example.org \
--to lisa@example.com \ --to lisa@example.com \
--port 587 -tls \ -tls \
--auth-user john@example.org \ --auth-user john@example.org \
--auth-password summersun --auth-password summersun
``` ```
<Aside type="caution"> Also give the `submissions` service a try:
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? swaks --server localhost:465 \
--from john@example.org \
--to lisa@example.com \
--tls-on-connect \
--auth-user john@example.org \
--auth-password summersun
```
No, it will not restrict that. We could be authenticating as `john@example.org` but send an email in the name of Did you notice the difference? This time we used port **465** and instead of the `-tls` option (which uses STARTTLS to
`brunhilde@example.org`. switch to encryption) we use `--tls-on-connect` that uses TLS right from from the start.
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 TODO: try sending from roundcube
this: `SELECT email FROM virtual_users WHERE email='%s' UNION SELECT destination FROM virtual_aliases WHERE source='%s'`
</Aside>
## Protecting against forged sender addresses ## 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 At this stage, a security issue remains. While Postfix is configured to relay emails only after a successful login with
mail server trusts him. Nothing keeps John from impersonating someone else and sending email in his name? Most email a valid username and password, it does not prevent an authenticated user from impersonating another sender. For example,
service providers have restrictions that you can only send emails if the sender matches your actual email address. Lets someone could log in as `john@example.org` but send an email appearing to come from `brunhilde@example.net`.
do that, too.
Postfix provides a setting called You want to see that this work? Let's pretend to be _brunhilde_ while authenticating as _john_:
[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…
- Left column: who you claim to be (emails sender address) ```sh
- Right column: who you logged in as (user name after authentiation) swaks --server localhost:587 \
--from brunhilde@example.org \
--to list@example.com \
-tls \
--auth-user john@example.org \
--auth-password summersun
```
That should not be allowed. But Postfix does not know better and responds with:
```
<~ 250 2.0.0 Ok: queued as EEEB420F51
```
We should fix that. For that purpose Postfix provides a setting called
[smtpd_sender_login_maps](http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps). From the "maps" part you can
deduce that it expects a _mapping_ again:
- _Question_: who you **claim** to be (emails sender address)
- _Answer_: who you **logged in as** (user name after authentiation)
If you think of it, the mapping is simple. The _question_ must match the _answer_. If `john@example.org` wants to be the
sender, he must also authenticate as `john@example.org`.
As we use the email address also as a user name we simply need a mapping where the email address is listed on both As we use the email address also as a user name we simply need a mapping where the email address is listed on both
sides. Fortunately we already have a mapping like that: our _email2email_ mapping that we used earlier as a workaround sides. Let's create it:
for catchall forwardings. Lets reuse it. Please run…
``` ```sh
postconf smtpd_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf cat > /etc/postfix/mariadb-email2email.cf << EOF
user = mailserver
password = SECOND-PASSWORD-HERE
hosts = 127.0.0.1
dbname = mailserver
query = SELECT email FROM virtual_users WHERE email='%s'
EOF
chown root:postfix /etc/postfix/mariadb-email2email.cf
chmod u=rw,g=r,o= /etc/postfix/mariadb-email2email.cf
``` ```
This sets the parameter both for the SMTP port (25) and the submission port (587). Defining these maps is not enough Tell Postfix to use this mapping for our purpose:
though. You also need to make Postfix act on this. Edit the /etc/postfix/master.cf again and in the _submission_ section
add the following option. Make sure the line is indented like all other options:
``` ```
-o smtpd_sender_restrictions=reject_sender_login_mismatch,permit_sasl_authenticated,reject postconf smtpd_sender_login_maps=mysql:/etc/postfix/mariadb-email2email.cf
``` ```
Restart Postfix after this change: You have already added `smtpd_sender_restrictions=reject_sender_login_mismatch` earlier in the master.cf for the
`submission` and `submissions` services. So you have achieved these three steps to prevent sender impersonation:
``` 1. Create a mapping that matches an email address to itself.
systemctl restart postfix 2. Tell Postfix to use that mapping for `smtpd_sender_login_maps` which controls who can be a sender.
``` 3. Enforce that restriction for the `submission` and `submissions` services.
You can now try to send email as a different user than you are logged in. Lets us swaks again to send a spoofed email: You can now try to send email as a different user than you are logged in. Lets us swaks again to send a spoofed email:
``` ```sh
swaks --server localhost --from john@example.com \ swaks --server localhost:587 \
--to john@example.org --port 587 -tls \ --from brunhilde@example.org \
--auth-user john@example.org \ --to list@example.com \
--auth-password summersun --tls \
--auth-user john@example.org \
--auth-password summersun
``` ```
Please note that Johns actual email address is **not** john@example.**com** but john@example.**org**. So it must be The server should rightfully reject that wrong sender:
rejected. The server should tell you:
``` ```
~> MAIL FROM:john@example.com ~> MAIL FROM:john@example.com
@ -463,8 +529,7 @@ rejected. The server should tell you:
<~ * 553 5.7.1 john@example.com: Sender address rejected: not owned by user john@example.org <~ * 553 5.7.1 john@example.com: Sender address rejected: not owned by user john@example.org
``` ```
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".
Of course you can test your new security feature in the Roundcube web mail interface as well. In Settings/Identities you 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: can create another sender email address as in:

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB