本教程将向您展示如何在 Qmail 上安装 SSL/TLS 证书,Qmail 是最初由 D. J. Bernstein 编写的邮件传输代理。安装流程和 Qmail 多年来一直使用的方式相同:构建一个包含私钥、服务器证书和中间证书链的单个 PEM 文件,放置在 /var/qmail/control/servercert.pem,然后重启 Qmail。
开始之前:Qmail 是一个传统 MTA
Qmail 本身自 1998 年最初的 qmail-1.03 版本发布以来就没有更新过。D. J. Bernstein 在 2007 年将代码归入公共领域,但没有恢复开发。知名的社区分支 netqmail 于 2007 年发布了最终的 1.06 版本,此后十多年未再维护。活跃的现代继任者是 notqmail(当前版本 1.09,2024 年 5 月发布),它延续了错误修复和现代系统兼容性改进,同时保持与原版的即插即用兼容性。
对本指南同样重要的是:原生 qmail 并不支持 SSL/TLS。TLS 是通过以下两种第三方方式之一添加的:
- 由 Frederik Vermeulen 编写的 qmail-tls 补丁,为 qmail-smtpd 添加了 STARTTLS 支持,并从硬编码路径读取证书:/var/qmail/control/servercert.pem。大多数现代 qmail 软件包(包括 netqmail 和 notqmail 构建版本、s/qmail 发行版以及许多供应商软件包)都自带了此补丁或等效功能。
- 在 qmail-smtpd 前面使用 stunnel,在 465 端口(SMTPS)上用 TLS 封装连接。由于 TLS 终止发生在 Qmail 之外,因此这种方式适用于任何 qmail 构建版本。
如果您维护的是现有的 qmail 部署,以下步骤仍然适用。如果您正在从头搭建新的邮件服务器,请考虑改用 Postfix 或 OpenSMTPD:两者都拥有原生且维护良好的 TLS 支持以及活跃的安全团队。我们还有一份关于如何在 Postfix 上安装 SSL 证书的单独教程。
为 Qmail 生成 CSR 代码
如果您已经生成了 CSR 并从 CA 处收到了 SSL 文件,请直接跳到在 Qmail 上安装 SSL 证书部分。
CSR 代表证书签名请求(Certificate Signing Request):一段包含您域名和组织信息的编码文本。您需要将其提交给证书颁发机构(CA),以便对方验证您的请求并颁发证书。生成 CSR 的同时也会创建对应的私钥,该私钥保存在您的服务器上,并且是安装过程所必需的。您有两种方式:
- 使用我们的CSR 生成器自动创建 CSR。
- 按照我们的分步教程操作:如何为 Qmail 生成 CSR。
用任意纯文本编辑器打开 CSR 文件,将整个代码块(包括 BEGIN CERTIFICATE REQUEST 和 END CERTIFICATE REQUEST 这两行)复制到订单表单的 CSR 字段中。请妥善保存私钥文件:您将在下一节中把它合并到 PEM 文件里。
在 Qmail 上安装 SSL 证书
验证完成后,CA 会通过邮件发送一个 ZIP 压缩包,其中包含您的 SSL 文件:主(服务器)证书和一个或多个中间(CA)证书。将压缩包解压到您的服务器上,然后按照以下步骤操作。
第 1 步:将密钥、证书和证书链合并为一个 PEM 文件
qmail-tls 补丁(以及 netqmail、notqmail 和 s/qmail 中的等效实现)从一个 PEM 文件中读取终止 TLS 所需的全部内容。请按以下顺序拼接文件:先是您的私钥,然后是您的服务器证书,最后是中间(CA)证书链。不需要包含根证书(客户端已经信任它)。在存放解压文件的目录下运行:
cat yourdomain.key yourdomain.crt yourdomain.ca-bundle > servercert.pem
如果您的 CA 提供的中间证书是多个单独文件而不是一个 .ca-bundle 文件,请在服务器证书之后依次列出每一个,末端证书在前,根签发的中间证书在后:
cat yourdomain.key yourdomain.crt intermediate1.crt intermediate2.crt > servercert.pem
在文本编辑器中打开生成的文件以确认结构。它应该看起来像这样,每个代码块周围都有真正的五连字符标记:
-----BEGIN RSA PRIVATE KEY-----
(the text of your private key)
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
(the text of your primary SSL certificate)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(the text of the intermediate CA certificate)
-----END CERTIFICATE-----
较新的密钥可能标记为 BEGIN PRIVATE KEY 而不是 BEGIN RSA PRIVATE KEY;两者都可以。密钥不能使用密码短语加密,因为 qmail-smtpd 在启动时会直接加载它,不会提示输入密码。如果您的 CA 颁发的是 ECDSA 证书,密钥块将是 BEGIN EC PRIVATE KEY;其他部分不变。
第 2 步:将文件移动到 Qmail 的控制目录
文件名和路径由 qmail-tls 补丁固定。将合并后的 PEM 文件移动到 Qmail 的控制目录中,并收紧其权限,因为它包含您的私钥:
sudo mv servercert.pem /var/qmail/control/servercert.pem
sudo chmod 600 /var/qmail/control/servercert.pem
sudo chown root:qmail /var/qmail/control/servercert.pem
所有者和组取决于您系统上运行 qmail-smtpd 的用户是谁。在标准的 netqmail 或 notqmail 安装中,通常是 qmaild 用户或 qmail 组,因此使用 root:qmail 配合 600 模式即可。在基于 vpopmail 的设置中,qmail-smtpd 以 vpopmail 身份运行,因此请使用:
sudo chown vpopmail:vchkpw /var/qmail/control/servercert.pem
sudo chmod 640 /var/qmail/control/servercert.pem
不管哪种方式,目标都是一样的:qmail-smtpd 进程必须能够读取该文件,而其他任何用户都不应有此权限。
第 3 步:针对自定义构建进行调整
大多数 qmail 变体都在默认位置查找证书,但也有少数使用环境变量代替。请根据您的构建版本进行匹配:
- 带有 qmail-tls 补丁的原生 qmail(netqmail / notqmail / 供应商软件包):无需进一步配置。qmail-smtpd 会自动读取 /var/qmail/control/servercert.pem。要让 qmail-smtpd 在 EHLO 响应中通告 STARTTLS,必须在 qmail-smtpd 运行脚本(通常是 /var/qmail/supervise/qmail-smtpd/run)中设置 UCSPITLS 环境变量:在 qmail-smtpd 那一行之前导出 UCSPITLS=””。若要求在认证之前必须加密,还需设置 FORCE_TLS=1。
- 带有 ucspi-ssl 的 s/qmail:证书路径不是硬编码的。请在您的 qmail-smtpd 运行脚本(或对应的 tcpserver / sslserver 包装器)中设置以下环境变量:CERTFILE 用于服务器证书,KEYFILE 用于私钥(两者可以指向同一个合并后的 PEM 文件),以及 CERTCHAINFILE 用于中间证书。有关您所用版本的具体指令名称,请参阅 s/qmail 文档。
- stunnel 前端(465 端口的 SMTPS):将 stunnel 的 cert 指令指向 /var/qmail/control/servercert.pem,并让其 exec 启动 qmail-smtpd。这种方式完全不需要 qmail-tls 补丁;TLS 在 stunnel 处终止。
如果您不确定您的 qmail-smtpd 是用哪个补丁构建的,可以检查二进制文件:
strings /var/qmail/bin/qmail-smtpd | grep -iE 'tls|starttls|servercert'
如果您看到有关 STARTTLS、servercert.pem 或 UCSPITLS 的引用,说明该二进制文件已编译进 qmail-tls 补丁。
第 4 步:重启 Qmail
Qmail 在进程启动时读取证书,因此需要重启 qmail-smtpd。在大多数使用附带的控制脚本的安装环境中:
sudo qmailctl restart
如果您的发行版是将 Qmail 包装成 systemd 服务,请使用:
sudo systemctl restart qmail
在 daemontools / runit 设置中,请改为通过 svc 向 qmail-smtpd 发送 TERM 信号:
sudo svc -t /service/qmail-smtpd
启动过程中请留意日志(通常是 /var/log/qmail/smtpd/current)中的错误。格式错误的 PEM 文件或所有权不正确是 qmail-smtpd 拒绝启用 TLS 的最常见原因。
测试您的 SSL 安装
使用 OpenSSL 确认 Qmail 是否提供 STARTTLS 并展示正确的证书。将 mail.yourdomain.com 替换为您的邮件服务器主机名:
openssl s_client -starttls smtp -connect mail.yourdomain.com:25 -servername mail.yourdomain.com </dev/null
| openssl x509 -noout -issuer -subject -dates
输出应打印出您证书的主体(通用名称)、颁发者(CA)和有效期。如果您改为通过 stunnel 在 465 端口设置了 SMTPS,请去掉 -starttls smtp,直接连接到 465 端口:
openssl s_client -connect mail.yourdomain.com:465 -servername mail.yourdomain.com
在握手过程的末尾查找 Verify return code: 0 (ok)。如果您看到证书链错误,说明中间证书没有包含在 servercert.pem 中:请添加它们并重启 qmail-smtpd。如需查看证书、证书链和协议支持的完整报告,请对您的邮件主机名运行我们的 SSL 检测工具。
常见问题
从文件 /var/qmail/control/servercert.pem 读取。该路径硬编码在为 qmail-smtpd 添加 STARTTLS 支持的 qmail-tls 补丁中,每个常见的 qmail 变体(netqmail、notqmail、供应商软件包)都使用同样的路径。该文件必须依次包含私钥、服务器证书和中间证书链。使用 ucspi-ssl 的 s/qmail 是个例外:它改为读取 CERTFILE、KEYFILE 和 CERTCHAINFILE 环境变量。
先是私钥,然后是服务器(末端)证书,最后是中间 CA 证书。不需要根证书,因为邮件客户端已经信任它。一行命令即可构建:cat yourdomain.key yourdomain.crt yourdomain.ca-bundle > servercert.pem
如果顺序不对,qmail-smtpd 将无法在启动时加载证书,也就不会提供 TLS。如果您怀疑是这个问题,请检查 /var/log/qmail/smtpd/ 下的日志。
不能单独支持。1998 年发布的原始 qmail-1.03 版本没有 SSL 或 TLS 支持。TLS 是通过 qmail-tls 补丁(编译进 qmail-smtpd 二进制文件,在 25 和 587 端口添加 STARTTLS)或在 qmail-smtpd 前面运行 stunnel(用于 465 端口的 SMTPS)来添加的。几乎所有现代 qmail 软件包都已经预先应用了 qmail-tls 补丁,因此您通常不需要自己修补源代码。
原始 Qmail(qmail-1.03)自 1998 年以来就没有更新过。第一个社区分支 netqmail 于 2007 年发布了最后的 1.06 版本。活跃的现代继任者是 notqmail,它在 2024 年 5 月发布了 1.09 版本,并持续提供错误修复和现代系统兼容性补丁。对于新的邮件服务器,大多数运维人员会选择 Postfix 或 OpenSMTPD,两者都拥有原生且积极维护的 TLS 支持。
通常有三个常见原因。第一,qmail-smtpd 运行脚本中没有导出 UCSPITLS 环境变量:请在 qmail-smtpd 那一行之前设置 UCSPITLS=””(值可以为空)并重启服务。第二,qmail-smtpd 二进制文件在构建时没有加入 qmail-tls 补丁:可以用 strings /var/qmail/bin/qmail-smtpd | grep -i tls 来确认。第三,/var/qmail/control/servercert.pem 文件缺失、qmail-smtpd 用户无法读取,或者格式不正确;请检查权限以及 PEM 代码块的顺序。
使用 OpenSSL 连接到 SMTP 端口,读取 Qmail 通过 STARTTLS 展示的证书:openssl s_client -starttls smtp -connect mail.yourdomain.com:25 -servername mail.yourdomain.com </dev/null | openssl x509 -noout -issuer -subject -dates
这会打印出主体、颁发者和有效期。如果是通过 stunnel 在 465 端口实现的 SMTPS,请去掉 -starttls smtp 参数。请确认颁发者与您的 CA 一致,并且握手结果显示 Verify return code: 0 (ok)。


