Exim

来自 ArchWiki

Exim 是一个通用的邮件传输代理。本文建立在邮件服务器之上。虽然Exim Wiki提供了一些关于特定用例的实用操作指南,但同时也提供了所有配置选项的详细描述

安装

安装 exim 软件包。

基本配置

Exim 附带一个庞大的默认配置文件,位于 /etc/mail/exim.conf。其中的许多选项在常规用例中是不必要的。

配置可以在包含多个章节的单个文件中完成。Exim 的配置分为多个不同的部分。通用选项设置必须始终出现在任何其他部分之前。其他部分都是可选的,并且可以以任何顺序出现。除了通用选项部分之外,每个部分都以单词 “begin” 开头,后跟至少一个文字空格,以及该部分的名称。关于 exim 运行时配置的其他一般性评论,请参考官方文档的此链接

以下是一个非常基本的配置,它提供:本地投递给系统用户,Mbox(也称为传统 BSD 邮箱)格式,以及授权中继到 MX 主机。描述基于在主机 “hostname.mydomain.tld” 上服务的域 “mydomain.tld”。强烈建议在使用下面给出的文档之前查阅官方文档。

注意: 此处提供的配置文件设置顺序已修改,与软件包附带的默认配置相比有所不同。
警告: 默认配置文件的默认权限为 0644。这允许任何用户读取。如果您在其中保留敏感信息(LDAP 凭据等),请根据需要更改此设置,例如更改为 0640。

主要参数

必需的通用选项章节包含一些基本选项。仅使用这些选项将打开端口以进行连接,但仍然不会接受任何邮件,也不会中继到任何地方。

# 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,安全 & 身份验证

此文章或章节需要扩充。

原因: 低权限用户访问私钥是否存在事实上的担忧?为什么 Exim 私钥会丢失?(在Talk:Exim中讨论)
警告: 如果您部署 TLS,请务必遵循 服务器端 TLS 以防止漏洞。

要获取证书,请参阅 OpenSSL#用法

以下选项的第一部分仍然是 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 在邮件处理期间使用低权限用户 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 身份验证

此文章或章节需要扩充。

原因: 什么是 LMTP?(在Talk:Exim中讨论)

本节描述了 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
注意: 从 Exim 4.88 开始,lmtp 驱动程序存在一个限制:在 ACL 中,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 服务必须进行相应的调整。例如:这是一个Git 提交,修复了完全相同的问题

由于 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 conf 文件的开头,您必须使用以下命令启用 TLS

tls_advertise_hosts = +local_network : *.gmail.com

或向所有主机声明 tls

tls_advertise_hosts = *

有关 TLS 的更多信息,请参见 exim 文档

注意: 以下内容必须放在配置文件的适当部分,例如,在 begin authenticators 之后。

在 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_authhosts_require_tls 而不是 smtp.gmail.com,以避免偶尔出现的 530 5.5.1 Authentication Required 错误。这些错误是由 smtp.gmail.com 的 DNS 查询中不断变化的 IP 地址引起的。$host_address 将扩展为 gmail_route 路由器解析的特定 IP 地址。

为了增加安全性,请使用应用专用密码。这也适用于 Google Apps 帐户。

加固

速率限制

安全漏洞时有发生。如果您没有任何提交本地邮件的服务(从端口上的 localhost 接收邮件不被视为本地提交),请完全禁用本地提交。为此,请在 main 部分中添加 acl_not_smtp = acl_local,并在 acl 部分中添加以下简单 ACL。

/etc/mail/exim.conf - acl section
	acl_local:

		deny	log_message = AL01: Local submission denied.

如果需要本地提交,请考虑对其施加速率限制。为此,请在 main 部分中添加 acl_not_smtp = acl_local,并在 acl 部分中添加以下 ACL。它施加了 2 个速率限制:1 分钟内 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 身份发送。

参见