强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

SMTP 服务器搭建完全指南 / 第 5 章:TLS/SSL 加密配置

第 5 章:TLS/SSL 加密配置

没有 TLS 的 SMTP 就像寄明信片——途经的每个人都能看到内容。


5.1 邮件加密概述

5.1.1 SMTP 加密方式

方式端口说明推荐
STARTTLS25, 587先建立明文连接,再升级为 TLS✅ 推荐
隐式 TLS (SMTPS)465从一开始就建立 TLS 连接✅ 推荐
无加密25明文传输❌ 不推荐

5.1.2 TLS 在邮件系统中的位置

发送方 MUA ──[STARTTLS 587]──► 发送方 MTA ──[STARTTLS 25]──► 接收方 MTA
                              (SASL认证)                     (投递检查)
                                                                    │
                                                              接收方 MDA
                                                                    │
接收方 MUA ◄──[STARTTLS 993]── 接收方 MRA (Dovecot IMAP)

5.1.3 TLS 版本选择

TLS 版本安全性状态
TLS 1.0❌ 已弃用
TLS 1.1❌ 已弃用
TLS 1.2✅ 推荐
TLS 1.3最高✅ 强烈推荐

5.2 获取 TLS 证书

5.2.1 Let’s Encrypt 免费证书(推荐)

# 安装 Certbot
sudo apt install -y certbot

# 申请证书(需要 80 端口可访问)
sudo certbot certonly --standalone \
    -d mail.example.com \
    --agree-tos \
    --email [email protected] \
    --non-interactive

# 证书文件位置
# /etc/letsencrypt/live/mail.example.com/fullchain.pem  (证书链)
# /etc/letsencrypt/live/mail.example.com/privkey.pem    (私钥)
# /etc/letsencrypt/live/mail.example.com/cert.pem       (证书)
# /etc/letsencrypt/live/mail.example.com/chain.pem      (中间证书)

5.2.2 自签名证书(测试环境)

# 生成自签名证书(有效期 365 天)
sudo openssl req -x509 -nodes -days 365 \
    -newkey rsa:2048 \
    -keyout /etc/postfix/certs/mail.key \
    -out /etc/postfix/certs/mail.crt \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=Example/CN=mail.example.com"

# 生成 DH 参数(增强安全性)
sudo openssl dhparam -out /etc/postfix/certs/dhparam.pem 2048

