re-ordering pages. adding dovecot page.
This commit is contained in:
parent
3a5508cdb5
commit
8a218dbe5a
5 changed files with 186 additions and 193 deletions
|
|
@ -248,3 +248,5 @@ you should run before taking your mail server into production:
|
|||
```sql
|
||||
DELETE FROM mailserver.virtual_domains WHERE name='example.org';
|
||||
```
|
||||
|
||||
TODO: move to final step
|
||||
|
|
|
|||
108
src/content/docs/ispmail-trixie/157-dns.mdx
Normal file
108
src/content/docs/ispmail-trixie/157-dns.mdx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
title: DNS records
|
||||
lastUpdated: 2025-08-24
|
||||
slug: ispmail-trixie/dns-records
|
||||
sidebar:
|
||||
order: 157
|
||||
---
|
||||
|
||||
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
|
||||
`example.com` domain. If some other mail server on the internet wants to send an email to your server, it looks for two
|
||||
records:
|
||||
|
||||
- **MX record (`example.com`)**
|
||||
The MX (Mail Exchange) record tells other mail servers _where to deliver emails_ for the domain `example.com`.
|
||||
Instead of pointing directly to an IP address, it points to a **hostname** (for example, `smtp.example.com`).
|
||||
MX records also include a **priority value**. If multiple mail servers are listed, the one with the lowest priority
|
||||
number is tried first. Others are used as fallback if the first is unavailable.
|
||||
|
||||
- **A / AAAA record (`smtp.example.com`)**
|
||||
The hostname specified in the MX record (`smtp.example.com`) must resolve to an IP address so that other mail servers
|
||||
know how to reach it.
|
||||
- An **A record** maps the hostname to an IPv4 address.
|
||||
- An **AAAA record** maps the hostname to an IPv6 address.
|
||||
|
||||
Once resolved, the sending mail server connects to that IP address on **TCP port 25**, which is the standard port for
|
||||
email delivery (SMTP).
|
||||
|
||||
<Aside>
|
||||
|
||||
If no MX record is found, the remote mail server will instead directly look for an A or AAAA record of `example.com`.
|
||||
|
||||
</Aside>
|
||||
|
||||
<Aside type="caution">
|
||||
|
||||
The MX record must point to a hostname. It **must not** point to an IP address directly.
|
||||
|
||||
</Aside>
|
||||
|
||||
In zone syntax you would create something like:
|
||||
|
||||
```
|
||||
@ IN MX 10 smtp
|
||||
smtp IN A 100.64.17.3
|
||||
smtp IN A fd7a:115c:a1e0::17
|
||||
```
|
||||
|
||||
If possible, also add a PTR record that makes the IP addresses point back to the name `smtp.example.com`. Other mail
|
||||
servers may be more likely so flag your sent emails as spam if you do not have a PTR record. Preferable the forward
|
||||
(A/AAAA) and reverse (PTR) records match.
|
||||
|
||||
<Aside icon="approve-check-circle" title="Test it">
|
||||
|
||||
Use the `host` command to check your domain's MX record.
|
||||
|
||||
```sh
|
||||
$> host -t mx example.com
|
||||
example.com mail is handled by 10 smtp.example.com.
|
||||
```
|
||||
|
||||
Then check if that result resolves to an IP address:
|
||||
|
||||
```sh
|
||||
$> host smtp.example.com
|
||||
smtp.example.com has address 49.13.89.249
|
||||
smtp.example.com has IPv6 address 2a01:4f8:c012:62d1::1
|
||||
```
|
||||
|
||||
Preferably the IP addresses should resolve back to the mail server's name:
|
||||
|
||||
```sh
|
||||
$> host 49.13.89.249
|
||||
249.89.13.49.in-addr.arpa domain name pointer smtp.example.com.
|
||||
$> host 2a01:4f8:c012:62d1::1
|
||||
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.d.2.6.2.1.0.c.8.f.4.0.1.0.a.2.ip6.arpa domain name pointer smtp.example.com.
|
||||
```
|
||||
|
||||
</Aside>
|
||||
|
|
@ -3,9 +3,13 @@ title: TLS certificate
|
|||
lastUpdated: 2025-08-24
|
||||
slug: ispmail-trixie/tls-certificate
|
||||
sidebar:
|
||||
order: 140
|
||||
order: 158
|
||||
---
|
||||
|
||||
import StepListReceive from "../../../components/StepListReceive.astro";
|
||||
|
||||
<StepListReceive currentStep={2} />
|
||||
|
||||
Your mail server can't work properly without a valid TLS certificate. It will be used in three places:
|
||||
|
||||
1. the **webmail** interface (driven by the Apache web server)
|
||||
|
|
@ -13,7 +17,7 @@ Your mail server can't work properly without a valid TLS certificate. It will be
|
|||
3. **Dovecot** (to encrypt IMAP/POP3 communication)
|
||||
|
||||
Your mail server will be able to host email addresses for many domains. But a TLS certificate is usually issued for a
|
||||
single [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) like “mail.example.org”. That _common name_ is
|
||||
single [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) like `mail.example.org`. That _common name_ is
|
||||
an important part of the certificate. It must match the name you use to talk to the server.
|
||||
|
||||
**Correct**: Your mail server is addressed as `mail.example.org`. The TLS certificate was also issued to
|
||||
|
|
@ -8,108 +8,13 @@ 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
|
||||
`example.com` domain. If some other mail server on the internet wants to send an email to your server, it looks for two
|
||||
records:
|
||||
|
||||
- **MX record (`example.com`)**
|
||||
The MX (Mail Exchange) record tells other mail servers _where to deliver emails_ for the domain `example.com`.
|
||||
Instead of pointing directly to an IP address, it points to a **hostname** (for example, `smtp.example.com`).
|
||||
MX records also include a **priority value**. If multiple mail servers are listed, the one with the lowest priority
|
||||
number is tried first. Others are used as fallback if the first is unavailable.
|
||||
|
||||
- **A / AAAA record (`smtp.example.com`)**
|
||||
The hostname specified in the MX record (`smtp.example.com`) must resolve to an IP address so that other mail servers
|
||||
know how to reach it.
|
||||
- An **A record** maps the hostname to an IPv4 address.
|
||||
- An **AAAA record** maps the hostname to an IPv6 address.
|
||||
|
||||
Once resolved, the sending mail server connects to that IP address on **TCP port 25**, which is the standard port for
|
||||
email delivery (SMTP).
|
||||
|
||||
<Aside>
|
||||
|
||||
If no MX record is found, the remote mail server will instead directly look for an A or AAAA record of `example.com`.
|
||||
|
||||
</Aside>
|
||||
|
||||
<Aside type="caution">
|
||||
|
||||
The MX record must point to a hostname. It **must not** point to an IP address directly.
|
||||
|
||||
</Aside>
|
||||
|
||||
In zone syntax you would create something like:
|
||||
|
||||
```
|
||||
@ IN MX 10 smtp
|
||||
smtp IN A 100.64.17.3
|
||||
smtp IN A fd7a:115c:a1e0::17
|
||||
```
|
||||
|
||||
If possible, also add a PTR record that makes the IP addresses point back to the name `smtp.example.com`. Other mail
|
||||
servers may be more likely so flag your sent emails as spam if you do not have a PTR record. Preferable the forward
|
||||
(A/AAAA) and reverse (PTR) records match.
|
||||
|
||||
<Aside icon="approve-check-circle" title="Test it">
|
||||
|
||||
Use the `host` command to check your domain's MX record.
|
||||
|
||||
```sh
|
||||
$> host -t mx example.com
|
||||
example.com mail is handled by 10 smtp.example.com.
|
||||
```
|
||||
|
||||
Then check if that result resolves to an IP address:
|
||||
|
||||
```sh
|
||||
$> host smtp.example.com
|
||||
smtp.example.com has address 49.13.89.249
|
||||
smtp.example.com has IPv6 address 2a01:4f8:c012:62d1::1
|
||||
```
|
||||
|
||||
Preferably the IP addresses should resolve back to the mail server's name:
|
||||
|
||||
```sh
|
||||
$> host 49.13.89.249
|
||||
249.89.13.49.in-addr.arpa domain name pointer smtp.example.com.
|
||||
$> host 2a01:4f8:c012:62d1::1
|
||||
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.d.2.6.2.1.0.c.8.f.4.0.1.0.a.2.ip6.arpa domain name pointer smtp.example.com.
|
||||
```
|
||||
|
||||
</Aside>
|
||||
|
||||
## Postfix
|
||||
|
||||
<StepListReceive currentStep={2} />
|
||||
<StepListReceive currentStep={3} />
|
||||
|
||||
Now that other mail servers can locate your mail server, the next step is to make sure it can receive and process
|
||||
incoming emails. This task is handled by Postfix, which communicates using the SMTP protocol.
|
||||
|
|
|
|||
|
|
@ -109,127 +109,101 @@ authenticate your users which is relevant later when we set up _relaying_.
|
|||
|
||||
### 10-ssl.conf
|
||||
|
||||
TODO: verify
|
||||
|
||||
Earlier in this guide you created both a key and a certificate file to encrypt the communication with POP3, IMAPs and
|
||||
HTTPS between the users and your mail server. You need to tell Dovecot where to find these files:
|
||||
|
||||
```
|
||||
ssl\_cert = \</etc/letsencrypt/live/webmail.example.org/fullchain.pem
|
||||
ssl\_key = \</etc/letsencrypt/live/webmail.example.org/privkey.pem
|
||||
```
|
||||
|
||||
And enforce TLS encryption by setting:
|
||||
Earlier in this guide you created both a key and a certificate file to encrypt the communication with IMAPS and HTTPS
|
||||
between the users and your mail server. You need to tell Dovecot where to find these files. Also set
|
||||
[ssl=required](https://doc.dovecot.org/2.4.1/core/config/ssl.html#how-to-specify-when-ssl-tls-is-required) to prevent
|
||||
that someone sends their password without encryption.
|
||||
|
||||
```
|
||||
ssl = required
|
||||
ssl_server_cert_file = /etc/letsencrypt/live/mail.example.org/fullchain.pem
|
||||
ssl_server_key_file = /etc/letsencrypt/live/mail.example.org/privkey.pem
|
||||
```
|
||||
|
||||
See the [Dovecot documentation on SSL encryption](https://doc.dovecot.org/admin_manual/ssl/) for more information.
|
||||
|
||||
Next let’s take a look at how Dovecot knows about users and their passwords:
|
||||
|
||||
### auth-sql.conf.ext
|
||||
|
||||
Dovecot reads the `auth-sql.conf.ext` which defines how to find user information in your database. Open the file. There
|
||||
are two sections:
|
||||
|
||||
- userdb: where to find a user’s mailbox in the file system
|
||||
- passdb: where to find the user’s hashed password
|
||||
|
||||
By default Dovecot will run two queries at your database. One for the _userdb_ that gets information like the user ID,
|
||||
group ID, home directory and quota. And another for the _passdb_ that gets the hashed password.
|
||||
|
||||
The “userdb” section already reads:
|
||||
Let's tell Dovecot where to find information on an valid users (_userdb_) and their passwords (_passdb_). You can safely
|
||||
replace the content of the `/etc/dovecot/conf.d/auth-sql.conf.ext` file by:
|
||||
|
||||
```
|
||||
userdb {
|
||||
driver = sql
|
||||
args = /etc/dovecot/dovecot-sql.conf.ext
|
||||
sql_driver = mysql
|
||||
|
||||
mysql /var/run/mysqld/mysqld.sock {
|
||||
user = mailserver
|
||||
password = 'SECOND-PASSWORD-HERE'
|
||||
dbname = mailserver
|
||||
}
|
||||
|
||||
userdb sql {
|
||||
query = SELECT email as user FROM virtual_users WHERE email='%{user}'
|
||||
iterate_query = SELECT email as user FROM virtual_users
|
||||
}
|
||||
|
||||
passdb sql {
|
||||
query = SELECT password FROM virtual_usrs where email='%{user}'
|
||||
}
|
||||
```
|
||||
|
||||
As you can see Dovecot uses an SQL database lookup to get that information. And it refers to the dovecot-sql.conf.ext
|
||||
file for more information. Let’s see…
|
||||
|
||||
## /etc/dovecot/dovecot-sql.conf.ext
|
||||
|
||||
(This configuration file is one level up and not in “conf.d”.)
|
||||
|
||||
You will find this file well documented although all configuration directives are commented out. Add these lines at the
|
||||
bottom of the file:
|
||||
|
||||
```
|
||||
driver = mysql
|
||||
|
||||
connect = \
|
||||
host=127.0.0.1 \
|
||||
dbname=mailserver \
|
||||
user=mailserver \
|
||||
password=x893dNj4stkHy1MKQq0USWBaX4ZZdq
|
||||
|
||||
user_query = SELECT email as user, \
|
||||
concat('*:bytes=', quota) AS quota_rule, \
|
||||
'/var/vmail/%d/%n' AS home, \
|
||||
5000 AS uid, 5000 AS gid \
|
||||
FROM virtual_users WHERE email='%u'
|
||||
|
||||
password_query = SELECT password FROM virtual_users WHERE email='%u'
|
||||
|
||||
iterate_query = SELECT email AS user FROM virtual_users
|
||||
```
|
||||
|
||||
<Aside type="tip" title="Backslashes">
|
||||
Ending a line with a backslash (\) means that it is continued on the next line. It keeps the configuration more
|
||||
readable when it is split over multiple lines.
|
||||
</Aside>
|
||||
|
||||
What these lines mean:
|
||||
|
||||
- driver: the kind of database. MariaDB is the same kind as MySQL.
|
||||
- connect: where to find the MySQL database and how to access it (username, password)
|
||||
- user_query: an SQL query that returns the user name (=the email address), the quota, the home directory, user ID and
|
||||
group ID.
|
||||
- password_query: this SQL query just gets the password hash from the database
|
||||
- iterate_query: ‘doveadm’ uses this query to get a list of all users. That allows you to use the “doveadm user ‘\*'”
|
||||
command later.
|
||||
|
||||
The _user_query_ gets several pieces of information from the database. Let’s look at it one by one:
|
||||
|
||||
- email AS user
|
||||
It gets the the _email_ field from the database which corresponds to the user name. Dovecot expects it in the _user_
|
||||
field so we set an alias to _“user”._
|
||||
- userdb_quota_rule
|
||||
This is the user’s quota in bytes. Think of it as the maximum possible space on disk that the user can occupy. As
|
||||
[documented](https://doc.dovecot.org/configuration_manual/quota/#per-user-quota) Dovecot expects the quota in a
|
||||
special format like “\*:bytes=10000” if the user should not be able to store more than 10,000 bytes. That’s why we
|
||||
begin the string with ‘\*:bytes=’.
|
||||
- userdb_home
|
||||
This leads to the directory where all emails and various control files for this user are located. The placeholder
|
||||
‘%d’ replaces the domain and ‘%n’ the user part. So for John that makes it “/var/vmail/example.org/john”.
|
||||
- userdb*uid and userdb_gid
|
||||
Those are the user ID and group ID of \_vmail* user – 5000 for both. Dovecot uses it to set the permissions of files
|
||||
it creates. As all users share the same system user “vmail” this is just a static number.s
|
||||
|
||||
## Fix permissions
|
||||
Dovecot can also read the path to a user's home directory and the user-ID and group-ID from the database. Our setup has
|
||||
a fixed schema for the home directory (`/var/vmail/DOMAIN/USER`) and the user-ID and group-ID are always `vmail` and
|
||||
`vmail`.
|
||||
|
||||
Make sure that only root can access the SQL configuration file so nobody else is reading your database access passwords:
|
||||
|
||||
```sh
|
||||
chown root:root /etc/dovecot/conf.d/auth-sql.conf.ext
|
||||
chmod go= /etc/dovecot/conf.d/auth-sql.conf.ext
|
||||
```
|
||||
chown root:root /etc/dovecot/dovecot-sql.conf.ext
|
||||
chmod go= /etc/dovecot/dovecot-sql.conf.ext
|
||||
```
|
||||
|
||||
## Restart Dovecot
|
||||
|
||||
Restart Dovecot from the shell:
|
||||
|
||||
```
|
||||
```sh
|
||||
systemctl restart dovecot
|
||||
```
|
||||
|
||||
Look at your /var/log/mail.log logfile. You should see:
|
||||
Check your logs:
|
||||
|
||||
```sh
|
||||
journalctl -fu dovecot
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```
|
||||
... Dovecot v2.3.13 (f79e8e7e4) starting up for imap, lmtp, sieve, pop3 (core dumps disabled)
|
||||
dovecot[13309]: master: Dovecot v2.4.1-4 (7d8c0e5759) starting up for imap, lmtp, sieve (core dumps disabled)
|
||||
systemd[1]: Started dovecot.service - Dovecot IMAP/POP3 email server.
|
||||
```
|
||||
|
||||
If you get any error messages please double-check your configuration files.
|
||||
|
||||
<Aside type="tip" title="Why there is no /var/log/mail.log?">
|
||||
|
||||
In previous guides we used to check the log output from Postfix and Dovecot in a file called `/var/log/mail.log`.
|
||||
However in the age of _systemd_ the output of the processes is written to systemd's _journal_ instead.
|
||||
|
||||
To read the complete logs of Dovecot, run (u=unit):
|
||||
|
||||
```sh
|
||||
journalctl -u dovecot
|
||||
```
|
||||
|
||||
To `tail -f` the logs (f=follow):
|
||||
|
||||
```sh
|
||||
journalctl -fu dovecot
|
||||
```
|
||||
|
||||
However on a busy server it will show the entire journal and then follow any changes. You may want to limit the output
|
||||
to the last 10 lines:
|
||||
|
||||
```sh
|
||||
journalctl -n 10 -fu dovecot
|
||||
```
|
||||
|
||||
If you prefer the old way of having log files, just install the `rsyslog` package.
|
||||
|
||||
</Aside>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue