Dovecot + CentOS 7 收取 QQ 邮箱转发邮件 LMTP持续占用 CPU 过高 问题解决方案

-
-
2025-11-22

问题症状

  • 现象:QQ 邮箱转发邮件到 Dovecot 邮件服务器时,lmtp 进程 CPU 占用 100%
  • 结果:邮件无法接收,进程卡死
  • 特点:QQ 新发邮件正常,只有转发邮件有问题

根本原因

  1. QQ 转发邮件特性:使用 message/rfc822 格式,原文被完整嵌套,保留原始的 GB18030/ISO-2022-CN 编码
  2. Dovecot 索引机制:索引时会对每个 MIME 部分进行 charset→UTF-8 转换
  3. glibc bug:CentOS 7 的 glibc-2.17 在处理 GB18030/ISO-2022-CN 时存在 iconv 死循环 bug(CVE-2020-27618)
  4. 配置限制:Dovecot 2.2.x 没有禁用字符集转换的配置选项

完整解决方案

前置条件

# 1. 确认系统版本
cat /etc/redhat-release
# CentOS Linux release 7.x

# 2. 确认 Dovecot 版本
dovecot --version
# 需要 2.3.x 或更高版本

# 3. 如果是 Dovecot 2.2.x,先升级到 2.3

升级 Dovecot(如果需要)

# 添加 Dovecot 官方仓库 导入GPG 密钥
curl https://repo.dovecot.org/DOVECOT-REPO-GPG-2.3 | gpg --import

# 1. 创建 Dovecot 仓库配置文件
cat > /etc/yum.repos.d/dovecot.repo << 'EOF'
[dovecot-2.3-latest]
name=Dovecot 2.3 CentOS $releasever - $basearch
baseurl=https://repo.dovecot.org/ce-2.3-latest/centos/$releasever/RPMS/$basearch
gpgkey=https://repo.dovecot.org/DOVECOT-REPO-GPG-2.3
gpgcheck=1
enabled=1
EOF

# 2. 清理 yum 缓存
yum clean all
yum makecache

# 3. 查看可用的 Dovecot 版本
yum list available dovecot --showduplicates

# 4. 升级 Dovecot
yum upgrade dovecot dovecot-pigeonhole

# 5. 重启 Dovecot
systemctl restart dovecot

核心解决脚本

#!/bin/bash
set -e

echo "=== Dovecot QQ 转发邮件 CPU 100% 问题修复脚本 ==="
echo "适用于:CentOS 7 + Dovecot 2.3.x + glibc-2.17"
echo ""

# 步骤 1: 备份配置
echo "=== 步骤 1: 备份配置 ==="
cp /etc/dovecot/conf.d/90-plugin.conf /etc/dovecot/conf.d/90-plugin.conf.bak.$(date +%s)
echo "✓ 配置已备份"

# 步骤 2: 添加 charset_aliases 到 Dovecot 配置
echo -e "\n=== 步骤 2: 配置 Dovecot charset_aliases ==="
if grep -q "charset_aliases" /etc/dovecot/conf.d/90-plugin.conf; then
  echo "charset_aliases 已存在,跳过"
else
  # 在第一个 plugin { 后面插入配置
  sed -i '0,/^plugin {/s/^plugin {/plugin {\n  charset_aliases = gb18030=utf-8 gbk=utf-8 gb2312=utf-8 iso-2022-cn=utf-8 iso-2022-cn-ext=utf-8/' /etc/dovecot/conf.d/90-plugin.conf
  echo "✓ 已添加 charset_aliases 配置"
fi

# 步骤 3: 移除系统字符集模块
echo -e "\n=== 步骤 3: 移除 glibc 字符集模块 ==="
cd /usr/lib64/gconv
BACKUP_DIR="/root/gconv_disabled_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

# 移动有问题的字符集模块文件
for file in GB18030.so GBK.so GBGBK.so GBBIG5.so ISO-2022-CN.so ISO-2022-CN-EXT.so libGB.so; do
  if [ -f "$file" ]; then
    mv "$file" "$BACKUP_DIR/"
    echo "已移除: $file"
  fi
done

# 重建 gconv 模块缓存
rm -f gconv-modules.cache
iconvconfig
echo "✓ gconv 缓存已重建"

# 步骤 4: 验证配置
echo -e "\n=== 步骤 4: 验证配置 ==="
echo "1. Dovecot charset_aliases 配置:"
doveconf -n | grep charset_aliases && echo "✓ charset_aliases 已配置" || echo "❌ charset_aliases 未配置"

echo -e "\n2. 字符集模块状态:"
ls -lh /usr/lib64/gconv/ | grep -E "GB18030|GBK|ISO-2022-CN" || echo "✓ 字符集模块已移除"

