re-order pages. add page on lmtp.

This commit is contained in:
Christoph Haas 2025-08-24 23:53:04 +02:00
parent b97d7b03da
commit 759ed36026
5 changed files with 215 additions and 53 deletions

View file

@ -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:
<Splide>
{Array.from({ length: 24 }, (_, i) => i + 1).map((i) => (
<SplideSlide key={i}>
<img src={`/big-picture-receive/${String(i).padStart(2, "0")}.svg`} alt={`Slide ${i}`} />
</SplideSlide>
))}
</Splide>

View file

@ -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:
<Splide>
{Array.from({ length: 24 }, (_, i) => i + 1).map((i) => (
<SplideSlide key={i}>
<img src={`/big-picture-receive/${String(i).padStart(2, "0")}.svg`} alt={`Slide ${i}`} />
</SplideSlide>
))}
</Splide>
---
Let's tackle the receiving of emails step by step:
## DNS
<StepListReceive currentStep={1} />
As shown in the slideshow, you need to have proper DNS records set up. Say that you want to receive emails for the

View file

@ -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:

View file

@ -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
<StepListReceive currentStep={3} />
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.
<StepListReceive currentStep={4} />
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 lets 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
```

View file

@ -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";
<StepListReceive currentStep={5} />
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 its 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:<root@mail>
<- 250 2.1.0 Ok
-> RCPT TO:<john@example.org>
<- 250 2.1.5 Ok
-> DATA
<- 354 End data with <CR><LF>.<CR><LF>
-> 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.