X.509 证书与 TLS 证书选择标准

邮件系统的 TLS 证书选择直接影响邮件传输安全性和客户端兼容性。以下对照表说明各邮件服务组件的证书需求。

表1:邮件服务 TLS 证书需求对照
服务类型协议端口证书主体说明
SMTP MTA→MTASTARTTLS25mx.example.com需 MX 域名在证书 SAN 中
MUA 提交→MSASTARTTLS / TLS587 / 465smtp.example.com使用提交专用证书
IMAP/POP3 MUA→MDDTLS / STARTTLS993 / 143mail.example.com使用邮件客户端域名证书,SAN 需包含所有主机名

RFC 3207 定义了 SMTP STARTTLS 扩展,邮件服务器间的加密传输依赖 X.509 PKI 体系。RFC 7817 更新了邮件协议的 TLS 服务器身份验证规则,强调 SAN(Subject Alternative Name)匹配逻辑。邮件服务应优先选择 由受信任公共 CA 签发的证书,内网环境可使用私有 CA,但需在所有客户端正确部署根证书。

Let's Encrypt 是由 Internet Security Research Group(ISRG)运营的免费自动化 CA 服务,采用 ACME 协议实现证书的自动申请和续期。其证书有效期为 90 天,通过 certbot 等客户端可完全自动化管理流程。

Let's Encrypt + certbot 自动化续期

2.1 配置 certbot 与 DNS-01 挑战

邮件服务器通常无法对外暴露 80/443 端口提供 HTTP 挑战验证,因此推荐使用 DNS-01 挑战方式。DNS-01 通过配置域名 TXT 记录完成域名所有权验证,无需开放入站端口,适合邮件服务器场景。以下示例使用 nsupdate(RFC 2136)动态更新 DNS 记录。

# Debian / Ubuntu
apt install certbot python3-certbot-dns-rfc2136

# RHEL / Rocky / Alma
dnf install certbot python3-certbot-dns-rfc2136

2.2 配置 NSupdate 凭证

在 DNS 服务器上使用 dnssec-keygen 生成 TSIG 共享密钥,用于认证 certbot 的 DNS 更新请求。

# 在 DNS 服务器生成 TSIG 密钥
dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST letsencrypt-update

# 配置 named.conf
cat >> /etc/bind/named.conf.local << 'KEYEOF'
key "letsencrypt-update" {
  algorithm hmac-sha256;
  secret "BASE64_ENCODED_SECRET_HERE";
};
zone "example.com" {
  type master;
  file "/etc/bind/db.example.com";
  allow-update { key "letsencrypt-update"; };
};
KEYEOF

创建 certbot 的 RFC 2136 凭证文件:

cat > /etc/letsencrypt/nsupdate.ini << 'INIEOF'
dns_rfc2136_server = ns1.example.com
dns_rfc2136_port = 53
dns_rfc2136_name = letsencrypt-update
dns_rfc2136_secret = BASE64_ENCODED_SECRET_HERE
dns_rfc2136_algorithm = HMAC-SHA256
INIEOF
chmod 600 /etc/letsencrypt/nsupdate.ini

2.3 签发通配符证书

certbot certonly   --dns-rfc2136   --dns-rfc2136-credentials /etc/letsencrypt/nsupdate.ini   -d "*.example.com" -d "example.com"   --non-interactive --agree-tos   --email admin@example.com

签发成功后,证书文件位于 /etc/letsencrypt/live/example.com/ 目录下,各文件用途如下:

表2:Let's Encrypt 证书文件
文件说明权限
fullchain.pem服务器证书 + 中间 CA 链644
privkey.pem私钥600(仅 root)
cert.pem仅服务器证书(不含 CA 链)644
chain.pem中间 CA 证书链644

2.4 续期部署 hook 脚本

Let's Encrypt 证书有效期为 90 天,certbot 会通过 systemd timer 每日自动检查续期。使用 deploy hook 可以在证书成功续期后自动重启邮件服务加载新证书。

cat > /etc/letsencrypt/renewal-hooks/deploy/reload-mail-services.sh << 'SH'
#!/bin/bash
# certbot deploy hook - 续期后自动重启邮件服务
set -e

DOMAINS=$(echo "$RENEWED_DOMAINS" | tr ',' ' ')
logger -t certbot "Certificate renewed for domains: $DOMAINS"

