跳转到主要内容

于 2025年04月22日 摘录自 Postfix Debugging Howto

本文件的目的

本文件描述了当Postfix邮件系统未按预期工作时,如何调试其部分组件。调试方法包括让Postfix记录大量详细日志,以及在调用跟踪器或调试器控制下运行某些守护进程。

本文假设 Postfix 的 main.cfmaster.cf 配置文件存储在 /etc/postfix 目录下。您可以使用命令 "postconf config_directory" 来查找您机器上此目录的实际位置。

按侵入性从低到高排序,调试技术如下:

查找明显的问题迹象

Postfix 将所有失败和成功的投递记录到日志文件中。

  • 当 Postfix 使用 syslog 日志记录(默认设置)时,文件通常名为 /var/log/maillog、/var/log/mail 或类似名称;确切的路径在名为 /etc/syslog.conf、/etc/rsyslog.conf 或类似文件中配置。
  • 当 Postfix 使用其自己的日志系统(参见 MAILLOG_README),日志文件的位置通过 Postfix 的 maillog_file 参数进行配置。

当 Postfix 无法接收或发送邮件时,首先要做的就是查找阻止 Postfix 正常运行的错误:

% grep -E '(warning|error|fatal|panic):' /some/log/file | more

注意:最重要的消息位于输出开头。后续出现的错误消息参考价值较低。

每个问题的性质如下所示:

  • "panic" 表示软件本身存在问题,仅能由程序员修复。Postfix 无法继续运行直至问题解决。
  • "fatal" 表示由于文件缺失、权限错误或配置文件设置错误导致的问题,您可以进行修复。Postfix 无法继续运行直至问题解决。 "fatal" 表示由于文件缺失、权限错误或配置文件设置错误导致的问题,您可以进行修复。Postfix 无法继续运行直至问题解决。
  • "error" 报告错误状态。出于安全考虑,当出现超过 13 个此类错误时,Postfix 进程将终止。

"警告" 表示非致命错误。这些问题可能无法修复(例如网络中其他位置的 DNS 服务器故障),但也可能指示本地配置错误,这些错误可能在未来成为问题。

从内部调试 Postfix

Postfix 2.1 及更高版本可生成用于调试的邮件投递报告。这些报告不仅显示地址重写、别名展开或转发后的发件人/收件人地址,还显示投递到邮箱、投递到非 Postfix 命令、远程 SMTP 服务器响应等信息。

Postfix 可生成两种类型的邮件投递报告用于调试:

  • 假设模式:报告可能发生的情况,但不实际投递邮件。此模式通过以下命令请求:

    % /usr/sbin/sendmail -bv address...
    邮件投递状态报告将发送至 <;您的登录名>;。
    
  • 实际发生情况:发送邮件并报告成功或失败情况,包括远程 SMTP 服务器的回复。此操作模式通过以下方式请求:

    % /usr/sbin/sendmail -v address...
    邮件投递状态报告将发送至 <;您的登录名>;。
    

这些报告包含由 Postfix 投递代理生成的信息。由于这些代理以守护进程方式运行,无法直接与用户交互,因此结果将作为邮件发送给测试消息的发送者。这些报告的格式与普通投递失败通知几乎完全相同。

有关邮件投递状态报告的详细示例,请参阅调试部分,位于ADDRESS_REWRITING_README文档的末尾。

尝试在 master.cf 中关闭 chroot 操作

一个常见的错误是在 master.cf 文件中启用 chroot 操作,而未完成所有必要的步骤来设置 chroot 环境。这会导致 Postfix 守护进程因各种文件缺失而失败。

下面的示例展示了一个禁用 chroot 的 SMTP 服务器配置:

/etc/postfix/master.cf:
# =============================================================
# 服务类型 私人 非特权 chroot 唤醒 最大进程数 命令
# (是) (是) (是) (从不) (100)
# =============================================
smtp inet n - n - - smtpd

检查 master.cf 文件中是否有任何进程的 chroot 操作未关闭。如果发现有,请保存 master.cf 文件的副本,并编辑相关条目。执行命令 "postfix reload" 后,查看问题是否已解决。

如果关闭 chroot 操作后问题消失,那么恭喜你。以这种方式运行 Postfix 对于大多数网站来说是足够的。如果您更喜欢 chrooted 操作,请参阅 Postfix 的 BASIC_CONFIGURATION_README 文件,了解如何为 chrooted 操作准备 Postfix。

为特定 SMTP 连接启用详细日志记录

