返回

Sendmail向Odoo发邮件不止?定位原因与解决方法

php

揪出幕后黑手:解决 Sendmail 不停向 Odoo 发送邮件的问题

服务器遇到怪事了?明明感觉没做什么,却收到服务商警告说你的 IP 正在疯狂往外发邮件,查了下网络连接,发现一堆 sendmail 进程死磕 Odoo 的邮件服务器 (mx*.odoo.com),端口 25 忙得不亦乐乎。更头疼的是,你尝试停掉 sendmail 服务,它却像打不死的小强,过会儿又自己跑起来了。这到底是怎么回事?怎么才能让它停下来?

别慌,这篇文章就带你一步步排查,找出那个在背后捣鬼的“家伙”,让服务器恢复清净。

问题现象复现

先看看你遇到的典型场景是不是这样:

  1. 服务商邮件轰炸警告: Contabo 或者其他主机提供商发来邮件,说你的服务器 IP 因为对外发送大量邮件(通常是 SMTP 协议,端口 25),触发了他们的监控警报。

  2. 检查网络连接 (lsof -i :25): 执行这个命令,你会看到类似下面的输出,显示 sendmail 进程与 mx*.odoo.com 建立了多个 SMTP 连接。这些连接可能反复出现。

    COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    sendmail-  1361 root    4u  IPv4  19981      0t0  TCP localhost:smtp (LISTEN) # 这个是 Sendmail 监听本地连接
    sendmail- 20971 root    6u  IPv4 401805      0t0  TCP myserver:44362->mx1a.odoo.com:smtp (ESTABLISHED)
    sendmail- 20971 root    7u  IPv4 401805      0t0  TCP myserver:44362->mx1a.odoo.com:smtp (ESTABLISHED)
    # ... 可能还有更多到 mx1b, mx1c, mx1d 的连接
    
  3. 查看邮件日志 (/var/log/mail.log):tail -f /var/log/mail.log 盯着日志看,会发现大量类似下面的记录不断刷屏:

    Feb 22 07:56:35 myserver sm-mta[20971]: 51IKDucD1512089: to=<[email protected]>, delay=3+10:42:39, xdelay=00:00:05, mailer=esmtp, pri=44852439, relay=mx1d.odoo.com. [91.134.56.180], dsn=4.3.5, stat=Deferred: 451 4.3.5 Server configuration error
    Feb 22 07:56:37 myserver sm-mta[20971]: 51IKDucD1512089: to=<[email protected]>, delay=3+10:42:41, xdelay=00:00:07, mailer=esmtp, pri=44852439, relay=mx1a.odoo.com. [141.94.255.216], dsn=4.3.5, stat=Deferred: 451 4.3.5 Server configuration error
    # ... 反复尝试不同的 Odoo MX 服务器
    

    注意这里的关键信息:to=<[email protected]>, stat=Deferred: 451 4.3.5 Server configuration error。这说明 Sendmail 正在尝试给 Odoo 的某个地址发邮件,但 Odoo 服务器返回了一个临时错误(451),告诉 Sendmail “服务器配置有问题,稍后再试”。Deferred 状态意味着 Sendmail 会把这封邮件暂存起来,过段时间继续重试。delay=3+10:42:41 表明这封邮件已经被尝试发送了 3 天 10 多个小时了!

  4. 尝试禁用 Sendmail (systemctl stop/disable/mask sendmail): 你可能试过用 systemctl 命令来停止并禁用 Sendmail,甚至 mask 它,理论上这应该能彻底阻止它运行。但奇怪的是,过了一段时间,sendmail 进程又出现了,邮件还在继续发。

剖析问题根源

综合以上现象,问题可能出在以下几个方面:

  1. Sendmail 的重试机制: 日志里的 Deferred451 4.3.5 错误是核心线索。这表明最初有某个进程或脚本(我们还不知道是谁)通过 Sendmail 发送了一封或多封邮件给 Odoo。但由于 Odoo 服务器端的原因(可能是你的服务器 IP 被 Odoo 暂时阻止,或者 Odoo 那边确实有配置问题,或者邮件内容触发了 Odoo 的反垃圾策略),邮件发送失败了,但失败类型是“临时性”的。Sendmail 作为邮件传输代理(MTA),职责就是保证邮件送达,遇到这种临时失败,它会默认把邮件放入队列 (/var/spool/mqueue 或类似目录),然后定期尝试重新发送。只要邮件还在队列里,并且 Sendmail 服务在运行,它就会不停地尝试。
  2. 邮件源头未知: lsof 和日志只能看到是 sendmail 进程在对外连接和记录日志。但 sendmail 只是个邮差,它本身不生产邮件内容。我们需要找到是谁(哪个脚本、哪个应用、哪个用户)把信交给了 sendmail 这个邮差。
  3. Sendmail "自动复活"之谜: systemctl disablesystemctl mask 是 systemd 管理服务比较强的禁用手段了。如果执行后 sendmail 依然能启动,那极有可能是:
    • 有其他脚本或 cron 定时任务在检测到 sendmail 停止后,又手动启动了它。
    • 服务器可能被入侵,恶意软件在后台监控并重新启动 sendmail 服务,或者直接利用 sendmail 发送垃圾邮件。
    • 存在某种进程守护工具(如 monitsupervisor 等,虽然不常见于守护 sendmail)配置了自动重启。

