背景
某内部环境中有两台 Debian 系服务器,分别承担虚拟化管理与备份服务。两台机器都不是实际的公网邮件服务器,也不直接对外投递邮件,而是作为本机系统邮件的提交端使用 Postfix。
整体结构如下:
内部主机 A / 内部主机 B
↓
本机 Postfix
↓
专用邮件服务器 mail.example.net:<RELAY_PORT>
↓
外部邮件系统
其中:
mail.example.net
代表真正负责对外收发和中继的邮件服务器。
两台内部主机上的 Postfix 只负责把本机系统邮件、告警邮件、root 邮件等提交给该邮件服务器。两台机器各自对应一个真实存在的邮件账户:
host-a@example.net
host-b@example.net
这两个账户在邮件系统中是独立账户,均可正常收发邮件。
原始状态
两台机器的对外发信功能已经长期正常运行,说明核心 SMTP 中继链路本身没有问题。
关键配置大致如下:
relayhost = [mail.example.net]:<RELAY_PORT>
smtp_tls_wrappermode = yes
smtp_tls_security_level = encrypt
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_generic_maps = hash:/etc/postfix/generic
inet_interfaces = loopback-only
这些项目是邮件能否成功提交到外部邮件服务器的核心配置,因此整理过程中没有改动。
真正需要检查的是一些非核心但影响一致性和邮件头表现的项目:
myhostname
mydomain
generic 地址改写表
inet_protocols
smtputf8_enable
发现的问题
检查后发现两台机器的核心中继配置一致,但 Postfix 自身身份和地址改写规则并不完全一致。
主要差异包括:
1. myhostname 不一致
主机 A 使用 mail.example.net
主机 B 使用 localdomain 风格的默认主机名
2. mydomain 不一致
主机 A 使用 example.net
主机 B 使用 localdomain
3. generic 地址改写表仍保留旧的本地域名
例如 host-a.old-local、host-b.old-local 之类旧名称
4. inet_protocols 不一致
一台为 ipv4
一台为 all
5. smtputf8_enable 不一致
一台使用默认值
一台显式关闭
同时,系统层面的主机名解析也进行了确认:
/etc/hostname
/etc/hosts
hostname
hostname -f
hostname -s
getent hosts
整理后,两台机器的系统层面主机名保持简单结构:
主机 A:
/etc/hostname = host-a
hostname -f = host-a.lan
主机 B:
/etc/hostname = host-b
hostname -f = host-b.lan
这里的 host-a.lan、host-b.lan 只作为内部局域网身份使用,不等同于公网管理入口,也不等同于实际邮件服务器。
generic 是什么
generic 指的是 Postfix 的出站地址改写表,对应配置项是:
smtp_generic_maps = hash:/etc/postfix/generic
它不负责连接 SMTP 服务器,不负责 TLS,不负责 SASL 登录,也不负责端口配置。
它的作用是:在邮件交给外部邮件服务器之前,把本机生成的发件人地址改写成真实存在的邮箱账户。
例如:
root
root@host-a.lan
root@mail.example.net
在主机 A 上统一改写为:
host-a@example.net
在主机 B 上统一改写为:
host-b@example.net
这样做的目的,是避免本机系统邮件以 root@localhost、root@host-a.lan、root@localdomain 这类内部地址对外出现。
整理原则
本次整理遵循几个原则:
1. 不改动 relayhost
2. 不改动 SMTP 端口
3. 不改动 TLS 配置
4. 不改动 SASL 认证配置
5. 不改动 /etc/postfix/sasl_passwd
6. 不改动 master.cf
7. 不把内部主机当成真正的公网邮件服务器
8. 只整理身份标识和地址改写层
换句话说,发信链路保持原样,只对 Postfix 的身份显示和出站发件人改写规则做一致化处理。
修改内容
1. 统一 myhostname 和 mydomain
两台机器统一为:
myhostname = mail.example.net
mydomain = example.net
这样做的目的,是让两台内部主机在 Postfix 层面统一使用邮件系统的主身份。
需要注意的是,这并不表示两台内部主机变成了邮件服务器。真正的邮件服务器仍然是:
mail.example.net:<RELAY_PORT>
内部主机仍然只是通过 relayhost 提交邮件。
2. 整理 generic 地址改写表
主机 A 的 /etc/postfix/generic 整理为类似结构:
root host-a@example.net
root@localhost host-a@example.net
root@mail.example.net host-a@example.net
@mail.example.net host-a@example.net
root@host-a host-a@example.net
root@host-a.lan host-a@example.net
@host-a.lan host-a@example.net
root@host-a.old-local host-a@example.net
@host-a.old-local host-a@example.net
主机 B 的 /etc/postfix/generic 整理为类似结构:
root host-b@example.net
root@localhost host-b@example.net
root@mail.example.net host-b@example.net
@mail.example.net host-b@example.net
root@host-b host-b@example.net
root@host-b.lan host-b@example.net
@host-b.lan host-b@example.net
root@host-b.old-local host-b@example.net
@host-b.old-local host-b@example.net
root@host-b.localdomain host-b@example.net
@host-b.localdomain host-b@example.net
整理后执行:
postmap /etc/postfix/generic
用于重新生成:
/etc/postfix/generic.db
3. 统一 inet_protocols
两台机器统一为:
inet_protocols = ipv4
由于两台机器均为:
inet_interfaces = loopback-only
因此这项主要是为了保持行为一致,避免一台监听 IPv4 loopback,另一台同时监听 IPv6 loopback。
4. 启用 smtputf8_enable
两台机器统一为:
smtputf8_enable = yes
当前使用的邮箱地址均为 ASCII 地址,因此启用该项不会改变现有发信行为。统一启用后,两台机器在 Postfix 行为上更加一致。
未修改的内容
本次没有修改以下关键配置:
relayhost = [mail.example.net]:<RELAY_PORT>
smtp_tls_wrappermode = yes
smtp_tls_security_level = encrypt
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
inet_interfaces = loopback-only
也没有修改:
/etc/postfix/sasl_passwd
/etc/postfix/master.cf
真正邮件服务器配置
公网 DNS 配置
管理网页反向代理配置
证书配置
这部分是整个发信功能的核心,不应在身份整理过程中顺手改动。
验证方式
整理后检查 Postfix 关键配置:
postconf myhostname mydomain myorigin relayhost inet_interfaces inet_protocols smtputf8_enable smtp_generic_maps
期望看到:
myhostname = mail.example.net
mydomain = example.net
myorigin = $myhostname
relayhost = [mail.example.net]:<RELAY_PORT>
inet_interfaces = loopback-only
inet_protocols = ipv4
smtputf8_enable = yes
smtp_generic_maps = hash:/etc/postfix/generic
检查 generic 改写是否生效:
postmap -q root hash:/etc/postfix/generic
postmap -q root@mail.example.net hash:/etc/postfix/generic
postmap -q root@host-a.lan hash:/etc/postfix/generic
主机 A 应返回:
host-a@example.net
主机 B 应返回:
host-b@example.net
最后执行:
postfix check
postfix reload
确认配置可被 Postfix 正常加载。
关于警告信息
整理过程中可能出现类似提示:
Postfix is using backwards-compatible default settings
这通常与 compatibility_level 有关,不代表本次修改导致发信异常。
也可能出现 chroot 环境中的库文件版本提示,例如:
/var/spool/postfix/... and /lib/... differ
这类提示和本次 myhostname、generic、inet_protocols、smtputf8_enable 整理不是同一个问题。若邮件功能正常,可以单独记录,不应与本次整理混在一起处理。
最终结果
整理完成后,两台机器达到以下状态:
1. 系统层面主机名清晰
host-a.lan
host-b.lan
2. Postfix 层面邮件系统身份统一
myhostname = mail.example.net
mydomain = example.net
3. 出站发件人地址按机器分别改写
主机 A → host-a@example.net
主机 B → host-b@example.net
4. SMTP 中继链路保持不变
relayhost、TLS、SASL 均未改动
5. inet_protocols 统一为 ipv4
6. smtputf8_enable 统一启用
7. 两台机器对外发信均保持正常
这次整理的关键点不是“把内部主机变成邮件服务器”,而是在不影响已有中继发信链路的前提下,让两台内部服务器的 Postfix 身份、协议行为和发件人改写规则更加一致。
最重要的经验是:邮件系统整理时必须先分清层级。
relayhost / TLS / SASL
决定能不能连接真正的邮件服务器
generic
决定本机生成的发件人地址如何改写
/etc/hostname / /etc/hosts
决定系统本机名称解析
myhostname / mydomain
决定 Postfix 自身身份
真正邮件服务器
负责最终对外收发邮件
只有明确这些层级,才能避免把一个已经稳定运行的邮件中继配置改坏。