Exim 4.95 配置参数详解
吃透 Ubuntu 22.04 仓库版 Exim 4.95 的 Debian 配置框架:update-exim4.conf.conf 部署型变量、单一/分片双模式的宏注入差异、MAIN 宏与主配置选项、ACL 中继三态控制、路由器与传输器、别名与 .forward、SMTP AUTH 认证,全部按「修改 → 重新生成 → 验证」闭环实测,附生产配置模板与排障 FAQ。
Exim 4.95 配置参数详解
目标:吃透 Ubuntu 22.04 仓库版 Exim 4.95 的 Debian 配置框架——
update-exim4.conf.conf部署型变量、MAIN 宏、ACL、路由器、传输器与别名/转发/认证,每个参数按「修改 → 重新生成 → 验证」闭环实测。所有命令与回显均来自实测(实测日期:2026-10-26)。
一、环境信息
| 项目 | 取值 |
|---|---|
| 操作系统 | Ubuntu 22.04(jammy,x86_64) |
| 软件版本 | Exim 4.95(4.95-4ubuntu2.12) |
| 主 MTA | mail1.example.org(10.89.61.11),邮件域 example.org,分片配置(use_split_config=true) |
| 次级 MTA | mail2.example.net(10.89.61.12),邮件域 example.net,单一配置文件(use_split_config=false) |
| 服务端口 | 25/tcp(SMTP,STARTTLS 可用) |
| 配置文件 | /etc/exim4/update-exim4.conf.conf、/etc/exim4/conf.d/(分片)或 /etc/exim4/exim4.conf.template(单一) |
| 生成配置 | /var/lib/exim4/config.autogenerated |
| 日志 | /var/log/exim4/mainlog、/var/log/exim4/rejectlog |
| 测试账号 | root 与 admin(口令 q1w2e3r4) |
两台机器特意保持两种配置形态:mail1 走分片(便于逐文件讲解 conf.d),mail2 走单一文件(便于讲解模板与宏),覆盖两种运维形态。
二、前置条件
两台机器已按《Ubuntu 22.04 安装 Exim 4.95 指南》完成安装与 internet site 模式配置。验证基线:
root@mail1:~# exim4 --version | head -1
Exim version 4.95 #2 built 22-Sep-2026 18:29:28
root@mail1:~# systemctl is-active exim4
active
root@mail1:~# exim4 -bV | tail -1
Configuration file is /var/lib/exim4/config.autogenerated
exim4 -bV 的最后一行永远显示当前实际生效的配置文件路径,是排查「改了配置没生效」的第一入口。
三、配置文件体系总览
3.1 配置文件清单
| 文件 | 作用 |
|---|---|
/etc/exim4/update-exim4.conf.conf | Debian 框架的部署型变量(dc_*),framework 的「开关面板」 |
/etc/exim4/conf.d/ | 分片配置的片段目录(40 个文件,按序号拼接) |
/etc/exim4/exim4.conf.template | 单一配置模式的模板(与 conf.d 内容同源) |
/var/lib/exim4/config.autogenerated | update-exim4.conf 的生成物,exim4 实际加载的配置 |
/etc/exim4/exim4.conf.localmacros | 单一模式下的本地宏注入文件 |
/etc/exim4/conf.d/main/000_localmacros | 分片模式下的本地宏注入文件(文件名以 000 开头保证最先拼接) |
/etc/exim4/hubbed_hosts | hubby 路由器的手工路由表(域名: 目标主机) |
/etc/aliases | 系统别名表(system_aliases 路由器读取) |
/etc/exim4/passwd.client | 作为客户端发往 smarthost 时的认证口令库 |
3.2 生成与生效流程
任何配置修改后的生效链路固定为三步:
root@mail1:~# update-exim4.conf && systemctl reload exim4
- 编辑源文件(
dc_*变量 / conf.d 片段 / 模板 / 宏文件); update-exim4.conf把源文件拼装为/var/lib/exim4/config.autogenerated(语法错误时直接失败,不会生成坏配置);systemctl reload exim4(或 restart)让守护进程重新加载。
3.3 单一配置 vs 分片配置
由 dc_use_split_config 决定。mail1 从单一模式切换到分片模式:
root@mail1:~# sed -i "s/^dc_use_split_config=.*/dc_use_split_config='true'/" /etc/exim4/update-exim4.conf.conf
root@mail1:~# update-exim4.conf && systemctl restart exim4
root@mail1:~# ls /etc/exim4/conf.d/
acl auth main retry rewrite router transport
root@mail1:~# for d in /etc/exim4/conf.d/*/; do echo "$d $(ls $d | wc -l) files"; done
/etc/exim4/conf.d/acl/ 5 files
/etc/exim4/conf.d/auth/ 2 files
/etc/exim4/conf.d/main/ 4 files
/etc/exim4/conf.d/retry/ 2 files
/etc/exim4/conf.d/rewrite/ 2 files
/etc/exim4/conf.d/router/ 13 files
/etc/exim4/conf.d/transport/ 12 files
root@mail1:~# exim4 -bV | tail -1
Configuration file is /var/lib/exim4/config.autogenerated
两种模式下「实际生效」的文件不同:
| 维度 | 单一模式(mail2) | 分片模式(mail1) |
|---|---|---|
| 配置源 | /etc/exim4/exim4.conf.template | /etc/exim4/conf.d/* 按文件名序拼接 |
| 本地宏文件 | /etc/exim4/exim4.conf.localmacros | /etc/exim4/conf.d/main/000_localmacros |
| 适用人群 | 喜欢一个文件改到底 | 需要精确修改某个 ACL/路由器/传输器 |
⚠️ 关键坑:两种模式的宏注入文件互不相认(见 3.4 的排障实录)。
3.4 宏注入文件差异排障实录(自签证书兜底)
mail1 切分片模式后,跨机投递日志里出现了陌生的证书 DN:
2026-10-26 11:07:36 1xLIf4-0003rM-TT => admin@example.org R=smarthost T=remote_smtp_smarthost H=mail1.example.org [10.89.61.11] X=TLS1.3:... CV=no DN="C=UK,O=Exim Developers,CN=mail1.example.org" K C="250 OK id=1xLIf4-0003rQ-Uq"
此前 s_client 验证过的证书明明是 O=Example Lab。检查运行值定位:
root@mail1:~# exim4 -bP tls_advertise_hosts tls_certificate tls_privatekey
tls_advertise_hosts = *
tls_certificate =
tls_privatekey =
root@mail1:~# grep -rn 'MAIN_TLS_ENABLE' /etc/exim4/conf.d/ /etc/exim4/exim4.conf.localmacros
/etc/exim4/exim4.conf.localmacros:MAIN_TLS_ENABLE = yes
根因:MAIN_TLS_ENABLE = yes 只写在了 exim4.conf.localmacros,而该文件只在单一模式被模板读取;分片模式根本不看它。于是 tls_certificate 为空——但 Debian 模板默认广播 STARTTLS(tls_advertise_hosts = *),Exim 4.95 在无证书可用的运行期自动生成内部自签证书兜底,其主体恰为 C=UK,O=Exim Developers。
修复(分片模式改用 conf.d/main/000_localmacros):
root@mail1:~# rm -f /etc/exim4/exim4.conf.localmacros
root@mail1:~# printf 'MAIN_TLS_ENABLE = yes\n' > /etc/exim4/conf.d/main/000_localmacros
root@mail1:~# update-exim4.conf && systemctl reload exim4
root@mail1:~# exim4 -bP tls_advertise_hosts tls_certificate tls_privatekey
tls_advertise_hosts = *
tls_certificate = /etc/exim4/exim.crt
tls_privatekey = /etc/exim4/exim.key
对端验证恢复:
root@mail2:~# echo | openssl s_client -connect mail1.example.org:25 -starttls smtp 2>/dev/null | openssl x509 -noout -subject
subject=CN = mail1.example.org, O = Example Lab, C=CN
四、部署型参数详解(update-exim4.conf.conf)
文件本体(mail1 最终态):
root@mail1:~# cat /etc/exim4/update-exim4.conf.conf
# /etc/exim4/update-exim4.conf.conf
#
# Edit this file and call 'update-exim4.conf' for changes to take effect.
# See update-exim4.conf(8) for details.
dc_eximconfig_configtype='internet'
dc_other_hostnames='mail1.example.org:example.org'
dc_local_interfaces='0.0.0.0 ; ::0'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets='10.89.61.0/24'
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'
(mail2 同结构,dc_other_hostnames='mail2.example.net:example.net'、dc_use_split_config='false'。)
4.1 dc_eximconfig_configtype:部署模式
五种取值与行为见下表:
| 取值 | 行为 | 典型场景 |
|---|---|---|
internet | 直接收发互联网邮件 | 独立邮件服务器 |
smarthost | 收信可本机投递/接收,外发全部经上游 | 内网统一出口 |
satellite | 无本地投递,全部经上游 | 只发不收的边缘机 |
local | 只投本机用户(全新安装默认) | 单机系统邮件 |
unconfigured | 最小骨架 | 尚未规划 |
smarthost 实测(在 mail2 上把外发改走 mail1):移除 hubbed_hosts 的干扰项后切换:
root@mail2:~# sed -i "s/^dc_eximconfig_configtype=.*/dc_eximconfig_configtype='smarthost'/; s/^dc_smarthost=.*/dc_smarthost='mail1.example.org::25'/" /etc/exim4/update-exim4.conf.conf
root@mail2:~# update-exim4.conf && systemctl reload exim4
root@mail2:~# printf 'Pure smarthost route test.\n' | mail -s 'smart-02' admin@example.org
root@mail2:~# grep '1xLIf4-0003rM-TT' /var/log/exim4/mainlog
2026-10-26 11:07:36 1xLIf4-0003rM-TT => admin@example.org R=smarthost T=remote_smtp_smarthost H=mail1.example.org [10.89.61.11] X=TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256 CV=no DN="C=UK,O=Exim Developers,CN=mail1.example.org" K C="250- 480 byte chunk, total 480\n250 OK id=1xLIf4-0003rQ-Uq"
2026-10-26 11:07:36 1xLIf4-0003rM-TT Completed
路由器变为 R=smarthost、传输器变为 T=remote_smtp_smarthost。收完实验改回:
root@mail2:~# sed -i "s/^dc_eximconfig_configtype=.*/dc_eximconfig_configtype='internet'/; s/^dc_smarthost=.*/dc_smarthost=''/" /etc/exim4/update-exim4.conf.conf
root@mail2:~# update-exim4.conf && systemctl reload exim4
root@mail2:~# grep -E 'configtype|smarthost' /etc/exim4/update-exim4.conf.conf
dc_eximconfig_configtype='internet'
dc_smarthost=''
💡 说明:
dc_smarthost语法为主机::端口(双冒号分隔);remote_smtp_smarthost传输器默认启用对上游证书的校验,自签证书会触发「校验失败回退明文」(日志含delivering unencrypted to H=...,见 10.3)。另注意hubbed_hosts路由器(分片序号 150)排在 smarthost 路由器之前,同一目标域两条路由并存时 hubby 优先生效——本实验前先移除了hubbed_hosts条目才能观察到R=smarthost。
4.2 dc_local_interfaces:监听地址
| 取值 | 效果 |
|---|---|
127.0.0.1 ; ::1 | 仅回环(local 模式默认) |
0.0.0.0 ; ::0 | 所有 IPv4/IPv6 接口(internet 站点常用) |
10.89.61.11 ; 127.0.0.1 | 只绑业务网卡与回环 |
修改后用 ss -ltn | grep ':25 ' 与 exim4 -bP local_interfaces 双重验证(本文所有监听切换均按此闭环执行,第七章不再重复)。
4.3 dc_other_hostnames:本机承接邮件域
分号分隔的域名列表,最终并入 +local_domains 域名表。mail1 当前值 mail1.example.org:example.org,验证:
root@mail1:~# exim4 -bP '+local_domains'
+local_domains = @ : localhost : example.org : mail1.example.org
非本机域名的对照(mail1 上查询 mail2 的域):
root@mail1:~# exim4 -bt admin@example.net
R: hubbed_hosts for admin@example.net
admin@example.net
router = hubbed_hosts, transport = remote_smtp
host mail2.example.net [10.89.61.12]
4.4 dc_relay_nets:允许中继的客户端网段
该变量填充 +relay_from_hosts 主机表,决定哪些客户端可以借本机向第三方域转发邮件:
root@mail1:~# exim4 -bP +relay_from_hosts
hostlist relay_from_hosts = 10.89.61.0/24 : 127.0.0.1 : ::::1
语义与 ACL 实验见第六章 6.3(允许/拒绝/认证放行三态实测)。
4.5 dc_localdelivery:本地投递格式
| 取值 | 投递目标 | 路由器/传输器 |
|---|---|---|
mail_spool | /var/mail/<user>(mbox) | local_user / mail_spool |
maildir_home | $home/Maildir/(Maildir) | local_user / maildir_home |
两种格式的完整投递与目录证据见《Ubuntu 22.04 安装 Exim 4.95 指南》7.4。
4.6 其余 dc_* 变量
| 变量 | 作用 | 实测状态 |
|---|---|---|
dc_relay_domains | 允许代收并转发的目标域(非本地域) | 空;如设 example.com 则本机为 example.com 的二级 MX |
dc_minimaldns | 最小化 DNS 查询(跳过主机名反查等) | false |
dc_hide_mailname / dc_readhost | 仅 smarthost 模式有意义:改写外发信封域名 | 空 |
dc_postmaster | postmaster 别名指向 | root |
CFILEMODE | 生成配置的权限 | 644 |
dc_mailname_in_oh | /etc/mailname 归属提示 | true |
五、MAIN 宏与主配置选项
5.1 宏机制
Debian 模板中所有可定制点都以 .ifdef MAIN_XXX 预留,本地宏文件只需赋值即可打开对应分支,无需改动模板本体。模板的宏注入位置在 conf.d/main/01_exim4-config_listmacrosdefs(即单一模式的模板头部):
# Macro defining the main configuration directory.
# We do not use absolute paths.
.ifndef CONFDIR
CONFDIR = /etc/exim4
.endif
# debconf-driven macro definitions get inserted after this line
5.2 MAIN_TLS_ENABLE:启用服务器证书
分片模式写入 /etc/exim4/conf.d/main/000_localmacros,单一模式写入 /etc/exim4/exim4.conf.localmacros,内容与验证:
root@mail1:~# cat /etc/exim4/conf.d/main/000_localmacros
MAIN_TLS_ENABLE = yes
root@mail1:~# exim4 -bP tls_advertise_hosts tls_certificate tls_privatekey
tls_advertise_hosts = *
tls_certificate = /etc/exim4/exim.crt
tls_privatekey = /etc/exim4/exim.key
其作用是打开 conf.d/main/03_exim4-config_tlsoptions 中的条件块(文件原文节选):
.ifdef MAIN_TLS_ENABLE
# Defines what hosts to 'advertise' STARTTLS functionality to. The
# default, *, will advertise to all hosts that connect with EHLO.
.ifndef MAIN_TLS_ADVERTISE_HOSTS
MAIN_TLS_ADVERTISE_HOSTS = *
.endif
tls_advertise_hosts = MAIN_TLS_ADVERTISE_HOSTS
未定义该宏时 tls_certificate 为空,Exim 4.95 会以运行期自签证书兜底(排障实录见 3.4)。
5.3 MAIN_HOST_LOOKUP:连接方反查
模板默认全量反查(conf.d/main/02_exim4-config_options 原文):
.ifndef DC_minimaldns
.ifndef MAIN_HOST_LOOKUP
MAIN_HOST_LOOKUP = *
.endif
host_lookup = MAIN_HOST_LOOKUP
.endif
实验网段没有 PTR 记录,反查只拖慢会话并产生内部命名回显,置空关闭:
root@mail1:~# printf 'MAIN_TLS_ENABLE = yes\nMAIN_HOST_LOOKUP =\n' > /etc/exim4/conf.d/main/000_localmacros
root@mail1:~# update-exim4.conf && systemctl restart exim4
root@mail1:~# exim4 -bP host_lookup
host_lookup =
关闭后 SMTP 会话回显从 Hello <反查名> [10.89.61.12] 变为 Hello lab [10.89.61.12](lab 为客户端 EHLO 宣告名)。
5.4 MESSAGE_SIZE_LIMIT:单信大小上限
默认 50MB(EHLO 能力 250-SIZE 52428800)。压到 10MB 做闭环验证:
root@mail2:~# printf 'MAIN_TLS_ENABLE = yes\nMESSAGE_SIZE_LIMIT = 10M\n' > /etc/exim4/exim4.conf.localmacros
root@mail2:~# update-exim4.conf && systemctl reload exim4
root@mail2:~# exim4 -bP message_size_limit
message_size_limit = 10M
root@mail2:~# printf 'EHLO lab\r\nQUIT\r\n' | timeout 5 bash -c 'exec 3<>/dev/tcp/127.0.0.1/25; cat <&3 & cat >&3; wait' | tr -d '\r' | grep SIZE
250-SIZE 10485760
EHLO 能力值同步变为 10485760,验证后恢复默认(从宏文件删除该行并重新生成)。
5.5 主配置选项速查(exim4 -bP)
root@mail1:~# exim4 -bP primary_hostname qualify_domain message_size_limit daemon_smtp_ports
primary_hostname = mail1.example.org
qualify_domain = example.org
message_size_limit = 50M
daemon_smtp_ports = 25
root@mail1:~# exim4 -bP '+local_domains' '+relay_to_domains' '+relay_from_hosts'
+local_domains = @ : localhost : example.org : mail1.example.org
+relay_to_domains =
+relay_from_hosts = 10.89.61.0/24 : 127.0.0.1 : ::::1
六、ACL 详解
6.1 ACL 入口
conf.d/main/02_exim4-config_options 把三个 ACL 挂到 SMTP 阶段:
acl_smtp_mail = MAIN_ACL_CHECK_MAIL
acl_smtp_rcpt = MAIN_ACL_CHECK_RCPT
acl_smtp_data = MAIN_ACL_CHECK_DATA
宏默认值指向 acl_check_mail / acl_check_rcpt / acl_check_data,实现分别在 conf.d/acl/ 下五个文件(00_exim4-config_header、20_exim4-config_local_deny_exceptions、30_exim4-config_check_mail、30_exim4-config_check_rcpt、40_exim4-config_check_data)。
6.2 acl_check_rcpt 逐段解读
conf.d/acl/30_exim4-config_check_rcpt 是中继控制的核心,关键段落原文(节选):
accept
hosts = +relay_from_hosts
control = submission/sender_retain
control = dkim_disable_verify
# Accept if the message arrived over an authenticated connection, from
# any host. ...
accept
authenticated = *
control = submission/sender_retain
control = dkim_disable_verify
# Insist that any other recipient address that we accept is either in one of
# our local domains, or is in a domain for which we explicitly allow
# relaying. Any other domain is rejected as being unacceptable for relaying.
require
message = relay not permitted
domains = +local_domains : +relay_to_domains
要点:来自 +relay_from_hosts 的连接直接放行(并打上 submission 修正标记);已认证连接放行;其余收件域必须属于本机域或显式中继域,否则 require 失败返回 relay not permitted。
6.3 中继控制三态实验(mail1)
状态一:relay_nets 在位,明文中继放行
root@mail2:~# { printf 'EHLO lab\r\nMAIL FROM:<root@example.net>\r\nRCPT TO:<someone@example.com>\r\nRSET\r\nQUIT\r\n'; sleep 2; } | timeout 10 bash -c 'exec 3<>/dev/tcp/mail1.example.org/25; cat <&3 & cat >&3; wait' | tr -d '\r'
220 mail1.example.org ESMTP Exim 4.95 Ubuntu Mon, 26 Oct 2026 11:22:55 +0000
250-mail1.example.org Hello lab [10.89.61.12]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-PIPE_CONNECT
250-AUTH PLAIN
250-CHUNKING
250-STARTTLS
250-PRDR
250 HELP
250 OK
250 Accepted
250 Reset OK
221 mail1.example.org closing connection
RCPT 返回 250 Accepted(mail1 的 dc_relay_nets='10.89.61.0/24' 覆盖了 mail2 的地址)。
状态二:清空 relay_nets,同会话被拒
root@mail1:~# sed -i "s/^dc_relay_nets=.*/dc_relay_nets=''/ /etc/exim4/update-exim4.conf.conf
root@mail1:~# update-exim4.conf && systemctl reload exim4
root@mail2:~# { printf 'EHLO lab\r\nMAIL FROM:<root@example.net>\r\nRCPT TO:<someone@example.com>\r\nRSET\r\nQUIT\r\n'; sleep 2; } | timeout 10 bash -c 'exec 3<>/dev/tcp/mail1.example.org/25; cat <&3 & cat >&3; wait' | tr -d '\r' | grep -E '^(250 [A-Z]|550|221)'
250 OK
550 relay not permitted
221 mail1.example.org closing connection
mail1 的拒绝日志:
2026-10-26 11:31:57 H=(lab) [10.89.61.12] F=<root@example.net> rejected RCPT <someone@example.com>: relay not permitted
状态三:认证放行(accept authenticated = *)
口令库与认证器(分片模式新增 /etc/exim4/conf.d/auth/170_lab-plain):
root@mail1:~# openssl passwd -6 'q1w2e3r4'
$6$roFh37CXl.VZTQyP$T5nYBi1eQE7l8umJbWKzOqR3WrLX/oW8gb2jNJlunhyvABCBWEVZNTtlrq6UkxWaEt9ugBFtGpwCIdVJZEXi/
root@mail1:~# printf 'admin:$6$roFh37CXl.VZTQyP$T5nYBi1eQE7l8umJbWKzOqR3WrLX/oW8gb2jNJlunhyvABCBWEVZNTtlrq6UkxWaEt9ugBFtGpwCIdVJZEXi/\n' > /etc/exim4/smtp.users
root@mail1:~# cat /etc/exim4/conf.d/auth/170_lab-plain
plain_server:
driver = plaintext
public_name = PLAIN
server_condition = ${if crypteq{$auth3}{${lookup{$auth2}lsearch{CONFDIR/smtp.users}{$value}fail}}{yes}{no}}
server_set_id = $auth2
root@mail1:~# update-exim4.conf && systemctl restart exim4
口令正确:明文会话直接认证成功并放行中继:
root@mail2:~# B64=$(printf '\0admin\0q1w2e3r4' | base64)
root@mail2:~# { printf "EHLO lab\r\nAUTH PLAIN %s\r\nMAIL FROM:<root@example.net>\r\nRCPT TO:<someone@example.com>\r\nRSET\r\nQUIT\r\n" "$B64"; sleep 2; } | timeout 10 bash -c 'exec 3<>/dev/tcp/mail1.example.org/25; cat <&3 & cat >&3; wait' | tr -d '\r' | grep -E '^(235|250 [A-Z]|550)'
235 Authentication succeeded
250 OK
250 Accepted
口令错误:535 且进入 rejectlog:
root@mail2:~# B64B=$(printf '\0admin\0wrongpass' | base64)
root@mail2:~# { printf "EHLO lab\r\nAUTH PLAIN %s\r\nQUIT\r\n" "$B64B"; sleep 2; } | timeout 10 bash -c 'exec 3<>/dev/tcp/mail1.example.org/25; cat <&3 & cat >&3; wait' | tr -d '\r' | grep -E '^(235|535|221)'
535 Incorrect authentication data
221 mail1.example.org closing connection
2026-10-26 11:32:01 plain_server authenticator failed for (lab) [10.89.61.12]: 535 Incorrect authentication data (set_id=admin)
恢复状态:dc_relay_nets='10.89.61.0/24' 写回并 reload,exim4 -bP +relay_from_hosts 复核三表项(见 4.4 回显)。
6.4 acl_check_data 与 check_mail
40_exim4-config_check_data 负责报文头检查(如头部语法、大小限制),30_exim4-config_check_mail 校验 MAIL FROM 阶段。本实验环境未引入内容扫描器,两段保持包默认配置即可;如需附件拦截/垃圾评分,应配合 heavy 守护进程与 SpamAssassin/ClamAV 在这两段扩展,本文不展开。
七、路由器与传输器详解
7.1 路由器清单(conf.d/router/,13 个文件)
| 文件 | 路由器 | 驱动 | 作用 |
|---|---|---|---|
100_exim4-config_domain_literal | domain_literal | ipliteral | 收件域为字面 IP 的处理 |
150_exim4-config_hubbed_hosts | hubbed_hosts | manualroute | 按 hubbed_hosts 表手工路由(本实验双机互发的关键) |
200_exim4-config_primary | dnslookup | dnslookup | 非本地域走 DNS MX 投递 |
300_exim4-config_real_local | real_local | redirect | real- 前缀本地直投 |
400_exim4-config_system_aliases | system_aliases | redirect | /etc/aliases 别名展开 |
500_exim4-config_hubuser | hubuser | redirect | hubuser 表展开 |
600_exim4-config_userforward | userforward | redirect | $home/.forward 用户转发/过滤 |
700_exim4-config_procmail | procmail | accept | 存在 ~/.procmailrc 时交 procmail |
800_exim4-config_maildrop | maildrop | accept | 存在 ~/.mailfilter 时交 maildrop |
850_exim4-config_lowuid | lowuid_aliases | redirect | 低 UID 用户的别名 |
900_exim4-config_local_user | local_user | accept | 常规本地用户投递入口 |
mmm_mail4root | mail4root | redirect | root 邮件落到 /var/mail/mail |
文件名前缀即拼接顺序,路由器按此顺序自上而下尝试。
7.2 关键路由器原文解读
hubbed_hosts(手工路由,双机互发的桥梁):
hubbed_hosts:
debug_print = "R: hubbed_hosts for $domain"
driver = manualroute
domains = "${if exists{CONFDIR/hubbed_hosts}\
{partial-lsearch;CONFDIR/hubbed_hosts}\
fail}"
same_domain_copy_routing = yes
route_data = ${lookup{$domain}partial-lsearch{CONFDIR/hubbed_hosts}}
transport = remote_smtp
dnslookup(默认外发路由):
dnslookup:
debug_print = "R: dnslookup for $local_part@$domain"
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
same_domain_copy_routing = yes
ignore_target_hosts = ROUTER_DNSLOOKUP_IGNORE_TARGET_HOSTS
no_more
domains = ! +local_domains 表明它只处理「非本机域」;no_more 意味着查不到路由目标后不再下探。
system_aliases:
system_aliases:
debug_print = "R: system_aliases for $local_part@$domain"
driver = redirect
domains = +local_domains
allow_fail
allow_defer
data = ${lookup{$local_part}lsearch{/etc/aliases}}
userforward:
userforward:
debug_print = "R: userforward for $local_part@$domain"
driver = redirect
domains = +local_domains
check_local_user
file = $home/.forward
require_files = $local_part_data:$home/.forward
no_verify
no_expn
check_ancestor
allow_filter
directory_transport = address_directory
file_transport = address_file
pipe_transport = address_pipe
reply_transport = address_reply
skip_syntax_errors
local_user 与 mail4root:
local_user:
debug_print = "R: local_user for $local_part@$domain"
driver = accept
domains = +local_domains
check_local_user
local_parts = ! root
transport = LOCAL_DELIVERY
cannot_route_message = Unknown user
mail4root:
debug_print = "R: mail4root for $local_part@$domain"
driver = redirect
domains = +local_domains
data = /var/mail/mail
file_transport = address_file
local_parts = root
user = mail
group = mail
local_user 用 LOCAL_DELIVERY 宏(由 dc_localdelivery 决定指向哪个传输器);root 被 local_parts = ! root 排除后由 mail4root 兜底——这就是 root 邮件落在 /var/mail/mail 的配置层原因。
7.3 传输器清单(conf.d/transport/,12 个文件)
| 文件 | 传输器 | 作用 |
|---|---|---|
30_exim4-config_address_file | address_file | 投到任意文件(含 mbox) |
30_exim4-config_address_pipe | address_pipe | 管道命令投递 |
30_exim4-config_address_reply | address_reply | 自动回信 |
30_exim4-config_maildir_home | maildir_home | Maildir 到家目录 |
30_exim4-config_mail_spool | mail_spool | mbox 到 /var/mail |
30_exim4-config_maildrop_pipe | maildrop_pipe | maildrop 管道 |
30_exim4-config_procmail_pipe | procmail_pipe | procmail 管道 |
30_exim4-config_remote_smtp | remote_smtp | 常规 SMTP 外发 |
30_exim4-config_remote_smtp_smarthost | remote_smtp_smarthost | smarthost 外发(含上游认证/校验) |
35_exim4-config_address_directory | address_directory | 投到目录 |
7.4 关键传输器原文解读
maildir_home:
debug_print = "T: maildir_home for $local_part@$domain"
driver = appendfile
directory = $home/Maildir
create_directory
delivery_date_add
envelope_to_add
return_path_add
maildir_format
directory_mode = 0700
mode = 0600
mode_fail_narrower = false
remote_smtp:
debug_print = "T: remote_smtp for $local_part@$domain"
driver = smtp
address_file:
debug_print = "T: address_file for $local_part@$domain"
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
(maildir_home 的 .ifdef 分支为可覆盖的宏参数,此处为默认展开值。)日志中的 T= 字段即实际命中的传输器,R= 是路由器,二者是判读投递路径的第一线索。
7.5 系统别名(/etc/aliases)
追加 billing 别名并验证路由链:
root@mail1:~# printf 'billing: admin\n' >> /etc/aliases
root@mail1:~# tail -2 /etc/aliases
ops-list: admin, root
billing: admin
root@mail1:~# exim4 -bt billing@example.org
R: system_aliases for billing@example.org
R: system_aliases for admin@example.org
R: userforward for admin@example.org
R: system_aliases for admin@example.org
R: procmail for admin@example.org
R: maildrop for admin@example.org
R: lowuid_aliases for admin@example.org (UID 1000)
R: local_user for admin@example.org
R: hubbed_hosts for example.net
admin@example.org
<-- admin@example.org
<-- billing@example.org
router = local_user, transport = maildir_home
admin@example.net
<-- admin@example.org
<-- billing@example.org
router = hubbed_hosts, transport = remote_smtp
host mail2.example.net [10.89.61.12]
别名展开(billing → admin)→ admin 的 .forward(见 7.6)→ 两个最终投递地址。别名修改即时生效,无需重启服务。
7.6 用户转发(.forward)
mail1 的 admin 配置「保留本地副本 + 转发到 mail2」:
root@mail1:~# printf '\\admin\nadmin@example.net\n' > /home/admin/.forward
root@mail1:~# chown admin:admin /home/admin/.forward
root@mail1:~# cat -A /home/admin/.forward
\admin$
admin@example.net$
root@mail1:~# printf 'Forward test: local copy + remote copy to mail2.\n' | mail -s 'fwd-02' admin@example.org
root@mail1:~# tail -4 /var/log/exim4/mainlog
2026-10-26 11:32:41 1xLIwT-0005Tm-L3 <= root@example.org U=root P=local S=485
2026-10-26 11:32:41 1xLIwT-0005Tm-L3 => admin <admin@example.org> R=local_user T=maildir_home
2026-10-26 11:32:41 1xLIwT-0005Tm-L3 => admin@example.net <admin@example.org> R=hubbed_hosts T=remote_smtp H=mail2.example.net [10.89.61.12] X=TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256 CV=no DN="CN=mail2.example.net,O=Example Lab,C=CN" K C="250- 500 byte chunk, total 500\n250 OK id=1xLIwT-0005Uq-Lk"
2026-10-26 11:32:41 1xLIwT-0005Tm-L3 Completed
\admin 的反斜杠表示「继续本地投递、避免二次别名展开」,因此同一封信一式两份:本地 Maildir 一份、mail2 一份。
八、SMTP AUTH 认证
8.1 认证器与口令库
见 6.3 的 plain_server 定义与 smtp.users 生成。crypteq 支持系统 crypt 哈希(openssl passwd -6 产生的 SHA-512),口令不以明文落盘。
8.2 会话验证
正确口令 235 Authentication succeeded、错误口令 535 Incorrect authentication data(回显见 6.3 状态三)。认证成功后 server_set_id = $auth2 把身份写入日志。
8.3 认证中继实录
以认证身份经 mail1 中继一封信到 mail2(两段式发送,尊重 354 同步点):
root@mail2:~# B64=$(printf '\0admin\0q1w2e3r4' | base64)
root@mail2:~# { printf "EHLO lab\r\nAUTH PLAIN %s\r\nMAIL FROM:<root@example.net>\r\nRCPT TO:<admin@example.net>\r\nDATA\r\n" "$B64"; sleep 2; printf "Subject: auth-relay-03\r\nFrom: root@example.net\r\n\r\nTwo-stage authenticated relay body.\r\n.\r\nQUIT\r\n"; sleep 2; } | timeout 14 bash -c 'exec 3<>/dev/tcp/mail1.example.org/25; cat <&3 & cat >&3; wait' | tr -d '\r'
235 Authentication succeeded
250 OK
250 Accepted
354 Enter message, ending with "." on a line by itself
250 OK id=1xLIw1-0005TI-WD
221 mail1.example.org closing connection
mail1 日志(P=esmtpa A=plain_server:admin 即认证标记):
2026-10-26 11:32:15 1xLIw1-0005TI-WD <= root@example.net H=(lab) [10.89.61.12] P=esmtpa A=plain_server:admin S=371
2026-10-26 11:32:16 1xLIw1-0005TI-WD => admin@example.net R=hubbed_hosts T=remote_smtp H=mail2.example.net [10.89.61.12] X=TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256 CV=no DN="CN=mail2.example.net,O=Example Lab,C=CN" K C="250- 383 byte chunk, total 383\n250 OK id=1xLIw4-0005Ub-0d"
2026-10-26 11:32:16 1xLIw1-0005TI-WD Completed
mail2 的 admin 收到主题为 auth-relay-03 的邮件(Maildir new 目录文件核对通过)。
8.4 安全注记
本实验认证器未设置 server_advertise_condition,明文会话也广播 250-AUTH PLAIN;生产环境应加 server_advertise_condition = ${if def:tls_in_cipher}(或等价配置)强制「先 STARTTLS 再认证」,避免口令明文过网。
九、生产配置模板(实验室单机 MTA 参考)
以下为本文实验收敛后的最终配置,可直接套用(替换主机名/域名/网段三处即可):
# /etc/exim4/update-exim4.conf.conf
dc_eximconfig_configtype='internet'
dc_other_hostnames='mail1.example.org:example.org'
dc_local_interfaces='0.0.0.0 ; ::0'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets='10.89.61.0/24'
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'
# /etc/exim4/conf.d/main/000_localmacros(分片模式)
MAIN_TLS_ENABLE = yes
MAIN_HOST_LOOKUP =
# /etc/exim4/exim4.conf.localmacros(单一模式,二选一)
MAIN_TLS_ENABLE = yes
MAIN_HOST_LOOKUP =
配套文件:/etc/exim4/hubbed_hosts(跨域手工路由)、/etc/aliases(系统别名)、/etc/exim4/smtp.users + conf.d/auth/170_lab-plain(如需认证中继)、/etc/exim4/exim.crt|exim.key(TLS)。
十、常见问题
10.1 update-exim4.conf 报错退出
向本地宏文件写入非法内容后重新生成,会在生成阶段即失败(不会污染运行配置):
root@mail2:~# printf 'MAIN_TLS_ENABLE = yes\nMESSAGE_SIZE_LIMIT = 10M\nBAD_DIRECTIVE_HERE\n' >> /etc/exim4/exim4.conf.localmacros
root@mail2:~# update-exim4.conf
2026-10-26 11:15:32 Exim configuration error in line 3 of /etc/exim4/exim4.conf.localmacros:
main option "bad_directive_here" unknown
处理:删除非法行后重新 update-exim4.conf(本实验修正后生成成功)。
10.2 改了配置不生效
检查三件事:是否跑了 update-exim4.conf、是否 reload/restart、exim4 -bV 最后一行是否指向预期文件(见第二章)。宏文件形态与配置模式不匹配(3.4)是最常见的隐形原因。
10.3 smarthost 外发报 TLS certificate verification failed
remote_smtp_smarthost 默认校验上游证书,自签证书会被拒绝并回退明文投递:
2026-10-26 11:07:36 1xLIf4-0003rM-TT TLS session: (certificate verification failed): certificate invalid: delivering unencrypted to H=mail1.example.org [10.89.61.11] (not in hosts_require_tls)
处理:为上游签发受信 CA 证书,或为该上游主机配置指纹/CA 信任(tls_verify_certificates),不建议长期明文。
10.4 hubbed_hosts 与 smarthost 谁生效
hubbed_hosts 路由器(序号 150)先于 smarthost 路由器拼接,两处同时命中时 R=hubbed_hosts 生效。要观察 smarthost 行为需先移除对应 hubbed 条目(4.1 实验即按此进行)。
10.5 SMTP protocol synchronization error
脚本一次灌入 DATA 与正文触发流水线同步保护,报文样例与处理见《Ubuntu 22.04 安装 Exim 4.95 指南》10.3。
十一、参考链接
- Exim 规格说明书(第 7 章主配置、第 39/40 章 ACL 与认证):https://www.exim.org/exim-html-current/doc/html/spec_html/index.html
- Debian Exim README(配置框架与 update-exim4.conf 权威说明):
/usr/share/doc/exim4-base/README.Debian.gz - Exim 官网:https://www.exim.org/
- 本站同批实践文档:《Ubuntu 22.04 安装 Exim 4.95 指南》(同环境安装与双机收发全流程)