在 /etc/postfix/main.cf 中,将远程站点名称或地址添加到 debug_peer_list 参数中。例如,为了使软件将大量信息记录到系统日志守护进程中,用于来自或发往回环接口的连接:

/etc/postfix/main.cfdebug_peer_list = 127.0.0.1

您可以指定一个或多个主机、域名、地址或网络/子网掩码。要使更改立即生效,请执行命令"postfix reload"。

使用网络嗅探器记录 SMTP 会话

此示例使用 tcpdump。为了记录对话,您需要使用 "-s" 选项指定足够大的缓冲区,否则可能会丢失部分或全部数据包负载。

# tcpdump -w /file/name -s 0 host example.com and port 25

较旧版本的 tcpdump 不支持 "-s 0";在这种情况下,请使用 "-s 2000" 代替。

运行一段时间后,按 Ctrl-C 停止。要查看数据,请使用二进制查看器,如 ethereal,或经典的 less

使 Postfix 守护进程程序更详细

在 /etc/postfix/master.cf 文件中,为选定的守护进程定义添加一个或多个 "-v" 选项,然后输入 "postfix reload"。这将导致大量活动被记录到 syslog 守护进程中。例如,要使 Postfix SMTP 服务器进程更加详细:

/etc/postfix/master.cf:
smtp inet n - n - - smtpd -v

要诊断地址重写问题,请为 cleanup(8) 和/或 trivial-rewrite(8) 守护进程,并为 qmgr(8)oqmgr(8) 队列管理器,或对于 lmtp(8)local(8)pipe(8)smtp(8)虚拟(8) 交付代理。

手动跟踪 Postfix 守护进程

许多系统允许您使用系统调用跟踪器检查正在运行的进程。例如:

# trace -p 进程 ID (SunOS 4)
# strace -p 进程 ID (Linux 及许多其他系统)
# truss -p 进程 ID (Solaris、FreeBSD)
# ktrace -p 进程 ID (通用 4.4BSD)

系统库调用的跟踪信息更为详尽。示例:

# ltrace -p 进程ID (Linux,已移植到 FreeBSD 和 BSD/OS)
# sotruss -p 进程ID (Solaris)

请参阅系统文档以获取详细信息。

跟踪一个正在运行的进程可以提供有关该进程试图执行的操作的宝贵信息。这是在不运行交互式调试程序的情况下能够获取的全部信息,如后续章节所述。

自动跟踪 Postfix 守护进程

Postfix 可在守护进程启动时附加调用跟踪器。调用跟踪器有多种类型。

  1. 系统调用跟踪器,如 tracetrussstracektrace。这些工具显示进程与内核之间的通信。
  2. 库调用跟踪器,如 sotrussltrace。这些显示库例程的调用,并更好地了解进程内部正在发生的事情。

在 /etc/postfix/master.cf 文件中,在可疑命令后添加 -D 选项,例如:

/etc/postfix/master.cf:
smtp inet n - n - - smtpd -D

编辑 debugger_command 参数,使其调用您选择的调用跟踪工具,例如:

/etc/postfix/main.cfdebugger_command =
PATH=/bin:/usr/bin:/usr/local/bin;
(truss -p $process_id 2>;&;1 | logger -p mail.info) &; sleep 5

输入"postfix reload"并查看日志文件。

使用交互式ddd调试器运行守护进程

如果Postfix机器上安装了X Window系统,则可以使用交互式调试器如ddd

编辑 /etc/postfix/main. cf,使其调用 ddd

/etc/postfix/main.cfdebugger_command =
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
ddd $daemon_directory/$process_name $process_id &; sleep 5

确保 gdb 在命令搜索路径中,并导出 XAUTHORITY 以使 X 访问控制正常工作,例如:

% setenv XAUTHORITY ~/.Xauthority (csh 语法)
$ export XAUTHORITY=$HOME/.Xauthority (sh 语法)

在 /etc/postfix/master.cf 中可疑守护进程的定义后添加一个 -D 选项,例如:

/etc/postfix/master.cf:
smtp inet n - n - - smtpd -D

停止并重新启动 Postfix 系统。这是为了确保 Postfix 以正确的 XAUTHORITYDISPLAY 设置运行。

每次启动可疑守护进程时,都会弹出调试器窗口,您可以详细查看发生的情况。

使用交互式 gdb 调试器运行守护进程

如果 Postfix 机器上安装了 screen 命令,则可以按照以下方式运行交互式调试器(如 gdb)。

编辑 debugger_command 设置为以下内容,使其在脱离的 screen 会话中运行 gdb

/etc/postfix/main.cfdebugger_command =
PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; HOME=/root;
export HOME; screen -e^tt -dmS $process_name gdb
$daemon_directory/$process_name $process_id &; sleep 2

