SMTP 服务器搭建完全指南 / 第 14 章:故障排查与调试
第 14 章:故障排查与调试
遇到问题不要慌——日志和状态码会告诉你答案。
14.1 退信分析
14.1.1 退信邮件结构
当邮件投递失败时,系统会生成退信通知(Bounce Message / NDR),通常包含以下信息:
This is the mail system at host mail.example.com.
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.
For further assistance, please send mail to postmaster.
If you do so, please include this problem report. You can
delete your own text from the attached returned message.
The mail system
<user@invalid-domain.com>: Host or domain name not found. Name service
error for name=invalid-domain.com type=MX: Host not found, try again
14.1.2 退信状态码解读
| DSN 码 | 含义 | 常见原因 |
|---|---|---|
| 4.x.x | 临时失败 | 可重试,服务器暂时不可用 |
| 5.x.x | 永久失败 | 不可重试,地址或配置错误 |
详细状态码:
| 状态码 | 含义 | 解决方案 |
|---|---|---|
| 4.0.0 | 临时网络问题 | 等待重试 |
| 4.1.1 | 邮箱暂时不可用 | 检查目标服务器状态 |
| 4.3.0 | 邮件系统满 | 目标服务器磁盘空间不足 |
| 4.4.1 | 连接超时 | 检查网络连通性 |
| 4.7.0 | 临时拒绝(灰名单) | 等待后重试 |
| 5.0.0 | 语法错误 | 检查收件人地址格式 |
| 5.1.0 | 收件人地址不存在 | 确认收件人地址正确 |
| 5.1.1 | 邮箱不存在 | 收件人地址错误 |
| 5.1.2 | 域名不存在 | 检查收件人域名 |
| 5.2.0 | 邮箱已满 | 收件人邮箱空间不足 |
| 5.3.0 | 邮件系统错误 | 目标服务器内部错误 |
| 5.4.0 | DNS 查询失败 | 检查 MX 记录配置 |
| 5.5.0 | 协议错误 | SMTP 通信问题 |
| 5.7.0 | 权限被拒绝 | SPF/DKIM 验证失败 |
14.1.3 退信分析脚本
#!/bin/bash
# bounce-analyzer.sh — 退信分析脚本
BOUNCE_FILE="$1"
if [ -z "$BOUNCE_FILE" ]; then
echo "用法: $0 <退信邮件文件>"
exit 1
fi
echo "=== 退信邮件分析 ==="
# 提取状态码
echo "状态码:"
grep -oP 'Status:\s*\K[0-9]\.[0-9]\.[0-9]+' "$BOUNCE_FILE" || \
grep -oP '[45]\.[0-9]\.[0-9]+' "$BOUNCE_FILE" | head -5
# 提取收件人
echo ""
echo "收件人:"
grep -oP '<[^>]+@[^>]+>' "$BOUNCE_FILE" | head -5
# 提取退信原因
echo ""
echo "退信原因:"
grep -i "reason\|error\|failure" "$BOUNCE_FILE" | head -10
# 提取 SMTP 响应
echo ""
echo "SMTP 响应:"
grep -E "^[45][0-9][0-9]" "$BOUNCE_FILE" | head -5
echo ""
echo "=== 分析完成 ==="
14.1.4 常见退信原因与解决方案
| 退信原因 | 检查命令 | 解决方案 |
|---|---|---|
| 域名不存在 | dig MX domain.com | 确认收件人地址正确 |
| 连接超时 | telnet mail.domain.com 25 | 检查网络连通性 |
| 550 被拒绝 | 查看日志 grep 550 /var/log/mail.log | 检查 IP 黑名单、SPF |
| DKIM 验证失败 | opendkim-testkey | 修复 DKIM 配置 |
| 灰名单延迟 | 检查 Postgrey 日志 | 等待自动重试 |
14.2 队列堵塞处理
14.2.1 诊断队列问题
# 查看队列状态
mailq
# 统计队列中的邮件数
find /var/spool/postfix/deferred -type f | wc -l
# 按域名统计延迟邮件
qshape deferred
# 查看队列中特定域名的邮件
mailq | grep "example.com"
# 查看队列目录大小
du -sh /var/spool/postfix/{active,deferred,hold,maildrop,incoming}
14.2.2 队列堵塞原因
| 原因 | 诊断方法 | 解决方案 |
|---|---|---|
| DNS 解析问题 | dig MX domain.com | 修复 DNS 配置 |
| 目标服务器不可达 | telnet mail.domain.com 25 | 检查网络或联系目标管理员 |
| TLS 握手失败 | 查看日志中的 TLS 错误 | 更新证书或调整 TLS 配置 |
| 队列文件损坏 | postsuper -c /var/spool/postfix | 清理损坏的队列文件 |
| 大量垃圾邮件 | 分析队列内容 | 清理并修复安全漏洞 |
14.2.3 队列管理操作
# 强制投递所有队列中的邮件
postqueue -f
# 强制投递特定域名的邮件
postqueue -s example.com
# 删除特定邮件
postsuper -d QUEUE_ID
# 删除所有延迟邮件
postsuper -d ALL deferred
# 暂停特定邮件
postsuper -h QUEUE_ID
# 恢复特定邮件
postsuper -H QUEUE_ID
# 清空整个队列(慎用!)
postsuper -d ALL
# 查看特定邮件内容
postcat -q QUEUE_ID
14.2.4 队列清理脚本
#!/bin/bash
# queue-cleanup.sh — 队列清理脚本
echo "=== 队列清理 ==="
# 统计当前队列
TOTAL=$(find /var/spool/postfix/deferred -type f | wc -l)
echo "延迟邮件数量: $TOTAL"
if [ $TOTAL -gt 1000 ]; then
echo "⚠️ 队列积压严重!"
# 查看域名分布
echo ""
echo "目标域名分布 TOP 10:"
qshape deferred | head -12
# 询问是否清理
read -p "是否清理所有延迟邮件?(y/N): " confirm
if [ "$confirm" = "y" ]; then
echo "正在清理..."
postsuper -d ALL deferred
echo "清理完成"
fi
else
echo "队列状态正常"
fi
# 检查损坏的队列文件
echo ""
echo "检查损坏的队列文件..."
CORRUPT=$(find /var/spool/postfix/corrupt -type f | wc -l)
if [ $CORRUPT -gt 0 ]; then
echo "发现 $CORRUPT 个损坏文件"
read -p "是否删除?(y/N): " confirm
if [ "$confirm" = "y" ]; then
postsuper -d ALL corrupt
fi
fi
echo ""
echo "=== 清理完成 ==="
14.3 常见错误排查
14.3.1 SMTP 连接错误
错误 1:Connection refused
# 诊断
telnet mail.example.com 25
# 连接被拒绝
# 排查步骤
# 1. 检查 Postfix 是否运行
systemctl status postfix
# 2. 检查端口监听
ss -tlnp | grep :25
# 3. 检查防火墙
ufw status
# 4. 检查 Postfix 配置
postconf inet_interfaces
错误 2:Connection timed out
# 诊断
telnet mail.example.com 25
# 连接超时
# 排查步骤
# 1. 检查网络连通性
ping mail.example.com
# 2. 检查防火墙规则
iptables -L -n
# 3. 检查云服务商安全组
# AWS: 检查 Security Group
# 阿里云: 检查安全组规则
# 4. 检查 ISP 是否封锁 25 端口
telnet gmail-smtp-in.l.google.com 25
14.3.2 认证错误
错误 1:SASL authentication failed
# 查看认证日志
grep "auth failed" /var/log/mail.log
# 排查步骤
# 1. 检查 SASL 配置
postconf smtpd_sasl_auth_enable
# 2. 检查 Dovecot SASL socket
ls -la /var/spool/postfix/private/auth
# 3. 检查用户密码
doveadm auth test user@example.com password
# 4. 检查 TLS 是否启用
postconf smtpd_tls_auth_only
错误 2:User unknown
# 查看日志
grep "User unknown" /var/log/mail.log
# 排查步骤
# 1. 检查用户是否存在
grep "user@example.com" /etc/dovecot/users
# 2. 检查虚拟用户配置
postconf virtual_mailbox_maps
postmap -q user@example.com hash:/etc/postfix/virtual
14.3.3 TLS 错误
错误 1:TLS handshake failure
# 测试 TLS
openssl s_client -starttls smtp -connect mail.example.com:25
# 排查步骤
# 1. 检查证书有效性
openssl x509 -in /etc/letsencrypt/live/mail.example.com/cert.pem -text -noout
# 2. 检查证书过期时间
openssl x509 -enddate -noout -in /etc/letsencrypt/live/mail.example.com/cert.pem
# 3. 检查 TLS 配置
postconf smtpd_tls_cert_file smtpd_tls_key_file
错误 2:Certificate verify failed
# 排查步骤
# 1. 检查证书链完整性
openssl s_client -starttls smtp -connect mail.example.com:25 -verify_return_error
# 2. 检查 CA 证书
postconf smtp_tls_CAfile
# 3. 更新 CA 证书
sudo update-ca-certificates
14.3.4 邮件投递错误
错误 1:Mailbox full
# 错误信息
550 5.2.0 <user@example.com>: Recipient address rejected: Mailbox full
# 解决方案
# 1. 检查邮箱大小限制
postconf mailbox_size_limit
# 2. 清理邮箱
doveadm quota recalc -u user@example.com
doveadm expunge -u user@example.com mailbox Trash savedbefore 30d
错误 2:Message size exceeded
# 错误信息
552 5.3.4 Error: message file too big
# 解决方案
# 增加邮件大小限制
postconf -e "message_size_limit = 52428800"
systemctl reload postfix
14.4 调试工具
14.4.1 Postfix 调试
# 查看当前配置
postconf -n
# 查看默认配置
postconf -d
# 查看单个参数
postconf myhostname
# 检查配置语法
postfix check
# 查看配置差异
diff <(postconf -d) <(postconf -n)
# 查看服务状态
postfix status
14.4.2 日志分析工具
# 实时查看邮件日志
tail -f /var/log/mail.log
# 过滤特定队列 ID
grep "QUEUE_ID" /var/log/mail.log
# 统计每小时邮件量
awk '{print $2}' /var/log/mail.log | cut -d: -f1 | uniq -c
# 查看被拒绝的邮件
grep "reject:" /var/log/mail.log
# 查看 TLS 连接
grep "TLS" /var/log/mail.log
# 查看认证失败
grep "auth failed\|authentication failed" /var/log/mail.log
14.4.3 网络诊断工具
# DNS 查询
dig MX example.com +short
dig A mail.example.com +short
dig -x 203.0.113.10 +short
# SMTP 连接测试
telnet mail.example.com 25
# OpenSSL 测试
openssl s_client -starttls smtp -connect mail.example.com:25
# swaks 测试
swaks --to user@example.com --from test@example.com --server mail.example.com
# 防火墙测试
nc -zv mail.example.com 25
nmap -p 25,587,993 mail.example.com
14.4.4 Postfix 日志级别调整
# 调整 SMTP 客户端日志级别
postconf -e "smtp_tls_loglevel = 1"
# 调整 SMTP 服务器日志级别
postconf -e "smtpd_tls_loglevel = 1"
# 调试特定组件
# /etc/postfix/master.cf
# smtp unix - - n - - smtp
# -o smtp_tls_loglevel=2
# 重新加载配置
systemctl reload postfix
14.5 邮件追踪
14.5.1 使用 postqueue 追踪
# 查看队列中的邮件
postqueue -p
# 按队列 ID 追踪
grep "QUEUE_ID" /var/log/mail.log | tail -20
# 查看邮件内容
postcat -q QUEUE_ID
14.5.2 使用 swaks 测试
# 完整测试邮件发送
swaks --to user@example.com \
--from test@example.com \
--server mail.example.com \
--port 587 \
--auth-user admin@example.com \
--auth-password "password" \
--tls \
--header "Subject: Test Email" \
--body "This is a test email."
# 测试 STARTTLS
swaks --server mail.example.com --port 25 --tls --quit-after STARTTLS
# 测试认证
swaks --server mail.example.com --port 587 \
--auth-user admin@example.com \
--auth-password "password" \
--tls \
--quit-after AUTH
14.5.3 使用 tcpdump 抓包
# 抓取 SMTP 流量
sudo tcpdump -i eth0 port 25 -A
# 保存到文件
sudo tcpdump -i eth0 port 25 -w /tmp/smtp-capture.pcap
# 分析抓包文件
tcpdump -r /tmp/smtp-capture.pcap -A
14.6 业务场景:邮件送达率问题排查
场景描述
用户反馈发送到 Gmail 的邮件进入了垃圾邮件箱。
排查步骤
# 1. 检查 IP 黑名单
# https://mxtoolbox.com/blacklists.aspx
# 2. 检查 SPF 记录
dig TXT example.com +short
# 确保包含发送服务器 IP
# 3. 检查 DKIM 签名
opendkim-testkey -d example.com -s mail -vvv
# 期望输出: key OK
# 4. 检查 DMARC 记录
dig TXT _dmarc.example.com +short
# 5. 检查 PTR 记录
dig -x $(curl -s ifconfig.me) +short
# 应该返回 mail.example.com
# 6. 检查邮件内容
# 避免垃圾邮件触发词
# 包含退订链接
# 保持文本/HTML 比例
# 7. 使用 Gmail Postmaster Tools
# https://postmaster.google.com/
14.7 注意事项
⚠️ 日志安全:
- 日志可能包含敏感信息(密码哈希、邮件内容)
- 不要将日志直接发送到公共分析工具
- 定期清理旧日志
⚠️ 队列操作:
postsuper -d ALL会删除所有队列邮件,慎用!- 操作前先备份队列目录
- 记录操作日志
💡 调试建议:
- 从日志入手,找到错误信息
- 使用测试工具验证假设
- 逐步排查,不要同时修改多个配置
- 记录排查过程,便于复盘