定位邮件始作俑者

好,理论分析完了,该动手找“真凶”了。思路是:先找到是哪封(或哪些)邮件卡在队列里,然后顺藤摸瓜找是谁投递的。

方案一:检查 Sendmail 邮件队列

这是最直接的方法,看看队列里都有些啥。

  1. 列出队列内容:

    sudo mailq
    # 或者
    sudo sendmail -bp
    

    这个命令会显示当前在队列中的邮件摘要,包括邮件 ID(例如日志中的 51IKDucD1512089)、发件人、收件人、以及邮件大小和入队时间。留意那些收件人是 @notification.odoo.com 的邮件。记下它们的邮件 ID。

  2. 查看邮件头和内容(需要 root 权限):
    Sendmail 的邮件队列通常在 /var/spool/mqueue/ 目录下(具体路径可能因配置而异,可以查看 Sendmail 配置文件确认)。每个邮件由两个文件组成:qf 文件(包含信封信息和头部)和 df 文件(包含邮件正文)。

    # 假设邮件 ID 是 51IKDucD1512089
    # 查看对应的 qf 文件内容
    sudo ls -l /var/spool/mqueue/qf51IKDucD1512089
    sudo less /var/spool/mqueue/qf51IKDucD1512089
    

    qf 文件里,仔细查找 From:Sender:Received:X- 开头的自定义头部。这些头部信息有时能提供线索,比如:

    • Received: 头部记录了邮件经过的服务器路径,最原始的那条可能包含最初提交邮件的脚本或本地主机信息。
    • 某些应用程序在发送邮件时会添加特定的 X-Mailer:User-Agent: 头部。
    • 发件人地址 From: 有时也能提供线索。

    安全建议: 查看文件内容时使用 lesscat,不要执行文件里的任何内容。

    进阶技巧: 如果队列里邮件很多,可以写个脚本批量分析 qf 文件,提取特定头部信息。例如,用 grep 在所有 qf 文件中搜索可疑的 IP 地址或脚本名。

方案二:审计系统进程和计划任务

如果队列信息不够明确,或者你想找的是持续生成新邮件的源头,那就得查查服务器上正在运行的进程和定时任务了。

  1. 检查当前运行的进程:

    ps aux | grep -Ei 'sendmail|mail|php|python|script|cron'
    

    看看有没有可疑的脚本进程在运行,或者与邮件发送相关的应用进程(比如,如果服务器上运行了 Odoo 实例,那 Odoo 本身就可能尝试发邮件)。

  2. 检查 Cron 定时任务:
    恶意脚本或配置错误的正常脚本常常通过 Cron 定期执行。

    # 检查 root 用户的 cron
    sudo crontab -l
    
    # 检查其他用户的 cron (例如 www-data 用户)
    sudo crontab -u www-data -l
    
    # 检查系统级的 cron 目录
    ls -l /etc/cron.*
    # 特别留意 /etc/crontab 文件和 /etc/cron.d/ 目录下的文件
    sudo less /etc/crontab
    sudo ls -l /etc/cron.d/
    

    仔细检查每个定时任务执行的命令,看有没有调用 sendmailmail 命令,或者执行可疑脚本的。

  3. 检查 Systemd Timers:
    现在很多定时任务也通过 Systemd Timer 实现。

    sudo systemctl list-timers --all
    

    查看是否有可疑的定时器单元在运行。

  4. 检查 Web 服务器和应用日志:
    如果是 Web 应用(比如 PHP 写的网站)在发邮件,检查 Web 服务器(Nginx, Apache)的访问日志和错误日志,以及应用程序自身的日志。有时能找到是哪个请求触发了邮件发送。

安全建议: 对于不认识的进程或 Cron 任务,上网搜索一下它的名字,判断是否正常。修改或删除 Cron 任务前最好先备份。

方案三:利用 auditd 进行深度追踪

