Exim
Exim 是一个多功能的邮件传输代理。本文建立在邮件服务器的基础上。虽然Exim wiki 提供了一些关于特定用例的有用指南,但有关所有配置选项的详细描述也可用。
安装
基本配置
Exim 自带一个庞大的默认配置文件,位于 /etc/mail/exim.conf。其中许多选项在常规使用场景中并不需要。
配置可以包含在多个章节的单个文件中。Exim 的配置分为几个不同的部分。通用选项设置必须始终出现在其他任何部分之前。其他部分都是可选的,可以按任意顺序出现。除通用选项部分外,每个部分都以单词“begin”开头,后跟至少一个空格,然后是部分名称。关于 exim 运行时配置的其他通用注释可通过此链接指向的官方文档获得。
以下是一个非常基本的配置,它提供了:本地邮件传递到系统用户、Mbox(也称为传统 BSD 邮箱)格式,以及授权中继到 MX 主机。描述基于在主机“hostname.mydomain.tld”上服务的域名“mydomain.tld”。强烈建议在使用下面的给定文档之前查阅官方文档。
主要参数
必需的通用选项部分包含一些基本选项。仅使用这些选项将打开连接端口,但仍然不会接受或中继任何邮件。
# be the responsible mail host for hostname.mydomain.tld (@) & mydomain.tld domainlist local_domains = @ : mydomain.tld # we don't relay. Not for foreign domains nor for a single host domainlist relay_to_domains = hostlist relay_from_hosts = # serve the on all used ports daemon_smtp_ports = 25 : 465 : 587 # do a reverse name lookup for all incoming connections host_lookup = * # Logging: log all events, add syslog to logging path & avoid double entries log_selector = +all log_file_path = : syslog syslog_duplication = false # undeliverables: discard bounce messages after 2d and all others after 7 d ignore_bounce_errors_after = 2d timeout_frozen_after = 7d
TLS、安全与认证
获取证书,请参见 OpenSSL#Usage。
以下选项的第一部分仍然是 Exim 中第一个配置节的一部分。从“begin authenticators”开始,这里建议的配置的第一个特殊节就开始了。稍后还会有其他节。下面定义了一些非常基本的安全相关选项,设置了 TLS 并引入了一个使用用户密码查找的纯文本身份验证器。
# actually not required: it's hard coded - anyway: no mail delivery to root
never_users = root
# don't show the exim version to everyone. Actually not even show the name.
smtp_banner = $smtp_active_hostname ESMTP $tod_full
# STARTTLS is offered to everyone
tls_advertise_hosts = *
tls_certificate = /path/to/exim/only/fullchain.pem
tls_privatekey = /path/to/exim/only/privkey.pem
# use all ciphers on port 25, use only good ciphers on port 587
tls_require_ciphers = ${if =={$received_port}{25} \
{DEFAULT} {HIGH:!MD5:!SHA1:!SHA2}}
# traffic on port 465 always uses TLS
tls_on_connect_ports = 465
# special sections always start with a "begin"
begin authenticators
PLAIN:
# authentication protocol is plain text
driver = plaintext
# authentication is offered as plain text
public_name = PLAIN
server_prompts = :
# authentication is successful, if the password provided ($auth3)
# equals the password found in a lookup file for user ($auth2)
server_condition = ${if eq{$auth3}{${lookup{$auth2}dbm{/etc/authpwd}}}}
server_set_id = $auth2
# only offer plain text authentication after TLS is been negotiated
server_advertise_condition = ${if def:tls_in_cipher}
exim 加载证书文件。虽然用这样的用户运行面向互联网的进程很好,但允许这种用户访问私钥有些奇怪。如果您的服务器有多种用途(例如 HTTP、IMAP、SMTP),并带有多个 DNS 别名(CNAME 或多个名称指向同一 IP),则可能需要请求多个证书。如果 Exim 私钥丢失,损坏仅限于 SMTP。路由、传输与重试
对于消息的每个收件人,路由执行如下:路由器按路由节中给出的顺序进行测试。对于每个路由器,可以应用条件(例如 domains = ! +local_domains)。只有当所有条件都适用时,消息才会被传递给定义的传输(例如 transport = smtp)。如果传输失败或路由器的所有条件不都满足,则测试下一个路由器。
begin routers # that's the relay router dnslookup: # the router type driver = dnslookup # the domains served on this router: not the local domain domains = ! +local_domains # the transport to be used (see transport section below) transport = remote_delivery # localhost is to be ignored if dns gives such result ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 # a router list is processed until matched and successful transported. # if transport fails, we don't want the next router to be used no_more # local delivery localuser: # the transport type - we accept the mail locally driver = accept # this router serves only our domains domains = +local_domains # use transport named local_delivery transport = local_delivery # in case local delivery fails cannot_route_message = Unknown local user begin transports remote_delivery: driver = smtp local_delivery: #deliver to a local mailbox driver = appendfile file = /var/mail/$local_part # if routing or transport fails, try again after this (default) ruleset begin retry # Address or Domain Error Retries # ----------------- ----- ------- * * F,2h,15m; G,16h,1h,1.5; F,4d,6h
使用 manualroute
如果您想改用 manualroute,请注释掉 dnslookup 块并添加 smarthost 块。
#dnslookup:
# the router type
# driver = dnslookup
# the domains served on this router: not the local domain
# domains = ! +local_domains
# the transport to be used (see transport section below)
# transport = remote_delivery
# localhost is to be ignored if dns gives such result
# ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
# a router list is processed until matched and successful transported.
# if transport fails, we don't want the next router to be used
# no_more
smarthost:
driver = manualroute
domains = !+local_domains
transport = remote_delivery
route_list = * smtp.myisp.com # change to the desired smtp server
ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
ACL:访问控制列表
访问控制列表是 Exim 的核心。它们对于基本检查是必需的,并可用于复杂的邮件处理。总的来说,Exim 中的整体邮件处理是
connection > (authentication >) ACL > routing > transport
因此,重要的是要注意,来自已认证客户端的消息(默认情况下)与来自其他邮件服务器的消息一样,都由相同的 ACL 处理。Exim 知道一套完整的不同的 ACL。要选择正确的 ACL 集,需要对 SMTP 协议有良好的了解。
acl_smtp_connect > acl_smtp_helo > ... > acl_smtp_rcpt > ... > acl_smtp_data > ...
对于基本设置,两个 ACL 是强制性的:acl_smtp_rcpt 和 acl_smtp_data。它们默认设置为拒绝,而所有其他默认设置为接受。下面的示例仅阻止成为开放中继。此设置存在多个安全漏洞(例如,所有已认证用户都可以使用任何电子邮件地址)。如果添加到现有配置中,它必须添加到任何其他特殊节之前(即,在任何现有“begin”之前)。
# use this ACL after SMTP RCPT TO: is received acl_smtp_rcpt = basic_acl_rcpt # use this ACL after SMTP DATA is finished, i.e. all data has been received acl_smtp_data = basic_acl_data begin acl basic_acl_rcpt: # accept all messages for which I am the receiving mail host accept domains = +local_domains # accept all messages from authenticated clients accept authenticated = * # deny all other messages (i.e. messages to be relayed from unauthorized # clients) deny message = "we are not an open relay" basic_acl_data: # we have done all checks after RCPT already accept
隐藏机器名
如果您使用的是笔记本电脑,或者在智能主机配置中的机器上,并且不希望机器的名称出现在出站邮件中,那么您必须启用 Exim 的重写功能。
在重写节中,您应该有类似以下的配置:
*@machine.mydomain $1@mydomain
其中 machine 是您的笔记本电脑或 PC 的主机名,mydomain 是机器的域名和出站邮件的域名。要仅重写发件人域名,请在行末添加特殊标志 (F)。有关详细信息,请参阅上游文档。
启动
启动/启用 exim.service。
Dovecot LMTP 投递和 SASL 认证
本节描述了Dovecot 的集成。假设 Dovecot 和 Exim 已安装并配置完毕。Dovecot 将作为SASL 身份验证器和本地传输机制。为此,Dovecot 服务将按以下方式设置。
/etc/dovecot/conf.d/10-master.conf
service auth {
unix_listener exim-auth {
mode = 0600
user = exim
group = exim
}
# Auth process is run as this user.
user = $default_internal_user
}
service lmtp {
# a unix socket is preferred of a local port communication
unix_listener exim-lmtp {
mode = 0600
group = exim
user = exim
}
}
要在受 TLS 保护的环境中使用 Dovecot SASL,请将以下身份验证器添加到 Exim。
/etc/mail/exim.conf - authenticators section
PLAIN:
driver = dovecot
public_name = PLAIN
server_socket = /var/run/dovecot/exim-auth
server_set_id = $auth1
server_advertise_condition = ${if def:tls_in_cipher}
本地投递的现有路由器可以重用。您可能需要考虑在路由器定义中添加 dsn_lasthop。如果使用DSN,Exim 将假定消息在此处最终投递。在传输节中,本地投递的传输必须替换为以下传输定义。
/etc/mail/exim.conf - transports section
local_delivery: driver = lmtp socket = /var/run/dovecot/exim-lmtp batch_max = 200
verify = recipient/callout=no_cache 将无法按预期工作,即不存在的用户帐户不会触发失败。要对 Dovecot 进行收件人检查,您必须用以下方式替换上面的驱动程序:driver = smtp protocol = lmtp port = 2525
主机在路由器中指定,而不是在传输中。因此,路由器必须如下所示:
lmtp_router: driver = manualroute domains = +local_domains transport = local_delivery route_list = * 127.0.0.1 byname此外,您的 Dovecot lmtp 服务必须相应调整[已失效链接 2025-08-15—HTTP 404]。例如:这是一个修复此确切问题的 Git commit。
由于 Dovecot 配置为向 exim 用户提供 Unix 套接字,您可以通过在主配置节中添加以下行来增强安全性。
/etc/mail/exim.conf - main section
# don't deliver being root, drop privileges to exim user deliver_drop_privilege = true
使用 Gmail 作为智能主机
在 exim 配置文件开头,您必须使用以下命令启用 TLS:
tls_advertise_hosts = +local_network : *.gmail.com
或者向所有主机宣传 tls
tls_advertise_hosts = *
有关 TLS 的更多信息,请参阅Exim 文档。
在 dnslookup 路由器之前或替代它添加一个路由器
gmail_route: driver = manualroute transport = gmail_relay route_list = * smtp.gmail.com
添加一个传输
gmail_relay: driver = smtp port = 587 tls_verify_certificates = /etc/ssl/certs/ca-certificates.crt # this forces host verification. tls_verify_hosts = smtp.gmail.com hosts_require_auth = <; $host_address hosts_require_tls = <; $host_address
由于主机验证,您的 exim 日志中可能会出现
SSL verify error: certificate name mismatch: "/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com"
但这不会影响邮件投递,可以忽略。添加一个身份验证器(将 myaccount@gmail.com 和 mypassword 替换为您自己的帐户详细信息)
gmail_login: driver = plaintext public_name = LOGIN hide client_send = : myaccount@gmail.com : mypassword
$host_address 用于 hosts_require_auth 和 hosts_require_tls,而不是 smtp.gmail.com,以避免偶尔出现 530 5.5.1 Authentication Required 错误。这些错误是由 smtp.gmail.com DNS 查询中的 IP 地址更改引起的。$host_address 将展开为 gmail_route 路由器解析到的特定 IP 地址。
为了提高安全性,请使用每个应用程序的密码。这同样适用于 Google Workspace 帐户。
加固
速率限制
安全漏洞时有发生。如果您没有任何提交本地邮件的服务(从 localhost 接收邮件不被视为本地提交),请完全禁用本地提交。通过在主节中添加 acl_not_smtp = acl_local 并将以下简单的 ACL 添加到 acl 节来实现。
/etc/mail/exim.conf - acl section
acl_local: deny log_message = AL01: Local submission denied.
如果需要本地提交,请考虑对其施加速率限制。通过在主节中添加 acl_not_smtp = acl_local 并将以下 ACL 添加到 acl 节来实现。它施加了 2 个速率限制:每分钟 20 封邮件,每 10 分钟 30 封邮件。这样,本地提交的警报爆发是可能的,而
/etc/mail/exim.conf - acl section
acl_local: # apply a limit of 30 mails to the administrator for alerts submitted # by local services deny ratelimit = 30 / 1m / strict recipients = admin@mydomain.tld : root@hostname.mydomain.tld log_message = AL01: Sender rate limit for local submission \ exceeded: $sender_rate / $sender_rate_period. # apply a burst limit of 3 mails per minute to everyone else deny ratelimit = 3 / 1m / strict !recipients = admin@mydomain.tld : root@hostname.mydomain.tld log_message = AL02: Sender rate limit for local submission \ exceeded: $sender_rate / $sender_rate_period. # apply a regular limit of 10 mails per 30 minutes to everyone else deny ratelimit = 10 / 30m / strict !recipients = admin@mydomain.tld : root@hostname.mydomain.tld log_message = AL03: Sender rate limit for local submission \ exceeded: $sender_rate / $sender_rate_period. accept
故障排除
451 临时本地问题
如果您在测试 SMTP 时收到“451 Temporary Local Problem”消息,您很可能正在以 root 用户身份发送。默认情况下,Exim 不允许您以 root 用户身份发送。