# 设置权限
sudo chmod 600 /etc/postfix/certs/mail.key
sudo chown root:root /etc/postfix/certs/*

5.2.3 商业证书

# 1. 生成 CSR(证书签名请求)
sudo openssl req -new -newkey rsa:2048 -nodes \
    -keyout /etc/postfix/certs/mail.key \
    -out /etc/postfix/certs/mail.csr \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=Company/CN=mail.example.com"

# 2. 将 CSR 提交给 CA(如 DigiCert, GlobalSign)
# 3. 完成域名验证
# 4. 下载证书文件

# 5. 合并证书链
cat mail.crt intermediate.crt root.crt > /etc/postfix/certs/mail-bundle.crt

# 6. 设置权限
sudo chmod 600 /etc/postfix/certs/mail.key

5.3 配置 Postfix TLS

5.3.1 服务器端 TLS 配置(接收邮件)

# /etc/postfix/main.cf — TLS 服务器配置

# ==================== TLS 证书 ====================
# 证书文件路径
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem

# DH 参数文件(可选,增强安全性)
smtpd_tls_dh1024_param_file = /etc/postfix/certs/dhparam.pem

# ==================== TLS 安全级别 ====================
# may: 可选 TLS(STARTTLS 可用但不强制)
# encrypt: 强制 TLS(如果客户端不支持则拒绝连接)
# dane: 使用 DANE 验证
# verify: 验证客户端证书
smtpd_tls_security_level = may

# 端口 587 强制 TLS(在 master.cf 中配置)
# -o smtpd_tls_security_level=encrypt

# ==================== TLS 协议和密码套件 ====================
# 最低 TLS 版本
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1

# 可用 TLS 版本
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1

# 强制密码套件
smtpd_tls_mandatory_ciphers = high

# 可用密码套件
smtpd_tls_ciphers = high

# 排除弱密码
smtpd_tls_exclude_ciphers = aNULL, MD5, DES, 3DES, DES-CBC3-SHA, RC4

# ==================== TLS 会话 ====================
# 启用 TLS 会话缓存
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache

# 会话超时
smtpd_tls_session_cache_timeout = 3600s

# ==================== TLS 日志 ====================
# TLS 日志级别(0-4)
smtpd_tls_loglevel = 1

# 接收端 TLS 安全级别报告
smtpd_tls_received_header = yes

5.3.2 客户端 TLS 配置(发送邮件)

# /etc/postfix/main.cf — TLS 客户端配置

# 客户端证书(用于向目标服务器证明身份)
smtp_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtp_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem

# 客户端 TLS 安全级别
# may: 尝试 STARTTLS,失败则明文
# encrypt: 强制 STARTTLS
# dane: 使用 DANE 验证
# verify: 验证服务器证书
smtp_tls_security_level = dane

# TLS 协议
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1

# TLS 日志
smtp_tls_loglevel = 1

# 安全级别报告
smtp_tls_note_starttls_offer = yes

5.4 强制 TLS

5.4.1 全局强制 TLS

# 强制所有连接使用 TLS(包括端口 25)
smtpd_tls_security_level = encrypt

# ⚠️ 注意:这会拒绝不支持 STARTTLS 的服务器
# 一些老旧的邮件服务器可能不支持 TLS

5.4.2 基于策略的 TLS

# 使用 check_policy_service 实现按域名强制 TLS
# /etc/postfix/tls_policy
# 对特定域名强制 TLS
google.com      secure match=google.com
microsoft.com   secure match=microsoft.com

# 对特定域名使用指纹验证
partner.com     secure match=partner.com fingerprint=AA:BB:CC:...

# 默认策略
*               may

# 在 main.cf 中配置
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy

# 生成数据库
sudo postmap /etc/postfix/tls_policy

5.4.3 端口 587 强制 TLS

# /etc/postfix/master.cf — 提交端口配置

submission inet n - y - - smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

5.5 证书自动续期

5.5.1 Let’s Encrypt 自动续期

# 测试续期
sudo certbot renew --dry-run

# 设置自动续期(certbot 安装时通常已自动配置)
# 检查定时任务
sudo systemctl list-timers | grep certbot

# 手动添加续期后钩子
sudo tee /etc/letsencrypt/renewal-hooks/postfix-reload.sh << 'EOF'
#!/bin/bash
systemctl reload postfix
systemctl reload dovecot
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/postfix-reload.sh

5.5.2 证书过期监控

#!/bin/bash
# check-cert-expiry.sh — 检查证书过期时间

CERT_FILE="/etc/letsencrypt/live/mail.example.com/cert.pem"
WARN_DAYS=30

# 获取过期日期
EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

echo "证书过期时间: $EXPIRY"
echo "剩余天数: $DAYS_LEFT"

if [ $DAYS_LEFT -lt $WARN_DAYS ]; then
    echo "⚠️ 警告:证书将在 $DAYS_LEFT 天后过期!"
    exit 1
fi

echo "✅ 证书状态正常"

5.6 验证 TLS 配置

5.6.1 命令行测试

# 测试 STARTTLS
openssl s_client -starttls smtp -connect mail.example.com:25 -brief

# 测试端口 587
openssl s_client -starttls smtp -connect mail.example.com:587 -brief

# 测试端口 465(隐式 TLS)
openssl s_client -connect mail.example.com:465 -brief

# 查看证书详情
openssl s_client -starttls smtp -connect mail.example.com:25 </dev/null 2>/dev/null | openssl x509 -text -noout

# 验证证书链
openssl s_client -starttls smtp -connect mail.example.com:25 -verify_return_error

5.6.2 使用 swaks 测试

# 测试 STARTTLS
swaks --to [email protected] \
      --from [email protected] \
      --server mail.example.com \
      --port 587 \
      --tls \
      --auth-user [email protected] \
      --auth-password "password"

# 仅测试 TLS
swaks --server mail.example.com \
      --port 25 \
      --tls \
      --quit-after STARTTLS

5.6.3 在线工具

MXToolbox TLS 检测:
https://mxtoolbox.com/SuperTool.aspx?action=smtp:example.com

CheckTLS:
https://www.checktls.com/

SSL Labs (HTTP, 但可参考):
https://www.ssllabs.com/ssltest/

5.7 TLS 参数优化

5.7.1 推荐的密码套件配置

# 高安全性配置(推荐)
smtpd_tls_mandatory_ciphers = high
smtpd_tls_ciphers = high
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES-CBC3-SHA

# 现代配置(仅 TLS 1.2+)
smtpd_tls_mandatory_protocols = >=TLSv1.2
smtpd_tls_protocols = >=TLSv1.2
smtpd_tls_mandatory_ciphers = medium
smtpd_tls_ciphers = medium

# 使用 Mozilla 推荐的密码套件
# 参考: https://ssl-config.mozilla.org/

5.7.2 DH 参数生成

# 生成 2048 位 DH 参数(耗时较长)
sudo openssl dhparam -out /etc/postfix/certs/dhparam.pem 2048

# 在 main.cf 中引用
smtpd_tls_dh1024_param_file = /etc/postfix/certs/dhparam.pem

5.8 业务场景:多域名 TLS 配置

场景描述

一台邮件服务器为多个域名提供服务,每个域名使用不同的 TLS 证书。

SNI 配置

# /etc/postfix/main.cf — SNI(Server Name Indication)配置

# 启用 SNI
tls_server_sni_maps = hash:/etc/postfix/sni_map

# 通用证书(默认)
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.default.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.default.com/privkey.pem
# /etc/postfix/sni_map
# SNI 映射文件
mail.example.com /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/letsencrypt/live/mail.example.com/privkey.pem
mail.other.com   /etc/letsencrypt/live/mail.other.com/fullchain.pem /etc/letsencrypt/live/mail.other.com/privkey.pem

# 生成数据库
sudo postmap -rhash:/etc/postfix/sni_map

5.9 注意事项

⚠️ 证书文件权限

# 私钥文件必须严格限制权限
sudo chmod 600 /etc/letsencrypt/live/mail.example.com/privkey.pem
sudo chown root:root /etc/letsencrypt/live/mail.example.com/privkey.pem

# Postfix 需要读取证书的权限
# 方法 1: 将 postfix 用户加入 ssl-cert 组
sudo usermod -aG ssl-cert postfix

# 方法 2: 使用 postmap 证书包
sudo cp /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/postfix/certs/
sudo cp /etc/letsencrypt/live/mail.example.com/privkey.pem /etc/postfix/certs/
sudo chmod 600 /etc/postfix/certs/privkey.pem

⚠️ 强制 TLS 的风险

  • 设置 smtpd_tls_security_level = encrypt 会拒绝所有不支持 TLS 的连接
  • 一些老旧的邮件服务器可能不支持 TLS,导致邮件无法送达
  • 建议端口 587 强制 TLS,端口 25 保持可选

💡 DANE 支持

  • DANE(DNS-based Authentication of Named Entities)通过 DNS TLSA 记录验证证书
  • 需要 DNSSEC 支持
  • 配置:smtp_tls_security_level = dane

5.10 扩展阅读


上一章← 第 4 章:SASL 认证与 Dovecot 集成 下一章第 6 章:反垃圾邮件策略 →