echo -e "\n3. iconv 字符集测试:"
iconv -l | grep -i gb18030 && echo "❌ GB18030 仍可用" || echo "✓ GB18030 已禁用"

# 步骤 5: 重启 Dovecot
echo -e "\n=== 步骤 5: 重启 Dovecot ==="
systemctl restart dovecot
sleep 2
systemctl status dovecot | grep Active

echo -e "\n=== 修复完成 ==="
echo "现在可以测试 QQ 转发邮件了"
echo ""
echo "监控命令:"
echo "  tail -f /var/log/maillog"
echo "  watch -n 1 'ps aux | grep lmtp | grep -v grep'"
echo ""
echo "如需恢复字符集模块:"
echo "  mv $BACKUP_DIR/* /usr/lib64/gconv/"
echo "  iconvconfig"

使用方法

# 1. 保存脚本
cat > /root/fix_dovecot_qq_forward.sh << 'EOF'
[粘贴上面的脚本内容]
EOF

# 2. 赋予执行权限
chmod +x /root/fix_dovecot_qq_forward.sh

# 3. 执行修复
/root/fix_dovecot_qq_forward.sh

# 4. 测试 QQ 转发邮件

技术原理说明

为什么需要两个步骤?

  1. charset_aliases 配置(主要方案)

    • 告诉 Dovecot:将 GB18030 等字符集当作 UTF-8 处理
    • Dovecot 跳过 iconv 转换,直接使用原始数据
    • 避免调用 glibc 的有问题的 iconv 函数
  2. 移除字符集模块(双重保险)

    • 即使某些代码路径仍尝试调用 iconv
    • 系统层面上这些字符集已不可用
    • 完全阻止触发 glibc bug 的可能性

影响和副作用

正面影响:

  • ✅ lmtp 进程 CPU 占用恢复正常
  • ✅ QQ 转发邮件能正常接收
  • ✅ 服务器不再卡死

可能的副作用:

  • ⚠️ GB18030 编码的邮件中文可能显示乱码
  • ⚠️ 但用户可以下载原始邮件,使用邮件客户端正确显示
  • ⚠️ 系统其他程序如需 GB18030 转换会失败(很少见)

验证和测试

验证配置是否生效

# 1. 检查 Dovecot 配置
doveconf -n | grep charset_aliases

# 2. 检查字符集模块
iconv -l | grep -i gb18030  # 应该没有输出

# 3. 测试 iconv 转换
echo "test" | iconv -f utf-8 -t gb18030
# 应该报错:conversion to 'GB18030' is not supported

测试 QQ 转发邮件

# 监控 CPU
watch -n 1 'ps aux | grep "[l]mtp"'

# 监控日志
tail -f /var/log/maillog

# 让 QQ 邮箱发送转发邮件
# 观察 lmtp CPU 是否正常(< 10%)
# 检查邮件是否能收到

恢复方法

如果需要恢复(通常不需要):

# 恢复字符集模块
BACKUP_DIR=$(ls -td /root/gconv_disabled_* | head -1)
mv $BACKUP_DIR/* /usr/lib64/gconv/
iconvconfig

# 移除 charset_aliases 配置
vi /etc/dovecot/conf.d/90-plugin.conf
# 删除 charset_aliases 那一行

# 重启 Dovecot
systemctl restart dovecot

适用范围

  • ✅ CentOS 7 / RHEL 7(glibc-2.17)
  • ✅ Dovecot 2.3.x 或更高版本
  • ✅ Postfix + Dovecot 邮件服务器
  • ✅ 收取 QQ 邮箱、163 邮箱等国内邮箱的转发邮件
  • ✅ 症状:lmtp 进程 CPU 100%,邮件收不到

不适用:

  • ❌ Dovecot 2.2.x(需先升级到 2.3+)
  • ❌ CentOS 8/9(使用更新的 glibc,可能不存在此 bug)
  • ❌ Ubuntu/Debian(glibc 版本不同)

相关 CVE 和 Bug

  • CVE-2020-27618:glibc iconv 处理无效多字节序列时无限循环
  • glibc bug:ISO-2022-CN、GB18030 字符集转换死循环
  • Dovecot 限制:2.2.x 版本无法禁用字符集转换

总结

这个问题是 CentOS 7 老旧 glibc + QQ 转发邮件特殊编码 + Dovecot 索引机制 三者结合导致的典型案例。解决方案通过 应用层配置 + 系统层禁用 双重手段,彻底避免触发 glibc iconv bug。

关键点:

  1. 升级 Dovecot 到 2.3+ 以获得 charset_aliases 配置
  2. 配置 charset_aliases 让 Dovecot 跳过转换
  3. 移除系统字符集模块作为双重保险
  4. 代价是 GB18030 邮件可能乱码,但能正常接收

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝

目录