确保 gdb 在命令搜索路径中。

在 /etc/postfix/master.cf 中可疑守护进程的定义后添加一个 -D 选项,例如:

/etc/postfix/master.cf:
smtp inet n - n - - smtpd -D

执行命令"postfix reload",并等待守护进程启动(可在 maillog 文件中查看)。

然后附加到屏幕,并开始调试:

# HOME=/root screen -r
gdb) continue
gdb) where

在非交互式调试器下运行守护进程

如果 Postfix 机器上未安装 X Windows,或者您不熟悉交互式调试器,则可以尝试以非交互模式运行 gdb,并在进程崩溃时打印堆栈跟踪。

编辑 /etc/postfix/main. cf 文件,使其调用 gdb 调试器:

/etc/postfix/main.cfdebugger_command =
PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; echo
where; sleep 8640000) | gdb $daemon_directory/$process_name 
$process_id 2>;&;1
>;$config_directory/$process_name.$process_id.log &; sleep 5

在 /etc/postfix/master.cf 中,在可疑守护进程后添加一个 -D 选项,例如:

/etc/postfix/master.cf:
smtp inet n - n - - smtpd -D

输入 "postfix reload" 以使配置更改生效。

每次启动可疑的守护进程时,都会创建一个输出文件,文件名由守护进程名称和进程 ID 组成(例如 smtpd.12345.log)。当进程崩溃时,会将堆栈跟踪(包含 "where" 命令的输出)写入其日志文件。

异常行为

有时 Postfix 的行为与源代码不符。为什么程序会偏离作者提供的指令?有两种可能性。

  • 编译器出错。这种情况很少发生。
  • 硬件出错。机器是否配备 ECC 内存?

在两种情况下,正在执行的程序都不是本应执行的程序,因此可能会发生任何情况。

还有第三种可能性:

  • 系统软件(内核或库)中的错误。

与硬件相关的故障通常在断电重启系统后无法以完全相同的方式复现。对于硬件故障,Postfix 几乎无能为力。请确保使用至少能检测内存错误的硬件。否则,Postfix 只会等待被内存错误击中。关键系统应使用真正的硬件。

当编译器出现错误时,问题会在运行生成的程序时重复出现。编译器错误最可能出现在代码优化阶段。如果问题在电源循环和系统重启后仍可复现,建议尝试禁用优化重新编译 Postfix,并观察优化是否对问题有影响。

要以禁用优化方式编译 Postfix:

% make tidy
% make makefiles OPT=

这将生成一组不请求编译器优化的 Makefile。

设置好 Makefile 后,编译软件:

% make
% su
密码:
# make install

如果问题消失,那么是时候联系供应商寻求帮助了。

[email protected] 报告问题

参与 [email protected] 的人员非常乐于助人,尤其是当您提供足够的信息时。请记住,这些志愿者愿意提供帮助,但他们的时间有限。

在报告问题时,请务必包含以下信息。

  • 问题摘要。请不要仅发送日志而不解释您认为错误的地方。
  • 完整的错误信息。请使用复制粘贴或附件,而不是从记忆中复述信息。
  • Postfix 日志记录。请参阅 DEBUG_README 文档顶部的文本,以确定日志存储位置。请勿通过换行来折行显示日志,以免让协助者感到困扰。如果日志文本超过几千字节,请考虑将 URL 发布到网页或 FTP 站点。
  • 建议使用测试邮箱地址,以免泄露无辜人员的邮箱地址或密码。
  • 若无法使用测试邮箱地址,请对邮箱地址和主机名进行一致的匿名化处理。将每个字母替换为"A",每个数字替换为"D",以便协助人员仍能识别语法错误。
  • 命令输出来自:
    • "postconf -n"。请勿发送您的 main.cf 文件,或 1000 行以上的 postconf 命令输出。
    • "postconf -Mf"(Postfix 2.9 或更高版本)。
  • 更好是提供 postfinger 工具的输出。该工具可通过 https://github.com/ford--prefect/postfinger 获取。
  • 如果问题与 SASL 相关,请考虑包含 saslfinger 工具的输出。该工具可在 https://packages.debian.org/search?keywords=sasl2-bin 找到。
  • 如果问题是由于队列中邮件过多,请考虑包含qshape工具的输出,具体请参阅QSHAPE_README文件。
  • 如果问题与协议相关(连接超时,或 SMTP 服务器报告语法错误等),请考虑使用 tcpdump 记录会话,具体请参阅 DEBUG_README 文档。