# Postfix SMTP TLS
if systemctl is-active --quiet postfix; then
  postfix reload
  logger -t certbot "Postfix reloaded"
fi

# Dovecot IMAP/POP3 TLS
if systemctl is-active --quiet dovecot; then
  dovecot reload
  logger -t certbot "Dovecot reloaded"
fi

# Nginx (托管 MTA-STS 等 Web 服务)
if systemctl is-active --quiet nginx; then
  nginx -s reload
  logger -t certbot "Nginx reloaded"
fi
SH
chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-mail-services.sh

验证续期配置:

# 测试续期流程(含 deploy hook 执行)
certbot renew --dry-run

# 查看续期日志
journalctl -u certbot.timer --since "7 days ago"

# 手动检查证书到期时间
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem   -noout -enddate

Postfix/Dovecot 证书配置

3.1 Postfix 配置

Postfix 使用 smtpd_tls_* 参数配置入站 SMTP TLS,smtp_tls_* 参数配置出站 SMTP TLS。推荐使用 Let's Encrypt 的 symlink 路径,certbot 续期后自动更新。

# /etc/postfix/main.cf 配置 TLS 证书路径
# 入站 SMTP(smtpd)
smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/example.com/privkey.pem
smtpd_tls_security_level = may

# 出站 SMTP(smtp)
smtp_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem
smtp_tls_key_file  = /etc/letsencrypt/live/example.com/privkey.pem
smtp_tls_security_level = may

# 提交端口 587 使用主证书,submission 服务继承 main.cf 配置
# 如需 465 独立证书:取消注释以下行
# smtpd_tls_cert_file = /etc/letsencrypt/live/smtp.example.com/fullchain.pem

权限注意:Postfix 以 postfix 用户运行,需确保可读取 /etc/letsencrypt/archive/ 下的私钥文件。将 postfix 用户加入 ssl-cert 组。

usermod -aG ssl-cert postfix
# 设置 /etc/letsencrypt/ 目录访问权限
chmod g+rx /etc/letsencrypt/live /etc/letsencrypt/archive
chmod 640 /etc/letsencrypt/archive/example.com/privkey*.pem

3.2 Dovecot 配置

# /etc/dovecot/conf.d/10-ssl.conf
ssl = required
ssl_cert = 

3.3 独立 SMTPS(465)证书配置

RFC 8314 推荐使用 587 STARTTLS 而非 465 隐式 TLS,但 465 端口仍然被广泛使用。Postfix 通过 master.cf 配置 smtps 服务实现 465 端口服务,可为 465 端口配置独立证书以支持不同域名。

# /etc/postfix/master.cf
smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  # 可选独立证书路径(覆盖主配置)
  -o smtpd_tls_cert_file=/etc/letsencrypt/live/smtp.example.com/fullchain.pem
  -o smtpd_tls_key_file=/etc/letsencrypt/live/smtp.example.com/privkey.pem

OCSP Stapling 配置(RFC 6066)

4.1 OCSP Stapling 原理

OCSP(Online Certificate Status Protocol)是 X.509 PKI 体系中用于实时查询证书吊销状态的协议。当邮件客户端(MUA)或邮件服务器(MTA)验证对方证书时,传统方式需要主动向 CA 的 OCSP 响应器发起查询,存在以下问题:

  • 隐私泄露:CA 可获知用户的访问时间和目标域名
  • 性能开销:额外的 HTTP 查询增加 TLS 握手延迟
  • 单点故障:若 CA 的 OCSP 响应器不可用,客户端通常采取 soft-fail 策略忽略验证结果

OCSP Stapling(RFC 6066,TLS Certificate Status Request Extension)解决了上述问题:服务器定期从 CA 获取 OCSP 响应并缓存在本地,在 TLS 握手阶段将响应一并发送给客户端,客户端无需额外查询。Nginx 从 1.3.7 版本开始支持 OCSP Stapling,Apache httpd 从 2.3.3 版本开始支持。

4.2 Nginx 配置 OCSP Stapling

以下 nginx 配置展示 MTA-STS Web 服务器的 OCSP Stapling 配置:

# /etc/nginx/conf.d/mta-sts-ocsp.conf
server {
    listen 443 ssl http2;
    server_name mta-sts.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    resolver 8.8.8.8 1.1.1.1 valid=300s;
    resolver_timeout 5s;

    # 其他配置省略...
}

