SMTP 服务器搭建完全指南 / 第 5 章:TLS/SSL 加密配置
第 5 章:TLS/SSL 加密配置
没有 TLS 的 SMTP 就像寄明信片——途经的每个人都能看到内容。
5.1 邮件加密概述
5.1.1 SMTP 加密方式
| 方式 | 端口 | 说明 | 推荐 |
|---|---|---|---|
| STARTTLS | 25, 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 admin@example.com \
--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 user@example.com \
--from test@example.com \
--server mail.example.com \
--port 587 \
--tls \
--auth-user admin@example.com \
--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 扩展阅读
- Postfix TLS Readme
- Let’s Encrypt 文档
- Mozilla SSL Configuration Generator
- RFC 3207 — SMTP STARTTLS
- RFC 8314 — SMTP over TLS