如果上面的方法都找不到源头,或者你想精确知道是哪个程序调用了 sendmail,可以使用 Linux 的审计工具 auditd

  1. 安装并配置 auditd:

    sudo apt-get update
    sudo apt-get install auditd
    sudo systemctl enable auditd
    sudo systemctl start auditd
    
  2. 添加审计规则:
    我们想监控对 sendmail 可执行文件的执行。

    # 找到 sendmail 的准确路径
    which sendmail
    # 假设路径是 /usr/sbin/sendmail
    # 添加规则,监控对 /usr/sbin/sendmail 的执行(-p x 表示执行权限),并给这个规则打个标签 sendmail_exec
    sudo auditctl -w /usr/sbin/sendmail -p x -k sendmail_exec
    

    为了让规则在重启后依然生效,需要将其添加到 /etc/audit/rules.d/ 目录下的规则文件中(例如 audit.rules),然后重启 auditd 服务。

  3. 分析审计日志:
    当有进程执行 /usr/sbin/sendmail 时,相关信息会被记录到 /var/log/audit/audit.log

    sudo ausearch -k sendmail_exec -i
    

    或者直接 tail -f /var/log/audit/audit.log 观察。日志会包含执行 sendmail 的进程 PID、父进程 PID (PPID)、用户 ID (uid, euid)、以及完整的命令行参数。通过 PID 和 PPID,你可以用 ps 命令追溯到最初发起调用的进程。

安全建议: auditd 会记录大量日志,可能影响性能。规则要尽量精确。问题解决后记得评估是否需要移除或调整规则。

进阶技巧: 可以设置更复杂的规则,例如监控对 sendmail 配置文件的修改,或者监控特定的系统调用。

方案四:分析网络流量

实在没办法了,还可以抓包看看 Sendmail 到底在往外发什么。

sudo tcpdump -i any -s 0 -A 'port 25 and host mx1a.odoo.com'
# 或者抓所有 Odoo MX 服务器的包
sudo tcpdump -i any -s 0 -A 'port 25 and (host mx1a.odoo.com or host mx1b.odoo.com or host mx1c.odoo.com or host mx1d.odoo.com)'
# 使用 tshark 可能更方便阅读 SMTP 协议
sudo apt-get install tshark # 如果没装的话
sudo tshark -i any -Y 'tcp.port == 25 && (ip.addr == 91.134.56.180 || ip.addr == 141.94.255.216 || ip.addr == 141.94.241.170)' -O smtp

观察抓到的 SMTP 对话内容。特别是 MAIL FROM:, RCPT TO:, 以及 DATA 部分的内容,或许能提供邮件来源的线索。比如邮件正文里包含某个应用的特定错误信息或标识。

安全建议: 抓包可能捕获敏感信息。结束后务必停止抓包,并妥善处理捕获文件。

彻底阻止 Sendmail 发送并处理潜在风险

定位源头的同时,或者实在找不到源头但必须立刻停止邮件发送时,可以采取以下措施。

措施一:强制停止并清理 Sendmail

