内网邮件系统目标很明确:只做内网收发,不碰公网 MX,不依赖 Let’s Encrypt,而是直接接入现有的 AD / DNS 环境,并使用已经搭好的 step-ca 为 Mailu 签发内部 HTTPS 证书。最终效果是,https://mailu.yxwa.info 可以正常打开,Mailu 管理员账号创建成功,核心容器全部健康运行,具备继续做内网邮箱创建与后续 PKI 统一信任分发的基础。整体配置范围与目标也与当前任务定义一致:Mailu 作为内网邮件系统部署,内网 DNS 已提供 A 记录与 MX 记录,step-ca 统一签发内网证书,根证书后续通过 GPO 下发属于后续任务范围。

一、这次部署的目标与环境

这次的邮件域是 ad.yxwa.info

Mailu 主机名为 mailu.yxwa.info

Mailu 服务器内网地址为 192.168.1.30。

AD 域控地址是 192.168.1.122,内网 DNS 已经提供了 mailu.yxwa.info -> 192.168.1.30 的 A 记录,以及MX 记录。

CA 由 step-ca 提供,服务地址是 ca.yxwa.info:8443,并且健康检查接口已经正常返回 {"status":"ok"}。这些前置条件决定了 Mailu 不需要自己处理公网证书获取,而是直接使用内部 PKI。

Mailu 本身是一个基于 Docker Compose 的邮件系统集合,不是单一容器。它把 Web 前端、管理后台、SMTP、IMAP、反垃圾、Webmail、Redis、内部 DNS 等服务拆成多个容器协同工作。外边看到的登录页其实主要是 front/nginx 暴露出来的统一入口,真正的邮件投递由 postfix 执行,收件与 IMAP 访问由 dovecot 提供,后台管理由 admin 容器处理,Webmail选择的是 Roundcube。当前生成的配置中,ADMIN=true、WEBMAIL=roundcube、API=true,而 TLS 模式使用的是 TLS_FLAVOR=cert,也就是由宿主机提供现成证书文件给 Mailu 使用。

二、Mailu 配置的关键点

Mailu 这套配置的核心参数非常少,真正要确认的是域名、主机名、TLS 方式和 Docker 子网。当前 mailu.env 中有效的关键配置如下:

DOMAIN=ad.yxwa.info
HOSTNAMES=mailu.yxwa.info
TLS_FLAVOR=cert
SUBNET=192.168.203.0/24
WEBMAIL=roundcube
API=true
ADMIN=true
WEBDAV=none
ANTIVIRUS=none

这些参数已经在最终运行配置中生效。前端容器对外绑定了 80、443、25、465、587、110、995、143、993、4190 等端口,正好覆盖 Web、SMTP、Submission、IMAP、POP3、ManageSieve 等常见访问方式。

一个非常重要的点是:当前生成的 compose 文件里,证书挂载目录不是 /opt/mailu/certs,而是宿主机的 /mailu/certs 映射到容器内 /certs。这一点如果忽略,Mailu 前端就会因为读不到 /certs/cert.pem 和 /certs/key.pem 而直接降级为无 TLS,前端健康检查也会失败。

三、使用 step-ca 为 Mailu 重新签发证书

Mailu 服务器上生成 CSR 的命令如下:

cd /opt/mailu/certsopenssl req -new \
-key /opt/mailu/certs/key.pem \
-out /opt/mailu/certs/mailu.yxwa.info.csr \
-subj "/CN=mailu.yxwa.info"openssl req -in /opt/mailu/certs/mailu.yxwa.info.csr -noout -text | grep -E "Subject:|DNS:"

在 CA 服务器上,先确认 step-ca 正常运行:

export STEPPATH=/opt/step
step path
ss -lnt | grep 8443
curl -vk https://ca.yxwa.info:8443/health

再查看可用 provisioner,并用 admin 进行签发:

step ca provisioner list \
--ca-url https://ca.yxwa.info:8443 \
--root /opt/step/certs/root_ca.crtstep ca sign \
/tmp/mailu-cert/mailu.yxwa.info.csr \
/tmp/mailu-cert/mailu.yxwa.info.crt \
--provisioner admin \
--ca-url https://ca.yxwa.info:8443 \
--root /opt/step/certs/root_ca.crt

签发完成后,可用以下命令核对新证书:

openssl x509 -in /tmp/mailu-cert/mailu.yxwa.info.crt -noout -text | grep -E "Subject:|Issuer:|DNS:"

最终确认结果里,证书主题为 mailu.yxwa.info,签发者为内部中间 CA,这一步已经完成并被实际用于 Mailu。

四、把证书放到 Mailu 能真正读到的位置

证书签出来以后,不要只放在 /opt/mailu/certs。因为当前 compose 挂载的是:

