From 759ed3602697e7600d33200e06c34056bbd916f4 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sun, 24 Aug 2025 23:53:04 +0200 Subject: [PATCH] re-order pages. add page on lmtp. --- .../docs/ispmail-trixie/156-overview.mdx | 24 +++ src/content/docs/ispmail-trixie/157-dns.mdx | 20 --- ...ive-emails-postfix.mdx => 160-postfix.mdx} | 26 +-- ...ver-emails-dovecot.mdx => 165-dovecot.mdx} | 43 ++--- src/content/docs/ispmail-trixie/167-lmtp.mdx | 155 ++++++++++++++++++ 5 files changed, 215 insertions(+), 53 deletions(-) create mode 100644 src/content/docs/ispmail-trixie/156-overview.mdx rename src/content/docs/ispmail-trixie/{160-receive-emails-postfix.mdx => 160-postfix.mdx} (86%) rename src/content/docs/ispmail-trixie/{165-deliver-emails-dovecot.mdx => 165-dovecot.mdx} (86%) create mode 100644 src/content/docs/ispmail-trixie/167-lmtp.mdx diff --git a/src/content/docs/ispmail-trixie/156-overview.mdx b/src/content/docs/ispmail-trixie/156-overview.mdx new file mode 100644 index 0000000..dfe5b97 --- /dev/null +++ b/src/content/docs/ispmail-trixie/156-overview.mdx @@ -0,0 +1,24 @@ +--- +title: Big picture +lastUpdated: 2025-08-24 +slug: ispmail-trixie/big-picture +sidebar: + order: 156 +--- + +import "@splidejs/splide/css"; +import { Splide, SplideSlide } from "astro-splide"; + +I have created a slideshow to help you understand the process of receiving an email from a remote mail server. Hopefully +it helps to get a better understanding of the steps involved in receiving email. We will prepare the necessary +configuration in the next chapters. + +Use the arrows to navigate between slides: + + + {Array.from({ length: 24 }, (_, i) => i + 1).map((i) => ( + + {`Slide + + ))} + diff --git a/src/content/docs/ispmail-trixie/157-dns.mdx b/src/content/docs/ispmail-trixie/157-dns.mdx index a041d43..da064b4 100644 --- a/src/content/docs/ispmail-trixie/157-dns.mdx +++ b/src/content/docs/ispmail-trixie/157-dns.mdx @@ -8,32 +8,12 @@ sidebar: import { Aside } from "@astrojs/starlight/components"; -import "@splidejs/splide/css"; -import { Splide, SplideSlide } from "astro-splide"; - import { Steps } from "@astrojs/starlight/components"; import Box from "../../../components/Box.astro"; import StepListReceive from "../../../components/StepListReceive.astro"; -I have created a slideshow to help you understand the process of receiving an email from a remote mail server. Use the -arrows to navigate between slides: - - - {Array.from({ length: 24 }, (_, i) => i + 1).map((i) => ( - - {`Slide - - ))} - - ---- - -Let's tackle the receiving of emails step by step: - -## DNS - As shown in the slideshow, you need to have proper DNS records set up. Say that you want to receive emails for the diff --git a/src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx b/src/content/docs/ispmail-trixie/160-postfix.mdx similarity index 86% rename from src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx rename to src/content/docs/ispmail-trixie/160-postfix.mdx index 80ab197..ec7c254 100644 --- a/src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx +++ b/src/content/docs/ispmail-trixie/160-postfix.mdx @@ -1,7 +1,7 @@ --- -title: "Receive emails: Postfix" +title: "Postfix" lastUpdated: 2025-08-20 -slug: ispmail-trixie/receive-emails-postfix +slug: ispmail-trixie/postfix sidebar: order: 160 --- @@ -48,7 +48,7 @@ SELECT "yes" FROM virtual_domains WHERE name='example.net' Run the following code in your shell to create a configuration file creating that mapping: ```sh -cat > /etc/postfix/virtual-mailbox-domains.cf << EOF +cat > /etc/postfix/mariadb-virtual-mailbox-domains.cf << EOF user = mailserver password = SECOND-PASSWORD-HERE hosts = 127.0.0.1 @@ -82,7 +82,7 @@ Apparently, there aren't any more. So the email will go to `john@example.org`. Run this code to create the appropriate mapping file: ```sh -cat > /etc/postfix/virtual-alias-maps.cf << EOF +cat > /etc/postfix/mariadb-virtual-alias-maps.cf << EOF user = mailserver password = SECOND-PASSWORD-HERE hosts = 127.0.0.1 @@ -98,7 +98,7 @@ The query now gets all the `destination` email addresses from the database for a The last mapping we need is a query to find valid mailboxes. Without further ado: ```sh -cat > /etc/postfix/virtual-mailbox-maps.cf << EOF +cat > /etc/postfix/mariadb-virtual-mailbox-maps.cf << EOF user = mailserver password = SECOND-PASSWORD-HERE hosts = 127.0.0.1 @@ -120,12 +120,12 @@ sufficient here. You have created the config files for the three mappings. Now you just need to tell Postfix to use them: ```sh -postconf virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf -postconf virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf -postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf +postconf virtual_mailbox_domains=mysql:/etc/postfix/mariadb-virtual-mailbox-domains.cf +postconf virtual_mailbox_maps=mysql:/etc/postfix/mariadb-virtual-mailbox-maps.cf +postconf virtual_alias_maps=mysql:/etc/postfix/mariadb-virtual-alias-maps.cf -chown root:postfix /etc/postfix/*.cf -chmod o= /etc/postfix/*.cf +chown root:postfix /etc/postfix/mariadb-*.cf +chmod o= /etc/postfix/mariadb-*.cf ``` `postconf` is a command that changes configuration in `/etc/postfix/main.cf` and applies them instantly. You don't have @@ -139,9 +139,9 @@ access. After all a database password is found in these files. Give the mappings a quick test using the `postmap -q` command (_q_ stands for _query_): ```sh -postmap -q example.org mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf -postmap -q jack@example.org mysql:/etc/postfix/mysql-virtual-alias-maps.cf -postmap -q john@example.org mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf +postmap -q example.org mysql:/etc/postfix/mariadb-virtual-mailbox-domains.cf +postmap -q jack@example.org mysql:/etc/postfix/mariadb-virtual-alias-maps.cf +postmap -q john@example.org mysql:/etc/postfix/mariadb-virtual-mailbox-maps.cf ``` These three commands use the three mapping files (`*.cf`) to query your three database tables. The result should show: diff --git a/src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx b/src/content/docs/ispmail-trixie/165-dovecot.mdx similarity index 86% rename from src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx rename to src/content/docs/ispmail-trixie/165-dovecot.mdx index 4316531..5ef97cc 100644 --- a/src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx +++ b/src/content/docs/ispmail-trixie/165-dovecot.mdx @@ -1,7 +1,7 @@ --- -title: "Deliver emails: Dovecot" -lastUpdated: 2025-08-22 -slug: ispmail-trixie/deliver-emails-dovecot +title: "Dovecot" +lastUpdated: 2025-08-24 +slug: ispmail-trixie/dovecot sidebar: order: 165 --- @@ -9,19 +9,7 @@ sidebar: import { Aside } from "@astrojs/starlight/components"; import StepListReceive from "../../../components/StepListReceive.astro"; -## About LMTP - - - -As explained in the previous section, Postfix speaks SMTP and receives the email from the internet. Postfix could even -save the email to a mailbox on disk. But instead we will use Dovecot for the final delivery. Actually Dovecot's main -purpose is to let users fetch their email using the IMAP protocol. But it provides additional features we can use as -well. - -So we need tell Postfix to hand over the incoming email to Dovecot. The communication between Postfix and Dovecot will -happen using LMTP – the [local mail transfer protocol](https://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol). -LMTP is a variant of SMTP with fewer features. It is meant for email communication between internal services that trust -each other. + In this chapter we will mainly configure Dovecot so that it knows how to deliver incoming emails. @@ -75,6 +63,8 @@ located, who owns them and what is their layout? Set this: mail_driver = maildir mail_home = /var/vmail/%{user | domain}/%{user | username} mail_path = ~/Maildir +mail_uid = vmail +mail_gid = vmail ``` This looks more sophisticated than with versions of Dovecot before 2.4. What it means: @@ -87,9 +77,12 @@ This looks more sophisticated than with versions of Dovecot before 2.4. What it - **mail_path**: Within the _home_ directory the actual mailbox will live in a subdirectory called `Maildir`. The reason is that we will store other data in the user's _home_ directory as well that should not conflict with the mailbox directory. +- **mail_uid**/**mail_gid**/: All mailboxes are stored on disk under the owner _vmail_. ### 10-master.conf +TODO: not needed. /var/run/dovecot/auth-userdb is created automatically. + This configuration file at `/etc/dovecot/conf.d/10-master.conf` deals with Dovecot's services. So most settings are sane here and do not have to be changed. However one change is required in the “service auth” @@ -120,7 +113,17 @@ ssl_server_cert_file = /etc/letsencrypt/live/mail.example.org/fullchain.pem ssl_server_key_file = /etc/letsencrypt/live/mail.example.org/privkey.pem ``` -Next let’s take a look at how Dovecot knows about users and their passwords: +### 20-lmtp.conf + +There is one setting in the `/etc/dovecot/conf.d/20-lmtp.conf` that will mess up the recipient's email address in our +setup. So please edit this file and comment out the `auth_username_format` line: + +``` +protocol lmtp { + #auth_username_format = %{user | username} +} + +``` ### auth-sql.conf.ext @@ -142,7 +145,7 @@ userdb sql { } passdb sql { - query = SELECT password FROM virtual_usrs where email='%{user}' + query = SELECT password FROM virtual_users where email='%{user}' } ``` @@ -220,8 +223,8 @@ That should give you: ``` field value -uid -gid +uid vmail +gid vmail home /var/vmail/example.org/john mail_path /var/vmail/example.org/john/Maildir ``` diff --git a/src/content/docs/ispmail-trixie/167-lmtp.mdx b/src/content/docs/ispmail-trixie/167-lmtp.mdx new file mode 100644 index 0000000..8158f65 --- /dev/null +++ b/src/content/docs/ispmail-trixie/167-lmtp.mdx @@ -0,0 +1,155 @@ +--- +title: LMTP +lastUpdated: 2025-08-24 +slug: ispmail-trixie/lmtp +sidebar: + order: 167 +--- + +import { Aside } from "@astrojs/starlight/components"; +import StepListReceive from "../../../components/StepListReceive.astro"; + + + +Glad to see that you are still with me. We are very close to receiving our first email. If you feel lost, please review +the [slideshow](/ispmail-trixie/big-picture/) from earlier in this guide. + +As explained in the previous section, Postfix speaks SMTP and receives the email from the internet. Postfix could even +save the email to a mailbox on disk. But instead we will use Dovecot for the final delivery. Actually Dovecot's main +purpose is to let users fetch their email using the IMAP protocol. But it provides additional features we can use as +well. + +So we need tell Postfix to hand over the incoming email to Dovecot. The communication between Postfix and Dovecot will +happen using LMTP – the [local mail transfer protocol](https://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol). +LMTP is a variant of SMTP with fewer features. It is meant for email communication between internal services that trust +each other. + +## Dovecot listens to LMTP + +There's nothing to do for you here. I just want to tell you how it works. By default Dovecot has this section in its +`/etc/dovecot/conf.d/10-master.conf` file: + +``` +service lmtp { + unix_listener lmtp { + } +} +``` + +So Dovecot has already put a socket into `/var/run/dovecot/lmtp` where it is ready to receive LMTP connections. A socket +is similar to a TCP port but it's only available for other processes running on same system. So nothing to do. + +## Postfix talks to Dovecot using LMTP + +The “_virtual_transport_” in Postfix defines the service to use for delivering emails to the local system. Dovecot has +created a socket file and is ready to listen to incoming LMTP connections. We just need to tell Postfix to send emails +there. So please run: + +```sh +postconf virtual_transport=lmtp:unix:/var/run/dovecot/lmtp +``` + +The syntax looks crazy, but it’s actually simple. You just told Postfix to use the LMTP protocol. And that we want to +use a UNIX socket on the same system (instead of a TCP connection). And the socket file is located at +`/var/run/dovecot/lmtp`. + +## Enable server-side mail rules + +TODO: move to optional chapter + +## Send a test mail locally + +Finally we are ready for our first email. Using the `swaks` command you can create a test email to `john@example.org` +and send it to Postfix: + +```sh +swaks --server localhost --to john@example.org +``` + +If all goes as planned, you will see an SMTP dialog like this: + +``` +=== Trying localhost:25... +=== Connected to localhost. +<- 220 mail ESMTP Postfix (Debian) + -> EHLO mail +<- 250-mail +<- 250-PIPELINING +<- 250-SIZE 10240000 +<- 250-VRFY +<- 250-ETRN +<- 250-STARTTLS +<- 250-ENHANCEDSTATUSCODES +<- 250-8BITMIME +<- 250-DSN +<- 250-SMTPUTF8 +<- 250 CHUNKING + -> MAIL FROM: +<- 250 2.1.0 Ok + -> RCPT TO: +<- 250 2.1.5 Ok + -> DATA +<- 354 End data with . + -> Date: Sun, 24 Aug 2025 21:08:59 +0000 + -> To: john@example.org + -> From: root@mail + -> Subject: test Sun, 24 Aug 2025 21:08:59 +0000 + -> Message-Id: <20250824210859.021444@mail> + -> X-Mailer: swaks v20240103.0 jetmore.org/john/code/swaks/ + -> + -> This is a test mailing + -> + -> + -> . +<- 250 2.0.0 Ok: queued as 2742B221FA + -> QUIT +<- 221 2.0.0 Bye +=== Connection closed with remote host. +``` + +If you see a `250 2.0.0 Ok: queued as…" near the end of this dialog, then Postfix has received the email successfully. + +Check if Postfix managed to pass the email to Dovecot: + +```sh +journalctl -n 10 -u dovecot +``` + +The line you are looking for looks like: + +``` +Aug 24 21:36:22 mail dovecot[23214]: lmtp(john@example.org)<…><…>: msgid=<20250824213622.023221@mail>: saved mail to INBOX +``` + +Does it say `saved mail to INBOX`? Awesome. The email should be visible in John's home directory. Check it: + +```sh +find /var/vmail/example.org/john +``` + +Does it look like this? + +``` +/var/vmail/ +/var/vmail/example.org +/var/vmail/example.org/john +/var/vmail/example.org/john/Maildir +/var/vmail/example.org/john/Maildir/maildirfolder +/var/vmail/example.org/john/Maildir/new +/var/vmail/example.org/john/Maildir/new/1756071382.M198387P23222.mail,S=635,W=655 +/var/vmail/example.org/john/Maildir/cur +/var/vmail/example.org/john/Maildir/tmp +/var/vmail/example.org/john/Maildir/dovecot-uidvalidity.68ab85d6 +/var/vmail/example.org/john/Maildir/dovecot.index.log +/var/vmail/example.org/john/Maildir/dovecot-uidvalidity +/var/vmail/example.org/john/Maildir/dovecot.index.cache +/var/vmail/example.org/john/Maildir/dovecot-uidlist +/var/vmail/example.org/john/Maildir/dovecot.list.index.log +``` + +The important file here is `…/Maildir/new/1756071382.M198387P23222.mail,S=635,W=655`. The numbers will look differently +on your system. But this is the actual mail that got delivered. You can view it with `cat` and will find the test email +you just sent using `swaks`. + +Don't worry about the other files like `maildirfolder` or the files that sound like `dovecot…`. Those help Dovecot keep +track of the mailbox. You can safely ignore them.