给监控系统当“邮差”:一次 Postfix 只发不收邮箱的搭建实录
搞运维的同学应该都有这种体会:半夜两点被告警短信震醒,爬起来一看,发现是误报;又或者监控平台推了一堆提醒,结果全被企业邮箱当成垃圾邮件拒了。反反复复折腾之后,你会意识到一个问题——监控告警邮件,其实不需要一个功能完整的邮箱系统,它只需要一个能发信、带认证、稳定不罢工的“发信通道”就够了。
我之前一直在用某个第三方企业邮箱发监控告警,但维护的服务器多了之后,每次换邮箱密码、配频率限制都要和公司IT部门来回沟通,实在是麻烦。后来干脆自己搭了一套仅用于外发的 Postfix 邮件服务器,配合用户认证,专门给 Zabbix、Prometheus Alertmanager、各类脚本跑批结果做消息推送。这套方案跑了两年多,稳定发给 163、QQ、Gmail 和公司内部邮箱,几乎没有被拒信。今天把它完整记录下来,从需求分析到认证选型,再到最终配置和排坑,一次性讲透。
这套方案适合谁?如果你是运维工程师、SRE、后端开发,手头有监控告警、定时任务、CI/CD 流水线需要发邮件通知,又不想用第三方邮箱的复杂策略限制,那你花一个小时照着这篇文章搭一套,之后一劳永逸。全文以 Debian/Ubuntu 系统为例,其他发行版命令略有差异但思路完全相同。
1. 为什么只发不收,以及认证方案怎么选
1.1 单一职责原则下的“发信专用通道”
先说核心需求:邮件服务器只发不收,并且启用用户认证。这里面有两个关键约束,很多人第一次搭的时候容易把整件事想复杂。
只发不收意味着这台服务器不需要接收外部来信,不需要为每个员工创建邮箱账户,也不需要处理 IMAP/POP3 收件服务。它要做的只有一件事:接收来自内网监控系统或脚本的连接,通过认证后,把邮件交给上游或直接投递到目标邮箱。这么设计的好处非常明显:
- 安全面最小化。没有收件服务,就没有远程投递攻击面,别人想利用你服务器发垃圾邮件也很难突破认证层;
- 配置量大幅减少。不需要配置 virtual_mailbox_domains、不需要 Dovecot 的收件存储、不需要 SPF 的 MX 记录指向,配置量砍掉一半以上;
- 运维心智负担低。把“发信”和“收信”两种职责解耦,出问题时排查链路短,日志量小。
从架构上看,这套 Postfix 在告警链路里扮演的是一个“本地 SMTP 出口网关”的角色。监控系统调用它,它负责和外界的邮件服务器打交道。真正的难点不在 Postfix 本身,而在于如何用安全的认证方式,让合法的内网用户能发信,同时挡住非授权用户。
1.2 三种认证选型:系统用户、SASL 库、LDAP 统一认证
“用户认证”是标题里的重点,也是方案选型的核心决策点。Postfix 自身不直接管理用户密码,它是通过 SASL(Simple Authentication and Security Layer)框架来对接认证源的。常见的方案有以下三种,我按实际落地情况做个对比。
第一,系统用户认证(Cyrus SASL + sasldb)。把账号密码存在本地 sasldb 文件里,Postfix 通过 saslauthd 或 sasldb 直接验证。优点是配置简单,不需要额外依赖;缺点是账号密码和系统用户分离,账号多了不好管理,而且 saslauthd 在高并发下性能一般。
第二,Dovecot SASL 认证。Dovecot 本身是收件服务器,但它自带的 SASL 实现可以作为 Postfix 的认证后端。配置 Postfix 的 smtpd_sasl_type 为 dovecot,Postfix 把认证请求通过 Unix socket 发给 Dovecot,Dovecot 再去查 PAM、用户数据库或者 SQL。优点是灵活、性能好,而且在只需要 IMAP 认证的叠加场景下天然兼容;缺点是为实现认证,得额外安装并维护一个 Dovecot 进程。
第三,LDAP 统一用户认证。如果你的公司已经有一套 OpenLDAP 或 AD 域控,用户、密码、组织架构都在里面,那把 Postfix 直接接入 LDAP 是最高效的做法。所有后续新增用户都走 LDAP,不用在邮件服务器上单独维护账号。缺点是前期需要 LDAP 基础设施,配置链路较长,如果 LDAP 挂掉,邮件认证也会挂掉。
我最终选的是 Dovecot SASL 方案,原因后面会详细说,但你可以先记住结论:Dovecot SASL 是目前社区用得最多、资料最全、性能均衡的选择,对于 99% 的监控发信场景都够用。如果你的团队已经有 LDAP 体系,第三种的思路是等价的,只是把认证源换成 LDAP 服务器,Postfix 侧配置不变。
2. 核心原理拆解:Postfix 认证机制的两个关键概念
2.1 SMTP 会话里,认证发生在哪一步
新手最容易迷糊的一个概念是:SMTP 认证到底是怎么“插入”到发送流程里的。我们用一个标准的 SMTP 会话来说明。
一个邮件客户端发送邮件时,和服务器之间的对话大致是这样:
客户端: EHLO monitor-server 服务器: 250-mail.example.com 服务器: 250-AUTH LOGIN PLAIN 服务器: 250-AUTH=LOGIN 服务器: 250-PIPELINING 服务器: 250 8BITMIME 客户端: AUTH LOGIN 服务器: 334 VXNlcm5hbWU6 客户端: dXNlcg== 服务器: 334 UGFzc3dvcmQ6 客户端: cGFzc3dvcmQ= 服务器: 235 2.7.0 Authentication successful 客户端: MAIL FROM:<alert@example.com> 服务器: 250 2.1.0 Ok 客户端: RCPT TO:<ops@example.com> 服务器: 250 2.1.5 Ok 客户端: DATA 服务器: 354 End data with <CR><LF>.<CR><LF> 客户端: From: <alert@example.com> 客户端: To: <ops@example.com> 客户端: Subject: test 客户端: 客户端: hello 客户端: . 服务器: 250 2.0.0 Ok: queued as 4E2A1F1A3仔细看这个过程,客户端是在发送 MAIL FROM(信封发件人)之前先执行了 AUTH 命令。也就是说,认证是发生在 SMTP 会话的中前段,只有认证成功之后,服务器才会接受你的“发件人声明”和“收件人声明”。如果没有认证直接发 MAIL FROM,服务器会返回530 5.7.0 Authentication Required。
Postfix 的配置里,smtpd_recipient_restrictions或smtpd_relay_restrictions中会用到permit_sasl_authenticated这个规则,它的意思就是:如果客户端完成了 SASL 认证,那就放行后续的 RCPT TO 指令。反过来说,如果客户端没认证,且来源 IP 不在mynetworks白名单里,RCPT TO 直接拒绝。这是整个只发不收方案的“门禁”核心。
2.2 Cyrus SASL 与 Dovecot SASL:到底该信谁
Postfix 主配置里有两个参数控制 SASL 的实现方式:smtpd_sasl_type和smtpd_sasl_path。前者指定用哪种 SASL 实现(cyrus 或 dovecot),后者指定认证 socket 的路径。
Cyrus SASL 是更“原教旨”的 SASL 实现,它有一套自己的密码库(sasldb)、插件机制和认证协议。配置上通过smtpd_sasl_type = cyrus,然后由 saslauthd 或 sasldb 来完成具体验证。它的优势是对 SASL 协议族支持最全面,包括 LOGIN、PLAIN、CRAM-MD5、DIGEST-MD5 等;劣势也比较明显:配置文件分散在/etc/sasl2/和/etc/default/saslauthd两处,权限、参数、用户库三个地方都是坑,稍有不慎就出现“认证失败但查不到日志”的诡异问题。
Dovecot SASL 的优势在于,Dovecot 本身把认证模块做得非常成熟和完善,无论是 PAM 还是 SQL 还是 LDAP,都有文档化的标准配置模板。Postfix 只需要通过 Unix socket 与 Dovecot 通信,认证过程完全由 Dovecot 接管。另外,Dovecot 的认证进程是事件驱动模型,性能在低并发场景下和 Cyrus 几乎没差别,但稳定性好了很多。基于这些原因,我最终选了 Dovecot SASL。
有一点必须提醒你:不管选哪种 SASL 实现,Postfix 的 chroot 机制会影响 socket 路径的解析。Debian 系默认 Postfix 是进 chroot 的,所以smtpd_sasl_path = private/auth实际对应的文件系统路径是/var/spool/postfix/private/auth。你手工ls /var/spool/postfix/private/auth如果能看到 socket 文件,说明 Dovecot 已经正确创建了这个文件。很多人报“No such file or directory”,就是用宿主机路径去猜了,没想到 chroot 前缀。
3. 实操:一步步搭出只发不收、带认证的 Postfix
3.1 环境准备和初始配置
本次实操环境:Ubuntu 22.04 LTS,Postfix 3.6,Dovecot 2.3.16。主域名用monitor.example.com,发件地址统一用alert@monitor.example.com。你按自己的域名替换即可。
先更新系统包并安装必要组件:
sudo apt update sudo apt install postfix dovecot-core dovecot-imapd mailutils -y安装过程中 Postfix 会弹出一个配置界面,选择“Internet Site”,然后在“System mail name”里填你的域名。别纠结,之后还能改。装了mailutils是为了测试时能直接用sendmail或mail命令发信,非常方便。
检查 Postfix 是否已经运行:
sudo systemctl status postfix确认状态为 active 后,我们先看几个默认配置项,这些通常在/etc/postfix/main.cf中。现在的发行版安装后会自带注释完整的模板,但我们要改掉其中一半以上,所以不用保守,大胆编辑。
3.2 编辑 main.cf:定义监听的网卡和协议
开始改配置文件之前,把最终要用的 main.cf 核心片段先贴出来。这样改的时候心里有数,不会边看代码边迷茫。
sudo vim /etc/postfix/main.cf我的整体配置如下,为了省篇幅,注释信息保留了关键行,其余无关的行删掉即可。
# 主机名,最好是 FQDN,且能解析到本机,避免一些邮件服务器校验失败 myhostname = monitor.example.com # 发件地址的域名后缀。只发不收,不需要多个域名 mydomain = monitor.example.com myorigin = $mydomain # 只监听本机回环地址,避免外部直接连上来发信 inet_interfaces = loopback-only # 关键:无需收信,所以不监听 110/143 等端口的投递,只做发送 mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain # 允许中继的来源:本机 + 内网指定网段(按需放开) mynetworks = 127.0.0.0/8, ::1/128, 10.0.0.0/8 # 不做本地投递,所有邮件都往外送 local_transport = error:local delivery disabled保存后,执行postfix check检查有没有语法错误。这个命令会把配置项的语法、引用关系过一遍,有错误会直接报出来。注意inet_interfaces = loopback-only这个非常关键,它决定了 Postfix 只监听 127.0.0.1 这个地址,外部网络连不进来。
有个细节:有人问“既然只监听回环,那内网其他机器怎么连上来发信?”答案是通过 SSH 隧道或者监控服务本身就部署在同一台服务器上。对于大部分监控场景,Zabbix Server、Prometheus、脚本跑批和 Postfix 在同一台机器,用回环就足够了。如果非要让内网其他服务器直连,那就把inet_interfaces改成本机内网 IP,同时确认防火墙只放行可信来源访问 25 端口。
3.3 启用 SASL 并配置 Dovecot 认证文件
改完 main.cf 后,在文件末尾追加以下内容来启用 SASL 认证:
# 启用 SMTP 认证 smtpd_sasl_auth_enable = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_local_domain = $myhostname broken_sasl_auth_clients = yes # 中继限制:认证用户 + 内网白名单才给投递 smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination解释一下几个容易懵的参数:
smtpd_sasl_local_domain:这个参数是告诉 Postfix 把 AUTH 登录的用户名按什么域名做归一化。如果用户名是alert,客户端用alert或alert@monitor.example.com登录,最终都会被转成完整形式。建议设成$myhostname,避免不同写法导致认证不一致。broken_sasl_auth_clients = yes:兼容某些老旧客户端实现 AUTH 时不按标准带=LOGIN后缀的情况。开启后一般没副作用,建议保留。smtpd_relay_restrictions:这是新版 Postfix 专门负责“谁允许中继”的规则表。老版本其实用smtpd_recipient_restrictions也能实现,但官方从 2.10 开始引入smtpd_relay_restrictions,优先级更高,且“中继”逻辑和“收件限制”逻辑分离,更清晰,也更安全。
接着配置 Dovecot 侧的认证 socket。编辑/etc/dovecot/conf.d/10-master.conf,确保service auth那个段落里开启 Unix socket 监听:
sudo vim /etc/dovecot/conf.d/10-master.conf找到service auth配置块,默认内容可能类似:
service auth { # unix_listener auth-userdb { # mode = 0600 # user = postfix # } }我们要改成 Postfix 可访问的 socket:
service auth { unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } }这里重点强调:socket 必须落在 Postfix 的 chroot 目录下,也就是/var/spool/postfix/private/。而且权限要对,Postfix 进程要能读写这个 socket。mode = 0660加上user = postfix、group = postfix,是实际中最稳的组合。如果你发现启动后 Postfix 还是连不上,八成就是权限不对,改成0666能快速验证问题,但生产环境不建议一直开 0666。
然后编辑/etc/dovecot/conf.d/10-auth.conf,启用明文认证并放开 PLAIN/LOGIN 机制:
sudo vim /etc/dovecot/conf.d/10-auth.conf找到disable_plaintext_auth这一行,改成:
disable_plaintext_auth = no再找到auth_mechanisms,确认包含:
auth_mechanisms = plain login为什么允许明文认证?因为 Postfix 和 Dovecot 之间走的是 Unix socket,不经过网络传输,明文也无所谓。如果你要面对的是网络环境中的 SASL auth,那另说,正常用 SSL/TLS 包裹 SMTP 会话即可,这里不展开。
最后,Dovecot 验证用户的方式默认走系统 PAM。也就是说,只要你在服务器上创建了系统账号,就可以用它登录 SMTP 发信。为了安全,我单独建一个专用账号,并把它锁在 shell 上,算是给发信用户一个“最小权限”:
sudo useradd -r -s /usr/sbin/nologin alert sudo passwd alertpasswd 会让你输入两遍密码,这个密码就是后面监控系统要用到的认证密码。
3.4 重启服务与端口验证
配置改完,重启服务让配置生效:
sudo systemctl restart dovecot sudo systemctl restart postfix然后检查两个关键状态:
sudo systemctl status postfix --no-pager sudo systemctl status dovecot --no-pager再确认 Postfix 监听了 25 端口、Dovecot 没有对外监听端口(因为它只给 Postfix 提供 socket 认证):
sudo ss -lntp | grep ':25 ' sudo ls -l /var/spool/postfix/private/auth如果一切正常,你会看到类似输出:
srw-rw-r-- 1 postfix postfix 0 Jan 20 11:22 /var/spool/postfix/private/auth这个 socket 文件就是 Postfix 和 Dovecot 之间的“电话线”。线接通了,认证就通。
3.5 用 swaks 做一轮完整测试
安装一个 swaks 工具,它是测 SMTP 的神器,比 telnet 高效太多了:
sudo apt install swaks -y swaks --to ops@example.com --from alert@monitor.example.com \ --server 127.0.0.1 --auth LOGIN --auth-user alert --auth-password '你的密码' \ --header "Subject: Postfix auth test" --body "Testing postfix with auth."其中ops@example.com换成任何真实可接收的邮箱,比如个人 Gmail。如果配置没问题,输出会看到:
-> 235 2.7.0 Authentication successful -> 250 2.0.0 Ok: queued as ...最后查一下收件人邮箱,会收到这封测试邮件。这里的“成功”包含两层含义:一是 Postfix 接收了这封信,二是有外部 MTA 接受了这封信的投递。如果只看到第一层,第二层失败的话,要去查mail.log。
我实际测试时曾经碰到 Gmail 拒信,提示“Technical details of temporary failure: 4.7.1”之类的,最后发现是 PTR 记录不匹配。也就是说,发信服务器的 IP 反向解析域名和 HELO 主机名对不上。这个问题困了很多新手,后面章节我会专门列出来讲。
4. 上生产前必须想清楚的细节:DNS、防火墙和发件人策略
4.1 SPF、DKIM、PTR:少一个都可能进垃圾箱
很多自建邮件服务器,明明发信、收信、认证全没问题,但对方邮箱就是收不到,或者进了垃圾箱。原因基本都在 DNS 配置上。这里讲三个必备点。
SPF(Sender Policy Framework):在 DNS 里添加一条 TXT 记录,声明“哪些 IP 被允许以你的域发送邮件”。例如:
monitor.example.com. IN TXT "v=spf1 ip4:你的服务器公网IP ~all"直白点说,SPF 就是一个“发件人白名单”。没有 SPF,接收方大概率对来自你域的信存疑。
DKIM(DomainKeys Identified Mail):用来给发出的邮件做数字签名,收件人通过 DNS 获取公钥验证签名。Postfix 可以通过OpenDKIM实现签名。配置不复杂,但涉及生成密钥、DNS 记录和 main.cf 集成三个环节,值得之后单独写一篇。如果只是给监控告警用,可以先不配,但进了垃圾箱别奇怪。如果要求高,就按官方文档配置完整。
PTR(Pointer Record):这是由 IP 所属方(通常是机房或云厂商)配置的反向解析。服务器 IP 的反解结果要尽量和myhostname一致。例如你的服务器 IP 是 1.2.3.4,myhostname 是monitor.example.com,那 1.2.3.4 的 PTR 必须指向monitor.example.com。主机名与 IP 对应关系是 Gmail、Outlook 等大型邮箱系统直接检查的硬指标。
4.2 mynetworks 白名单和密码认证的边界
在 main.cf 里,我设置了mynetworks = 127.0.0.0/8, ::1/128, 10.0.0.0/8。这意味着内网 10.0.0.0/8 网段的机器可以不认证直接发信。这个设计是故意的——很多内网监控脚本没有“优雅地携带认证”的能力,直接允许受信网段投递最方便。
但要注意,这台 Postfix 如果部署在云上,公网 IP 很容易被扫描到。即使只用回环地址,如果云安全组里不小心放行了 25 端口,外部流量一样能进来。所以哪怕只监听loopback-only,我也建议在云控制台的安全组里把入方向 25 端口彻底关掉,双保险。
我做安全测试的时候试过:在安全组放行 25 端口的瞬间,日志里就会涌现大量"SASL authentication failed"或"connect from unknown"记录。所以端口层面最好把所有外部到 25 的入站流量都毙掉,只在服务器内部开放。
4.3 单点登录和 LDAP 延伸:认证源再向前一步
标题的热词里有“LDAP统一用户认证和单点登录”,结合我们的场景说几句。如果你所在团队已经有 OpenLDAP 或 AD 域控,那完全可以跳过服务器上的系统用户认证,让 Dovecot 直接对接 LDAP。Dovecot 有专门的 LDAP 认证支持,在/etc/dovecot/dovecot-ldap.conf.ext里配置服务器地址、base DN 和绑定 DN,然后在10-auth.conf里启用ldap的 passdb 和 userdb 即可。
这样做的收益是显而易见的:新同事入职,管理员只需要在 LDAP 里加一条账号记录,邮件发信权限自动拥有;离职时只需禁用 LDAP 账号,认证立刻失效,不用跑到每台服务器上删系统用户。对于成规模的运维团队,这减少了巨大的账号维护成本,也避免出现“人走了密码还留在邮箱配置里”的安全隐患。
LDAP 配置里必须特别注意超时设置。我当时调了一段时间,最后把dovecot-ldap.conf.ext里connect_timeout设为 5 秒,request_timeout也设为 5 秒。这样 LDAP 一旦出现故障,认证请求会在 5 秒内失败返回,而不是卡住整个 SMTP 会话。否则监控系统发信超时,告警反而变成另外一种故障。
5. 常见问题与排查技巧实录
5.1 认证失败反复出现,日志到底看哪里
Postfix 和 Dovecot 的日志分散在两个地方:/var/log/mail.log(或/var/log/maillog)和 Dovecot 自己的日志。Debian/Ubuntu 上默认通过 rsyslog 统一收口,用 journalctl 也能看:
sudo journalctl -u postfix -u dovecot --no-pager -n 100认证失败的经典报错长这样:
postfix/smtpd[1234]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed: authentication failure看到这句话,说明 Postfix 本身收到了认证请求,但下游验证没过。这时候要去 Dovecot 的日志里找更底层的原因:
sudo journalctl -u dovecot --no-pager -n 100常见原因有三个:
- 密码错误。这最好排查,重新
passwd一次即可。 - Dovecot 和 Postfix 之间的 socket 权限不通。检查
/var/spool/postfix/private/auth的属主和权限,确认 Postfix 用户有读写权限。 - PAM 配置异常。检查
/etc/pam.d/dovecot是否存在且内容正确,有时系统升级会覆盖掉这个文件。
5.2 邮件已排队,但对方就收不到
Postfix 显示queued as,但收件箱迟迟没到货,分两种情况。
第一种,查看 mail.log 看到类似host gmail-smtp-in.l.google.com[142.250.x.x] said: 421 4.7.0的字样。这属于被对方临时拒绝,通常和 IP 信誉有关。如果是新 IP、机房 IP、动态 IP,Gmail 基本都有临时限制。除了确认 PTR 和 SPF 之外,也可以尝试从收件人到发件人一封一封“暖机”发送,头部邮件内容越接近真实业务越好。
第二种,对方直接 550 拒绝。常见原因是 SPF 没配或者配错、发件域名和 myorigin 不一致、收件方有铁了心的反垃圾策略。处理办法是右键查看原始邮件,里面会有Authentication-Results头,能清楚看到 SPF、DKIM 是被 pass 还是 fail。
5.3 发送频率过高触发热门邮箱限流
监控告警有个特点:不分白天黑夜,系统抽风时可能就是每分钟一封。对于个人邮箱来说,五分钟内在同一主题发送 50 封以上,很容易触发限流。建议在 Postfix 层面直接做发送限速,通过anvil模块控制每个客户端的连接频率和每次会话的邮件数:
smtpd_client_connection_rate_limit = 10 smtpd_client_message_rate_limit = 30这两行参数放 main.cf 里,能有效防止某个告警循环把整个发信通道打挂。我见过真实案例:监控系统的一个死循环脚本,半天把邮件队列塞爆,导致积压了几万封,把磁盘直接撑满。有了限速之后就再没出过这种事故。
5.4 忘记密码或需要轮换密码后的处理
监控脚本里永远有一个最大的安全黑洞:明文密码。不管是 Zabbix 的告警媒介脚本还是 Alertmanager 的 email_config,密码都以可读形式躺在配置里。需要定期轮换时,直接执行:
sudo passwd alert改完之后重启 Postfix 和 Dovecot 不是必需的,但如果你发现某些客户端还在用旧密码连接且认证失败,建议还是重启一下两个服务,确保连接池里的旧认证状态被清干净。
6. 从零到可用的完整清单
最后把配置思路整理成一张速查表,方便你部署时逐项对照:
| 项目 | 配置动作 | 验证命令 |
|---|---|---|
| 关闭收信 | inet_interfaces = loopback-only | ss -lntp | grep ':25 ' |
| 无本地投递 | local_transport = error:local delivery disabled | 内网发信测试 |
| 启用认证 | smtpd_sasl_auth_enable = yes | telnet 127.0.0.1 25 |
| Dovecot socket | /var/spool/postfix/private/auth | ls -l /var/spool/postfix/private/auth |
| 中继限制 | permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination | 未认证发信测试 |
| 创建用户 | useradd alert+passwd alert | swaks --auth LOGIN |
| DNS 记录 | SPF 必须,PTR/DKIM 按需 | dig TXT monitor.example.com |
| 限速 | smtpd_client_message_rate_limit | 观察 mail.log |
这套配置跑下来,我最大的体会是:自建发信服务器的难点不是“让它跑起来”,而是“让它发出的信被真正信任”。认证只是门禁,真正决定邮件会不会到达收件箱的,是 SPF、DKIM、PTR 这些落在邮件系统之外的功夫。
如果你只是给监控告警用,直接按上面的步骤操作,半小时内就能收到第一封由自己服务器发出来的邮件。等把这个通道跑顺了,再回头看,你会发现这套“只发不收”的 Postfix,其实是你所有监控体系里最稳的那根绳子——它平时不出声,但每次系统抽风,都是它在第一时间叫醒你。