- "/mailu/certs:/certs"

所以 Mailu 真正要读取的是宿主机 /mailu/certs 下的 cert.pem 和 key.pem。当前 cert.pem 中已经包含了两张证书,也就是服务器证书和中间证书,因此证书链本身已经是完整的;浏览器里剩余的“不安全”提示,后续主要靠根证书通过 GPO 下发到域内终端来解决。

实际操作命令如下:

mkdir -p /mailu/certs
cp /opt/mailu/certs/cert.pem /mailu/certs/
cp /opt/mailu/certs/key.pem /mailu/certs/
ls -l /mailu/certs

如果想确认当前 cert.pem 里是否已经带上链,可以直接检查:

grep -c "BEGIN CERTIFICATE" /mailu/certs/cert.pem

如果输出是2,通常就表示当前文件里至少已经包含服务器证书和中间证书。实际验证中,这个结果就是2。

同时,证书与私钥的匹配关系也已经通过 modulus 校验确认无误。

openssl x509 -noout -modulus -in /opt/mailu/certs/cert.pem | openssl md5
openssl rsa -noout -modulus -in /opt/mailu/certs/key.pem | openssl md5

五、启动 Mailu,并解决镜像拉取问题

Mailu 启动本身只需要:

cd /opt/mailu
docker compose up -d

但这次实际部署时,内网服务器访问 docker.io 与 ghcr.io 都不够稳定,所以需要分两步处理。先是 Docker Hub 镜像通过镜像源解决,随后发现 Mailu 核心镜像来自 ghcr.io/mailu/...:2024.06,最终采用的是“在外网机器预拉镜像并打包,再导入内网 Mailu 服务器”的方式。根据当前 compose 文件,这一套至少需要以下镜像,这些镜像名和版本都来自最终实际使用的 compose 配置。

外网机器上打包镜像:

docker save -o mailu-2024.06-bundle.tar \
redis:alpine \
ghcr.io/mailu/nginx:2024.06 \
ghcr.io/mailu/unbound:2024.06 \
ghcr.io/mailu/admin:2024.06 \
ghcr.io/mailu/dovecot:2024.06 \
ghcr.io/mailu/postfix:2024.06 \
ghcr.io/mailu/oletools:2024.06 \
ghcr.io/mailu/rspamd:2024.06 \
ghcr.io/mailu/webmail:2024.06

内网服务器导入:

gunzip /home/yxwa/mailu-2024.06-bundle.tar.gz
docker load -i /home/yxwa/mailu-2024.06-bundle.tar
docker images | grep -E 'mailu|redis'

最终这些镜像都已经成功导入,之后 docker compose up -d 即可顺利把所有核心容器拉起。

六、验证容器状态与前端健康

Mailu 启动后,最简单的检查方式就是:

docker ps

最终运行状态中,postfix、webmail、dovecot等软件全部已经进入运行状态,并且后续都达到了 healthy。说明 Mailu 主体部署已经完成,而不是只停留在“容器能启动”。

前端的 25、80、110、143、443、465、587、993、995、4190 等端口也全部绑定到了 192.168.1.30 上。

前端如果异常,优先看:

docker logs --tail=100 mailu-front-1

当前这次部署里,前端健康问题的根因就是证书目录挂载路径不一致,修正后重启 mailu-front-1,再等待健康检查完成即可恢复正常。

docker restart mailu-front-1
docker ps

最终结果里,mailu-front-1 也已经从 unhealthy 变成了 healthy,Web 入口恢复正常。

七、验证 Web 入口并创建管理员账号

当前 Web 入口已经能正常访问。直接访问 https://192.168.1.30 会返回 301,跳转到 /webmail/?homepage,说明前端反向代理与 Webmail 入口已可用。

使用 https://mailu.yxwa.info 访问时,页面也已经能正常打开。浏览器显示不安全主要与域内终端是否信任内部 CA 有关,不影响Mailu 主体部署完成判断。

curl -kI https://192.168.1.30

管理员账号这次已经创建并且登录成功,管理员地址为:

admin@ad.yxwa.info

登录后台后已经能看到用户设置与管理菜单,这一步意味着 Mailu 的管理侧已经实际可用,而不仅仅是页面能打开。最终就 3.4 而言,Mailu 已部署完成、证书已接入、DNS 已对接、管理员账号已创建并验证登录成功,因此这项任务可以判定为完成。

八、注意事项

目前重点不是公网收发,而是先把邮件系统、域名解析、内部证书与管理界面建立起来。当前浏览器还提示不安全并不代表不完成,它更属于后续的工作:把 root_ca.crt 和中间证书通过 GPO 推到域内终端,让 Windows 客户端自动信任 step-ca 签发的站点证书。

此作者没有提供个人介绍。
最后更新于 2026-04-02