Featured image of post 安装和配置电子邮件服务端软件 Mailu

安装和配置电子邮件服务端软件 Mailu

之前搭建Matrix服务器时使用了Synapse自带的发件服务,但是为了以后能扩展基于电子邮件的功能,遂打算自建电子邮件服务器。

Mailu是一个简单但功能齐全的邮件服务器,通过一套Docker镜像来组织服务。当然它是自由软件。该项目提供了一个易于设置、易于维护和功能齐全的邮件服务器,同时不提供专有软件中经常出现的无关功能。

建议以开发者给出的文档为主要参考,本文仅作为补充。本文安装的版本是1.9稳定版。写下本文时,podman-compose还不能正确解析mailu的docker-compose.yml文件中的子网设置部分,因此除非后续podman-compose更新解决这一问题,否则不要用podman来替代docker。安装docker的方法此处略过。

配置DNS

访问域名供应商或者DNS服务供应商。向DNS中添加A类记录。a.b.c.d是服务器的地址。

mail.mydomain.com.  IN  A  a.b.c.d

再添加一条MX类记录,指向服务器mail.mydomain.com

mydomain.com.  IN  MX  10 mail.mydomain.com.

再访问VPS供应商,写入一条rDNS记录。rDNS记录是从IP地址a.b.c.d到域名mail.mydomain.com的反向解析,因此需要通过VPS供应商来设置。

配置防火墙

通过SSH连接到服务器后,首先配置防火墙。可以选择安装ufw来间接配置或者直接配置iptables,我觉得ufw更加方便。配置防火墙需要以root身份或使用sudo。

ufw自带了根据应用预设的防火墙配置,需要打开其中的OpenSSHNginx FullMail submissionSMTPPOP3POP3SIMAPIMAPS。注意OpenSSH一定要打开,否则会和服务器丢失连接,如果修改过SSH的端口,也要记得打开相应端口。除了以上预设配置外,需要额外开启端口465用于TLS的SMTP通信、端口587用于STARTLS的SMTP通信,端口5432用于Mailu从其容器中访问Postgresql。ufw添加规则的方法如下。

添加预设规则(如Mail submission):

sudo ufw allow "Mail submission"

打开指定端口(如465)的TCP功能:

sudo ufw allow 465/tcp

添加后使用以下命令查看规则。

sudo ufw status numbered

根据以上命令列出的规则编号删除规则。比如删除11号规则:

sudo ufw delete 11

以上命令删除多个规则时,不能一次填写多个编号。由于删除规则后规则编号会变化,需要重新查看规则编号,修改后再次执行以上命令。

设置好规则后激活生效。

sudo ufw enable

安装和配置Postgresql

为了方便,我也使用docker来安装postgresql数据库管理软件。以下的配置里Postgresql的容器和Mailu的容器运行在同一个服务器上。

使用docker时需要以root身份或使用sudo,本文中的docker相关操作都通过sudo运行。为了方便之后的配置,这里最好使用默认端口号5432。注意替换POSTGRES_PASSWORD=your-password部分。

sudo docker run -it --name postgresql-default --restart always -e POSTGRES_PASSWORD=your-password -v /srv/postgresql-data:/var/lib/postgresql/data -e POSTGRES_INITDB_ARGS="--data-checksums" -p 5432:5432 postgres

连接到docker中配置数据库

sudo docker exec -it postgresql-default psql -U postgres -d postgres

之后创建mailu用户和相应的数据库。注意替换my_secure_pass部分。

postgres=# create user mailu;
postgres=# alter user mailu password 'my_secure_pass';
postgres=# create database mailu owner mailu;
postgres=# \c mailu
mailu=# create extension citext;
mailu=# \q

注意记录数据库用户mailu的密码。由于Mailu服务的各个容器位于另一个子网中,为了Mailu能够顺利访问数据库,需要设置Postgresql的访问权限。

在当前目录创建pg_hba.conf文件,其中的内容类似于:

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
host    mailu           mailu           172.17.0.0/16           md5

注意将类似host all all all scram-sha-256的部分删除,阻止其他远程访问请求。限定除了本机通信外(如127.0.0.1/32::1/128),只有用户mailu能从Docker的网段172.17.0.0/16访问数据库mailu。 然后将配置文件覆盖到Postgresql的容器中并重启数据库。

sudo docker cp ~/pg_hba.conf postgresql-default:/var/lib/postgresql/data/pg_hba.conf
sudo docker restart postgresql-default

为了验证数据库可用,可以尝试在服务器的Shell中尝试连接到数据库。

psql -U mailu -d mailu -h 127.0.0.1

生成Mailu配置文件

Mailu的开发者们提供了一个Web应用来生成配置文件。开发者提供了配置建议,非常方便。需要注意的是 TLS certificatesDatabase preferencesIPv4 listen address 选项。

