Postfix+Dovecot系统限制仅转发用户邮件接收范围及SPF相关配置咨询
嘿,这个场景我太熟了——不少用Postfix+Dovecot搭企业邮箱的朋友都踩过类似的坑,咱们一步步拆解解决,既要满足你的核心需求,又要把SPF相关的麻烦彻底摆平。
先把核心需求掰扯明白
你要的效果其实可以拆成两条硬规则:
- 有本地邮箱的用户:敞开收所有来源的邮件,没任何限制
- 仅转发的用户:只允许接收来自你内部域名(
example.com)的邮件,外部发件人的直接拒掉
之前被拉黑的问题,本质是转发了外部发件人的邮件到外部地址,导致SPF验证失败(你的服务器不在原发件人域名的SPF记录里)。现在咱们直接拦掉这类邮件,这个问题自然就消失了,根本轮不到SPF验证那一步。
第一步:给Postfix加「用户类型识别+收件范围限制」的核心规则
这部分要让Postfix能自动判断某个收件人属于哪类用户,然后执行对应的规则。咱们用数据库查询配合Postfix的访问控制来实现:
1. 写一个能识别用户类型的SQL查询
假设你的用户别名存在virtual_aliases表(字段address是收件地址,target是目标地址),咱们需要一个能递归解析别名链的查询,判断最终是否指向本地邮箱(比如target以/结尾):
WITH RECURSIVE alias_chain AS ( SELECT address, target FROM virtual_aliases WHERE address = '%s' UNION ALL SELECT va.address, va.target FROM virtual_aliases va JOIN alias_chain ac ON va.address = ac.target WHERE va.target NOT LIKE '%/' -- 没到本地邮箱就继续递归 ) SELECT 'OK' FROM alias_chain WHERE target LIKE '%/' UNION SELECT 'PERMIT_INTERNAL_ONLY' FROM dual WHERE NOT EXISTS (SELECT 1 FROM alias_chain WHERE target LIKE '%/')
这个查询的逻辑是:如果收件人的别名链最终指向本地邮箱,返回OK(允许所有来源);否则返回PERMIT_INTERNAL_ONLY(仅允许内部发件人)。
2. 把查询做成Postfix的访问映射
在/etc/postfix/mysql/目录下新建check_recipient_type.cf文件,内容如下(替换成你的数据库信息):
user = postfix_db_user password = 你的数据库密码 dbname = postfix_db hosts = 127.0.0.1 query = WITH RECURSIVE alias_chain AS (SELECT address, target FROM virtual_aliases WHERE address = '%s' UNION ALL SELECT va.address, va.target FROM virtual_aliases va JOIN alias_chain ac ON va.address = ac.target WHERE va.target NOT LIKE '%/') SELECT 'OK' FROM alias_chain WHERE target LIKE '%/' UNION SELECT 'PERMIT_INTERNAL_ONLY' FROM dual WHERE NOT EXISTS (SELECT 1 FROM alias_chain WHERE target LIKE '%/')
3. 定义「仅允许内部发件人」的规则类
在Postfix的main.cf里添加以下配置:
# 自定义规则类:仅允许内部域名发件人 smtpd_restriction_classes = permit_internal_only permit_internal_only = check_sender_access hash:/etc/postfix/internal_senders, reject -- 不符合条件直接拒绝
4. 配置内部发件人列表
新建/etc/postfix/internal_senders文件,内容就是你的内部域名:
example.com OK
然后执行postmap /etc/postfix/internal_senders生成Postfix能识别的哈希表。
5. 把规则加到Postfix的收件限制链里
在main.cf的smtpd_recipient_restrictions里,把咱们的检查规则加到合适的位置(比如在SPF/DKIM检查之后,默认拒绝规则之前):
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, check_recipient_access mysql:/etc/postfix/mysql/check_recipient_type.cf, check_policy_service unix:private/policyd-spf, -- 你的现有SPF检查 check_milter inet:127.0.0.1:8891, -- 你的现有DKIM检查 reject_unauth_destination
记得执行postfix reload让配置生效。
第二步:搞定SPF相关的收尾工作
现在咱们已经拦掉了外部发件人给仅转发用户的邮件,之前的拉黑/SPF问题就解决了:
- 直接关掉SRS:如果你之前开了SRS(发件人重写服务),现在完全用不上了——因为你不再转发外部发件人的邮件,留着SRS反而可能引入不必要的复杂度。
- 强化内部发件人验证(可选):如果想更严谨,可以在
permit_internal_only规则里加一条check_sender_spf pass,确保只有通过SPF验证的内部发件人才能给仅转发用户发邮件(不过内部发件人本来就会过SPF,这步算是双重保险):permit_internal_only = check_sender_spf pass, check_sender_access hash:/etc/postfix/internal_senders, reject
几个测试&排查的小技巧
- 用内部发件人(比如
test@example.com)给仅转发用户发邮件,看是否能正常接收; - 用外部发件人(比如
test@gmail.com)给仅转发用户发邮件,看是否被拒绝; - 查看Postfix日志
/var/log/mail.log,如果有问题,日志里会明确显示是因为哪个规则被拒绝的; - 测试别名链的解析:比如
c.chip@example.com指向compuchip@example.com(仅转发用户),要确保Postfix能正确识别c.chip是仅转发用户。
备注:内容来源于stack exchange,提问作者CompuChip