验证 OCSP Stapling 是否正常工作:

# 使用 openssl 检查 OCSP response 状态
openssl s_client -connect mta-sts.example.com:443 -tlsextdebug   -status 2>&1 | grep -i "OCSP response"

# 预期输出:
# OCSP response:
# OCSP Response Data:
#     OCSP Response Status: successful (0x0)
#     Response Type: Basic OCSP Response

# 查看 OCSP response 缓存状态(nginx 调试)
tail -f /var/log/nginx/error.log | grep -i ocsp

4.3 Postfix 配置 OCSP Stapling

Postfix 从 3.1 版本开始支持 OCSP Stapling,通过 smtpd_tls_staple 参数启用。以下配置展示 Postfix 的 OCSP Stapling 与外部 OCSP 响应缓存方案。

# /etc/postfix/main.cf
# 启用 OCSP Stapling
smtpd_tls_staple = yes

# OCSP 响应缓存文件
smtpd_tls_staple_file = /var/spool/postfix/ocsp-cache

# 信任链文件(用于 OCSP 验证)
smtpd_tls_trust_chain_file = /etc/letsencrypt/live/example.com/chain.pem

创建 systemd timer 定时刷新 OCSP 响应:

cat > /usr/local/bin/refresh-ocsp-response.sh << 'SH'
#!/bin/bash
# 刷新 OCSP 响应缓存供 Postfix 使用
DOMAIN="example.com"
CERT="/etc/letsencrypt/live/${DOMAIN}/cert.pem"
CHAIN="/etc/letsencrypt/live/${DOMAIN}/chain.pem"
PRIVKEY="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"

# 从 cert.pem 提取 OCSP 响应器地址
OCSP_URL=$(openssl x509 -in "$CERT" -noout -ocsp_uri)

# 发起 OCSP 请求并缓存响应
openssl ocsp -issuer "$CHAIN" -cert "$CERT"   -url "$OCSP_URL" -header "Host" "$(echo $OCSP_URL | sed 's|https\?://||;s|/.*||')"   -respout /var/spool/postfix/ocsp-cache/example.com.ocsp   -noverify 2>/dev/null

# Postfix 用户需可读取 response 文件,权限设为 644
chmod 644 /var/spool/postfix/ocsp-cache/example.com.ocsp
postfix reload
SH
chmod 755 /usr/local/bin/refresh-ocsp-response.sh

# 每 8 小时刷新一次
cat > /etc/systemd/system/refresh-ocsp.service << 'UNIT'
[Unit]
Description=Refresh OCSP stapling response for Postfix
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/refresh-ocsp-response.sh
UNIT
cat > /etc/systemd/system/refresh-ocsp.timer << 'TIMER'
[Unit]
Description=Refresh OCSP response every 8 hours

[Timer]
OnCalendar=*-*-* 00,08,16:00:00
Persistent=true

[Install]
WantedBy=timers.target
TIMER
systemctl daemon-reload
systemctl enable --now refresh-ocsp.timer

验证 Postfix OCSP Stapling 是否生效:

# 查看 Postfix 日志中的 OCSP 状态
grep 'tls_staple' /var/log/mail.log

# 使用 openssl 检查 SMTP 会话中的 OCSP 响应
openssl s_client -starttls smtp -connect mx.example.com:25   -servername mx.example.com -tlsextdebug -status 2>&1 |   grep -E "OCSP|TLS server extension"

Certificate Transparency 监控

5.1 CT 日志工作原理

Certificate Transparency(RFC 6962)是一种证书审计框架,要求 CA 将所有签发的证书提交到公开的 CT 日志服务器。这些日志基于 Merkle 树哈希结构,确保日志不可篡改且可公开审计。邮件管理员应定期监控 CT 日志以发现潜在的证书滥用。

  • 发现未授权证书:及时发现他人为你的域名签发的未授权证书
  • 证书即将过期提醒:在证书到期前 30 天主动告警
  • 合规审计需求:满足安全合规对证书生命周期的审计要求

5.2 使用 certspotter 监控

以下示例使用 certspotter(SSLMate 开发的 CT 日志监控工具)进行证书监控:

# 安装 certspotter(Go 环境)
go install software.sslmate.com/src/certspotter/cmd/certspotter@latest