由于我打算使用外置的Nginx服务器作为反向代理,因此 TLS certificates 可以选择 mail 或者 mail-letsencrypt 。这两个选项意味着将Web的TLS交给反向代理服务器来处理,Mailu的Web服务只需处理HTTP协议的通信,而IMAP、POP、SMTP的TLS还是由Mailu来处理。选项 mail 意味着使用手动安装的证书,需要在 Mailu storage path 项目里填写的目录下创建 certs 文件夹,并在其中安装证书文件cert.pem和密钥文件key.pem。当然也可以在之后生成的mailu.env文件中通过变量TLS_CERT_FILENAMETLS_KEYPAIR_FILENAME来更改这两个文件的名称。如果选择mail-letsencrypt,则Mailu服务会自动申请并安装证书。服务器上没有其他占用80和443端口的服务时,可以不使用反向代理,此时这里可以选择letsencrypt

如果和我一样使用外置的Postgresql数据库管理软件,则在Database preferences中选择postgresql并填写上一步里配置的信息即可。注意在数据库的地址栏中要填写数据库的真实地址,不要填写127.0.0.1。也不要添加端口号,如果在地址里添加了端口号(如1.1.1.1:5432),会导致后续解析数据库地址时出错,因此之前配置数据库时建议不要更改默认端口。

Mailu的开发者建议在 IPv4 listen address 中填写实际的IPv4地址。注意在此处填写实际IPv4地址时,Nginx的配置需要做针对性修改,在下文中将会说明。

为了方便使用,我选择安装了WebUI(对应选项WebMail)和管理员后台AdminUI。在WebMail处可以根据喜好选择两种不同风格的UI。

根据提示填写完所有信息后,点击生成按钮。此时根据跳转后页面中Step1的提示,创建项目文件夹并下载生成的配置文件docker-compose.ymlmailu.env。此时先不要按Step3操作。

配置Nginx服务器

由于服务器上会运行各不相同的服务,相应的配置也千差万别,因此本节仅说明配置Nginx作为反向代理时可能出错的地方。如果不想使用反向代理,可以直接跳过这一节。

首先修改之前生成并下载到本地的配置文件docker-compose.yml。找到# Core services部分,修改容器中的80443在服务器上对应的端口,比如我改为了80808443。这样就不会和监听80443端口的Nginx冲突。修改后的ports前两行如下:

ports:
  - "x.x.x.x:8080:80"
  - "x.x.x.x:8443:443"

修改mailu.env文件中的REAL_IP_HEADERREAL_IP_FROM部分。x.x.x.x替换为服务器真实IPv4地址。

REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x

此处给出Nginx配置文件的一种写法,更多的配置模板请参考文档。修改Nginx配置后需要重新加载Nginx服务。

server {
    listen 443 ssl http2 reuseport;
    server_name mail.yourdomain.com;
    charset utf-8;

    # ssl
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:40m;
    ssl_session_timeout 5m;
    ssl_session_tickets off;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security "max-age=63072000";

    location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header WL-Proxy-SSL true;
    proxy_set_header X-Forwarded-For $remote_addr;
    client_max_body_size 64M;
    proxy_pass http://a.b.c.d:8080;
    }
}

server_name处需要根据之前的DNS记录填写,ssl_certificatessl_certificate_key处需要填写SSL证书的路径。如果还没有证书,可以通过acme.sh获取证书。此处略过获取证书的过程。

location中的proxy_set_header参照以上例子填写即可。但需要特别注意proxy_pass后需要填写服务器的真实IPv4地址,而不能填写127.0,0,1之类的地址,后续遇到502错误时可以重点检查此处的信息是否填写正确。

安装和运行Mailu容器

确认配置文件无误后按照Step3的提示进入项目目录并启动服务。

cd /srv/mailu
sudo docker-compose -p mailu up -d

服务启动后添加管理员用户,以adminuser为例。

sudo docker-compose -p mailu exec admin flask mailu admin adminuser yourdomain.com PASSWORD

参照Step3的说明,PASSWORD处的初始密码可以在之后进入管理界面修改。如果在添加用户这一步报错,并提示无法访问Postgresql,需要检查Postgresql的访问设置pg_hba.conf和防火墙的设置。先关闭Mailu服务。

sudo docker-compose -p mailu down

修改相关设置后重启服务。

sudo docker-compose -p mailu up -d

如果添加用户过程中出现过错误,需要删除Postgresql中的数据库mailu并按照上文中的方法新建数据库mailu,否则会遇到Postgresql找不到域(Domain)的错误。

之后可以添加一个普通用户(比如myuser)来测试收发邮件是否正常。

sudo docker-compose exec admin flask mailu user myuser yourdomain.com 'password123'

用网页访问mail.mydomain.com,登陆后即可测试相关功能。

维护服务器

后续维护和功能设置可以参考文档