既然 disable/mask 不一定管用(因为可能有“外力”在重启它),我们需要更强硬的手段,并解决它“复活”的问题。

  1. 找到重启 Sendmail 的元凶: 在执行停止操作后,持续监控进程 (ps aux | grep sendmail)。一旦发现它又启动了,立刻用 ps -ef --forest 查看它的进程树,找到它的父进程是谁。或者检查系统日志 (/var/log/syslogjournalctl) 看是否有启动 sendmail 的记录。重点排查 Cron、Systemd Timers、以及可疑的守护进程。

  2. 解决元凶: 如果是 Cron 或 Timer,注释掉或删除对应任务。如果是未知进程,可能需要先定位该进程并处理掉(可能是恶意软件,参考下面的“安全入侵”部分)。

  3. 确保 Sendmail 停止: 在解决了重启源头之后,再次执行:

    sudo systemctl stop sendmail
    sudo systemctl disable sendmail
    sudo systemctl mask sendmail
    # 极端情况下,如果它还在跑,可能需要找到进程PID直接kill
    # sudo pkill sendmail
    # sudo pkill -9 sendmail # 强制杀死,作为最后手段
    
  4. 清理邮件队列(非常重要): 即使 Sendmail 停了,队列里的邮件还在。一旦 Sendmail 被(意外或故意)再次启动,它会继续尝试发送这些邮件。

    # 确认队列目录,通常是 /var/spool/mqueue
    # 清空队列 (!! 操作危险,确认路径无误 !!)
    sudo rm -rf /var/spool/mqueue/*
    # 有些系统可能还有一个 clientmqueue 目录也需要检查
    # sudo rm -rf /var/spool/clientmqueue/*
    
  5. 考虑彻底移除 Sendmail(如果不需要): 如果服务器根本不需要运行 Sendmail 服务(比如所有邮件都通过外部 SMTP 服务商发送,或者本机不需要发邮件),在确认没有其他服务依赖它之后,可以考虑卸载。

    # 模拟卸载,看看会影响哪些包
    sudo apt-get -s purge sendmail*
    # 如果确认没问题,执行卸载
    sudo apt-get purge sendmail*
    

安全建议: rm -rf 操作非常危险,执行前务必再三确认路径正确。卸载 Sendmail 前一定用 -s 参数模拟,检查依赖关系,避免破坏系统。

措施二:使用防火墙阻止出站 SMTP

这是一个比较“暴力”但有效的临时或永久措施:直接在防火墙层面禁止服务器对外访问 TCP 端口 25。

  1. 使用 UFW (Uncomplicated Firewall):

    sudo ufw status # 查看 UFW 状态和现有规则
    sudo ufw deny out 25/tcp comment 'Block outgoing SMTP port 25'
    sudo ufw reload # 或者 enable UFW 如果之前没启用
    
  2. 使用 iptables:

    sudo iptables -A OUTPUT -p tcp --dport 25 -j REJECT --reject-with tcp-reset
    # 或者直接丢弃数据包(让连接超时)
    # sudo iptables -A OUTPUT -p tcp --dport 25 -j DROP
    
    # 查看 OUTPUT 链规则
    sudo iptables -L OUTPUT -v -n --line-numbers
    
    # 保存规则(方法因发行版而异,例如使用 iptables-persistent)
    sudo apt-get install iptables-persistent # Debian/Ubuntu
    sudo netfilter-persistent save
    

影响和风险:

  • 优点: 能立刻阻止所有通过端口 25 发送的邮件,包括来自 Sendmail 或其他任何程序的。很多主机商也推荐或强制执行此操作以防滥用。
  • 缺点: 会阻止所有依赖端口 25 的合法 邮件发送。例如,某些老旧应用或配置不当的应用可能硬编码使用端口 25。系统本身的一些通知邮件(比如 Cron 的输出)如果默认走本地 Sendmail 转发出也可能失败。
  • 替代方案: 现代应用和服务通常推荐使用 SMTP 提交端口 587 (带 STARTTLS 加密)SMTPS 端口 465 (带 SSL/TLS 加密) ,并需要身份验证。阻止端口 25 不会影响这些端口。如果你的应用需要发邮件,应配置它们使用这些端口连接到你的邮件服务商(如 Mailgun, SendGrid, Gmail 等)或你自己的邮件服务器(如果设置了 Submission 服务)。

安全建议: 实施防火墙规则后,要监控是否有合法应用因此失败。

应对潜在的服务器安全入侵

Sendmail 无法正常停止并自行重启,这本身就是一个危险信号,暗示服务器可能已经被入侵。

如果你怀疑服务器被黑了,必须严肃对待:

  1. 立即隔离(如果可能): 在不影响业务的前提下,考虑通过网络防火墙或云平台安全组限制服务器的入站和出站连接,尤其是对外的敏感端口。

  2. 修改所有凭证: 从一个干净、可信 的电脑上,立刻修改服务器的 root 密码、所有用户密码、SSH 密钥对。检查 /root/.ssh/authorized_keys 和用户家目录下的 ~/.ssh/authorized_keys 是否有未知的公钥,并移除。检查数据库、应用后台等所有相关服务的密码。

  3. 运行安全扫描工具: 安装并运行 chkrootkitrkhunter 等工具,检查 rootkit 和后门。

    sudo apt-get install chkrootkit rkhunter
    sudo chkrootkit
    sudo rkhunter --update
    sudo rkhunter --check --sk # --sk 跳过按键等待
    

    注意:已被入侵的系统上运行这些工具可能结果不可靠,因为工具本身也可能被篡改。

  4. 检查系统文件和进程:

    • last 命令查看最近登录记录。
    • 检查 /etc/passwd, /etc/shadow, /etc/group 文件是否有异常用户或用户组。
    • 使用 netstat -tulnpss -tulnp 检查监听的端口和对应程序,看是否有不认识的服务。
    • 使用 ps auxtop/htop 仔细检查运行中的进程。
    • 检查系统日志(/var/log/auth.log, /var/log/syslog, journalctl)寻找可疑活动。
    • 检查系统文件的完整性(如 Debian/Ubuntu 用 debsums -c)。
  5. 终极方案:重装系统: 如果确认被入侵,或者强烈怀疑但无法彻底清理,最安全、最推荐的做法是:备份必要且确认安全 的数据(注意:不要备份可执行文件或配置文件,除非你完全理解它们且确信未被篡改),然后彻底格式化硬盘,重装操作系统 。之后,从可信来源重新安装应用,恢复数据,并加强安全措施(如配置防火墙、定期更新、使用强密码、禁用不必要的服务等)。

处理 Sendmail 异常邮件发送问题,既需要排查技术配置,也得时刻警惕安全风险。按部就班地检查队列、进程、定时任务,并结合强制停止和防火墙策略,通常能解决问题。但如果遇到无法解释的“复活”现象,务必优先考虑服务器安全,采取更深层次的检查甚至重装。