# 创建监控配置
mkdir -p /etc/certspotter
cat > /etc/certspotter/config.yaml << 'YAML'
# 需要监控的域名
domains:
  - example.com
  - mail.example.com
  - mx.example.com

# CT 日志服务器地址(Google、DigiCert、Sectigo 等)
logs:
  - "https://ct.googleapis.com/logs/argon2022"
  - "https://ct.googleapis.com/logs/argon2023"
  - "https://ct.cloudflare.com/logs/nimbus2024"
  - "https://ct.sectigo.com/logs/2024"

# 告警通知方式(webhook 或邮件)
webhook: "https://alertmanager.example.com/api/v1/alerts"
YAML

# 首次运行建立基准
certspotter -config /etc/certspotter/config.yaml --first-run

# 添加到定时任务(每 6 小时)
echo "0 */6 * * * certspotter -config /etc/certspotter/config.yaml --report-new >> /var/log/certspotter.log 2>&1"   > /etc/cron.d/certspotter

5.3 使用 crt.sh API 监控

crt.sh 提供公开的 CT 日志查询接口,可通过 API 编程方式批量检索证书信息:

# 查询 example.com 的所有已签发证书
curl -s 'https://crt.sh/?q=%.example.com&output=json' |   jq '.[] | {issuer: .issuer_name, not_before: .not_before, not_after: .not_after, fingerprint: .fingerprint}' |   head -20

# 每日差异比对脚本
cat > /usr/local/bin/ct-daily-monitor.sh << 'SH'
#!/bin/bash
DOMAIN="example.com"
LAST_KNOWN="/var/lib/ct-monitor/last_known.txt"
DAILY_REPORT="/tmp/ct_new_certs_${DOMAIN}.txt"

mkdir -p /var/lib/ct-monitor

# 获取当前所有证书指纹
curl -s "https://crt.sh/?q=%.${DOMAIN}&output=json" |   jq -r '.[].fingerprint' | sort > /tmp/ct_current.txt

# 与上次记录比对
if [ -f "$LAST_KNOWN" ]; then
  comm -13 "$LAST_KNOWN" /tmp/ct_current.txt > "$DAILY_REPORT"
  if [ -s "$DAILY_REPORT" ]; then
    mail -s "[ALERT] 发现 ${DOMAIN} 新证书" admin@example.com < "$DAILY_REPORT"
  fi
fi

cp /tmp/ct_current.txt "$LAST_KNOWN"
SH
chmod 755 /usr/local/bin/ct-daily-monitor.sh

# 添加到 cron
echo "0 8 * * * /usr/local/bin/ct-daily-monitor.sh" > /etc/cron.d/ct-daily-monitor

证书吊销与过期监控策略

6.1 证书状态验证方式对比

表3:证书状态验证方式对比
方案标准实时性隐私保护推荐度
CRLX.509 CRL Distribution Points依赖CA定期发布CRL基线方案
OCSPRFC 2560 / RFC 5019实时查询响应器高(依赖网络)
OCSP StaplingRFC 6066 / RFC 6961服务器缓存OCSP响应强烈推荐
CRLiteRFC 9331压缩合并CRL全量数据Firefox 集成

6.2 证书吊销操作

当证书私钥泄露或域名不再使用时,应立即吊销证书。以下为 Let's Encrypt 证书吊销操作示例:

# 吊销证书(指定证书文件)
certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem   --reason keycompromise

# 已吊销的证书仍需保留 revoked.pem 用于 OCSP 验证
certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem   --reason keycompromise --key-path /etc/letsencrypt/live/example.com/privkey.pem

# 吊销后重新签发新证书
certbot certonly --dns-rfc2136 -d "*.example.com" -d "example.com"

6.3 过期自动告警

cat > /usr/local/bin/cert-expiry-warning.sh << 'SH'
#!/bin/bash
# 监控所有 Let's Encrypt 证书到期时间,14 天预警
WARN_DAYS=14
CRIT_DAYS=3
ADMIN="admin@example.com"

