diff --git a/src/content/docs/ispmail-trixie/180-relaying.mdx b/src/content/docs/ispmail-trixie/180-relaying.mdx index a50fee9..ab5582c 100644 --- a/src/content/docs/ispmail-trixie/180-relaying.mdx +++ b/src/content/docs/ispmail-trixie/180-relaying.mdx @@ -9,10 +9,10 @@ sidebar: 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 -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 -comparison: +Let's begin with the difference of how **users** send emails versus how **mail servers** send emails. For comparison: - A **mail server** fetches the _MX_ record for the destination domain of the recipient’s 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. @@ -50,7 +50,10 @@ indeed John, your server must reject the email. Try for yourself: ```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: @@ -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: ```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: @@ -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: -| 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 | +| Service name | SMTP | Submission | Submission TLS | +| -------------------- | ------------------------------------ | ---------------------------- | --------------------- | +| TCP Port | 25 | 587 | 465 | +| Transport encryption | **Off** at first. STARTTLS optional. | **Off**. STARTTLS mandatory. | TLS all the time | +| 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 allow this port | will allow this port | [^1]: 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 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 @@ -364,7 +385,7 @@ submission inet n - y - - smtpd 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 port 587. - 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 Layer_. - **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: @@ -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 port 25. -Feel free to send a test mail using the submission port and encryption. You will need to install the libnet-ssleay-perl -package first, so that SWAKS can speak TLS: +Feel free to send a test mail using the `submission` port and encryption (using STARTTLS): ``` swaks --server localhost:587 \ --from john@example.org \ --to lisa@example.com \ - --port 587 -tls \ + -tls \ --auth-user john@example.org \ --auth-password summersun ``` - +TODO: try sending from roundcube ## Protecting against forged sender addresses -A "forged" sender address means that someone claims to be someone else. Let’s 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. Let’s -do that, too. +At this stage, a security issue remains. While Postfix is configured to relay emails only after a successful login with +a valid username and password, it does not prevent an authenticated user from impersonating another sender. For example, +someone could log in as `john@example.org` but send an email appearing to come from `brunhilde@example.net`. -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… +You want to see that this work? Let's pretend to be _brunhilde_ while authenticating as _john_: -- Left column: who you claim to be (email’s sender address) -- Right column: who you logged in as (user name after authentiation) +```sh +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 (email’s 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 -sides. Fortunately we already have a mapping like that: our _email2email_ mapping that we used earlier as a workaround -for catchall forwardings. Let’s reuse it. Please run… +sides. Let's create it: -``` -postconf smtpd_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf +```sh +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 -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: +Tell Postfix to use this mapping for our purpose: ``` - -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: -``` -systemctl restart postfix -``` +1. Create a mapping that matches an email address to itself. +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. Let’s us swaks again to send a spoofed email: -``` -swaks --server localhost --from john@example.com \ - --to john@example.org --port 587 -tls \ - --auth-user john@example.org \ - --auth-password summersun +```sh +swaks --server localhost:587 \ + --from brunhilde@example.org \ + --to list@example.com \ + --tls \ + --auth-user john@example.org \ + --auth-password summersun ``` -Please note that John’s actual email address is **not** john@example.**com** but john@example.**org**. So it must be -rejected. The server should tell you: +The server should rightfully reject that wrong sender: ``` ~> 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 ``` -Sorry if the mixture of example.org and example.com in my examples makes this guide less readable. But I can’t use any -real world domains here. Please just note that sometimes it’s ".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: diff --git a/src/content/docs/ispmail-trixie/images/relaying-authenticated-relaying.png b/src/content/docs/ispmail-trixie/images/relaying-authenticated-relaying.png new file mode 100644 index 0000000..d1001a0 Binary files /dev/null and b/src/content/docs/ispmail-trixie/images/relaying-authenticated-relaying.png differ diff --git a/src/content/docs/ispmail-trixie/images/relaying-roundcube-selecting-identity.png b/src/content/docs/ispmail-trixie/images/relaying-roundcube-selecting-identity.png new file mode 100644 index 0000000..1390759 Binary files /dev/null and b/src/content/docs/ispmail-trixie/images/relaying-roundcube-selecting-identity.png differ diff --git a/src/content/docs/ispmail-trixie/images/relaying-unauthenticated-relaying-denied.png b/src/content/docs/ispmail-trixie/images/relaying-unauthenticated-relaying-denied.png new file mode 100644 index 0000000..34ceb94 Binary files /dev/null and b/src/content/docs/ispmail-trixie/images/relaying-unauthenticated-relaying-denied.png differ