邮件服务器
Postfix 和 Dovecot 配置 TLS 证书(Let's Encrypt)
Postfix 和 Dovecot 可以共用一张 mail.example.com 的 Let's Encrypt 证书:Postfix 在 25 端口提供 STARTTLS、在 587 和 465 提交端口提供 TLS,Dovecot 在 993 端口提供 IMAPS。Certbot 续期后由部署钩子重载两个服务。适用于 Debian、Ubuntu、RHEL 系发行版上的 Postfix 3.4 及以上、Dovecot 2.3 或 2.4,Certbot 4.1 及以上。
前提条件
邮件服务器需要一张覆盖客户端和其他服务器连接所用域名的证书。
- Postfix 和 Dovecot 已安装,不开 TLS 时能正常工作,提交端口通过 Dovecot 做 SASL 认证。
- DNS:
mail.example.com的 A/AAAA 记录(例如203.0.113.10),域名的 MX 指向它,并配置对应的反向解析(PTR)。 - 安全组和防火墙放行入方向 TCP 25、465、587、993;HTTP-01 验证还需要 80 端口(不通就用 DNS-01)。
- 已用 snap 安装 Certbot:
sudo snap install --classic certbot && sudo ln -s /snap/bin/certbot /usr/local/bin/certbot。
第一步:为邮件主机名申请证书
证书签给邮件主机名 mail.example.com。80 端口没有被占用时用 standalone 模式:
sudo certbot certonly --standalone -d mail.example.com
同一台机器上已有 Web 服务占用 80 端口的,改用 --webroot -w /var/www/html 或 --nginx。证书在 /etc/letsencrypt/live/mail.example.com/。
第二步:配置 Postfix
Postfix 用 smtpd_tls_chain_files 同时指定私钥和证书链,私钥写在前面。设置证书、25 端口的机会性 TLS 和日志:
sudo postconf -e 'smtpd_tls_chain_files = /etc/letsencrypt/live/mail.example.com/privkey.pem, /etc/letsencrypt/live/mail.example.com/fullchain.pem'
sudo postconf -e 'smtpd_tls_security_level = may'
sudo postconf -e 'smtpd_tls_mandatory_protocols = >=TLSv1.2'
sudo postconf -e 'smtp_tls_security_level = may'
sudo postconf -e 'smtpd_tls_loglevel = 1'
sudo postconf -e 'smtp_tls_loglevel = 1'
在 /etc/postfix/master.cf 里开启 submission(587,STARTTLS)和 submissions(465,直接 TLS):
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
submissions inet n - y - - smtpd
-o syslog_name=postfix/submissions
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
sudo postfix check && sudo systemctl reload postfix
第三步:配置 Dovecot
Dovecot 2.3 和 2.4 的证书配置项名称不同。Dovecot 2.3(Ubuntu 24.04、Debian 12)编辑 /etc/dovecot/conf.d/10-ssl.conf:
ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_min_protocol = TLSv1.2
Dovecot 2.4(例如 Debian 13)改了配置项名称,直接写路径:
ssl = required
ssl_server_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_server_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
用 dovecot --version 确认版本,然后检查并重载:
sudo doveconf -n | grep -E '^ssl'
sudo systemctl reload dovecot
第四步:续期后重载两个服务
Postfix 和 Dovecot 不会自己读取续期后的证书,要靠 Certbot 的部署钩子重载:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/mail.sh > /dev/null <<'EOF'
#!/bin/sh
case "$RENEWED_DOMAINS" in
*mail.example.com*) systemctl reload postfix dovecot ;;
esac
EOF
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/mail.sh
发布了 DANE TLSA 记录的,用 --reuse-key 重新签发一次,以后续期就会保留同一把私钥:sudo certbot certonly --standalone -d mail.example.com --reuse-key --force-renewal。
验证
配置正确时,25、587、465、993 四个端口都返回 mail.example.com 的 Let's Encrypt 证书,OpenSSL 显示 Verify return code: 0 (ok)。从另一台机器逐个端口测试,SMTP 可以用 STARTTLS 检查,也可以用 OpenSSL:
openssl s_client -connect mail.example.com:25 -starttls smtp -servername mail.example.com </dev/null | head -n 20
openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com </dev/null | grep -E 'subject|issuer|Verify'
openssl s_client -connect mail.example.com:465 -servername mail.example.com </dev/null | grep -E 'subject|issuer|Verify'
openssl s_client -connect mail.example.com:993 -servername mail.example.com </dev/null | grep -E 'subject|issuer|Verify'
这台主机同时提供 HTTPS 的话,SSL 证书检查 或 curl ssl.sh/mail.example.com 能看到同一张证书的证书链和到期时间。
自动续期
snap 版 Certbot 每天两次运行 certbot renew,4.1 及以上按 ARI 建议时间续期,之后部署钩子重载 Postfix 和 Dovecot。standalone 续期时 80 端口必须空闲;以后加了 Web 服务,就用 sudo certbot reconfigure --cert-name mail.example.com --webroot -w /var/www/html 改成 webroot。
sudo certbot renew --dry-run --run-deploy-hooks
邮件客户端不会缓存证书,Let's Encrypt 有效期缩短(2027 年 2 月 10 日起 64 天)不需要改配置。把 mail.example.com 加到 ssl.sh 监控(开发中)。
常见问题排查
- warning: cannot get RSA private key from file:
smtpd_tls_chain_files顺序错了,先写私钥,再写证书链。 - Dovecot 报 ssl_cert: Can't open file:2.3 漏了
<前缀,或在 2.4 上用了旧配置项名称,用doveconf -n查看实际生效的配置。 - 客户端提示证书链不受信任或不完整:用了
cert.pem,改成fullchain.pem,见 缺少中间证书。 - 邮件客户端提示证书域名不匹配:客户端连接的域名(比如主域名)不在证书里,见 NET::ERR_CERT_COMMON_NAME_INVALID。
- 对方服务器拒绝你的邮件服务器出示的客户端证书:Let's Encrypt 证书已不含客户端认证 EKU,见 客户端认证 EKU 被移除。
常见问题
25 端口要强制 TLS 吗?
不要。公网 MX 服务器必须能接收不支持 TLS 的发件方,25 端口用 smtpd_tls_security_level = may,只在用户登录发信的 587、465 端口强制 TLS。想让其他服务器投递给你时强制加密,用 MTA-STS 或 DANE。
国内云服务器 25 端口发不出信怎么办?
阿里云、腾讯云等国内云服务器默认封禁 25 端口出方向,能否解封以云厂商的规定为准。证书配置不受影响:收信(入方向 25)、用户提交(587/465)和 IMAP(993)都可以正常配置 TLS,外发邮件可以通过云厂商或第三方的邮件推送服务中继。
续期会让 DANE 失效吗?
可能会。3 1 1 类型的 TLSA 记录固定的是公钥,而 Certbot 默认每次续期都生成新私钥。给邮件证书加 --reuse-key,或在换密钥前提前发布新公钥的 TLSA 记录。TLSA 生成器 可以生成记录。