for cert_dir in /etc/letsencrypt/live/*/; do
  cert="${cert_dir}fullchain.pem"
  [ -f "$cert" ] || continue

  expiry=$(openssl x509 -in "$cert" -noout -enddate | cut -d= -f2)
  expiry_epoch=$(date -d "$expiry" +%s)
  now_epoch=$(date +%s)
  days_left=$(( (expiry_epoch - now_epoch) / 86400 ))

  domain=$(basename "$cert_dir")

  if [ "$days_left" -le 0 ]; then
    echo "CRITICAL: Certificate for $domain EXPIRED on $expiry"
    echo "紧急:请立即执行 certbot renew --force-renewal" |       mail -s "[CRITICAL] TLS 证书已过期 - $domain" "$ADMIN"
  elif [ "$days_left" -le "$CRIT_DAYS" ]; then
    echo "WARNING: Certificate for $domain expires in $days_left days ($expiry)"
    echo "域名 $domain 的证书将在 $days_left 天后过期,请尽快处理" |       mail -s "[WARNING] TLS 证书即将过期 - $domain" "$ADMIN"
  elif [ "$days_left" -le "$WARN_DAYS" ]; then
    echo "INFO: Certificate for $domain expires in $days_left days ($expiry)"
  fi
done
SH
chmod 755 /usr/local/bin/cert-expiry-warning.sh

# 每天 9:00 执行检查
echo "0 9 * * * /usr/local/bin/cert-expiry-warning.sh" > /etc/cron.d/cert-expiry-warning

多MX节点证书同步

邮件系统部署多个 MX 节点时,所有节点需共享相同的 TLS 证书配置。推荐使用 主从同步集中管理方案。

7.1 主从同步方案

将主 MX 节点的证书目录通过 rsync 同步到备用 MX 节点:

# 主 MX 节点上的证书同步脚本
# 使用 rsync 将证书同步到备用 MX
rsync -avz --delete   /etc/letsencrypt/live/example.com/   mx1.example.com:/etc/letsencrypt/live/example.com/

# 备用 MX 续期触发(主节点统一管理)
ssh mx1.example.com "certbot renew --post-hook 'postfix reload && dovecot reload'"

7.2 使用 Ansible 管理 MX 集群

推荐使用自动化配置管理工具统一管理所有 MX 节点的证书部署:

# ansible/playbooks/tls-cert-deploy.yml
---
- name: Deploy TLS certificates to all MX hosts
  hosts: mail_servers
  tasks:
    - name: Sync certificate from primary to secondary
      synchronize:
        src: /etc/letsencrypt/live/example.com/
        dest: /etc/letsencrypt/live/example.com/
        rsync_opts:
          - "--chown=root:ssl-cert"
          - "--chmod=D2755,F640"
      when: inventory_hostname != 'mx1'

    - name: Restart Postfix
      systemd:
        name: postfix
        state: reloaded

    - name: Restart Dovecot
      systemd:
        name: dovecot
        state: reloaded

    - name: Restart Nginx
      systemd:
        name: nginx
        state: reloaded

注意:通配符证书会简化多 MX 节点的证书管理。若 MX 节点使用不同的主机名(如 mx1.example.commx2.example.com),使用通配符证书 *.example.com 可在所有节点共用同一份证书,但需确保所有 MX 主机名均被 SAN 覆盖。

参考文献

  1. RFC 6066 - Transport Layer Security (TLS) Extensions: Extension Definitions (OCSP Stapling). IETF, January 2011.
  2. RFC 6962 - Certificate Transparency. IETF, June 2013.
  3. RFC 8314 - Cleartext Considered Obsolete: Use of Transport Layer Security (TLS) for Email Submission and Access. IETF, February 2018.
  4. RFC 3207 - SMTP Service Extension for Secure SMTP over Transport Layer Security. IETF, February 2002.
  5. RFC 7817 - Updated Transport Layer Security (TLS) Server Identity Check Procedure for Email Related Protocols. IETF, March 2016.
  6. RFC 7525 - Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS). IETF, May 2015.
  7. Let's Encrypt - ACME Protocol. https://letsencrypt.org/how-it-works/
  8. SSLMate - CertSpotter CT Log Monitor. https://sslmate.com/certspotter/
  9. NIST SP 800-177 Rev. 1 - Trustworthy Email, Section 5.4: Certificate Management.
  10. M3AAWG - Email TLS Certificate Management Best Practices (2025 Update).

引用本文

ztpop.net 知识库编辑. "邮件 TLS 证书配置最佳实践:STARTTLS 证书管理、证书链优化与自动续期" ztpop.net 知识库.

本站技术文章采用 CC-BY 4.0 许可,可自由引用,仅需标注来源 ztpop.net。