Postfix/dovecot 的 SASL 与 SSL/TLS 指南

/!\ 注意:本文档是为 CentOS 5 而撰写的。它的内容对 CentOS 6 或较后版本也许并不准确。

1. 引言

这份指南被设计来补充基本的 postfix 指南。它是针对 CentOS 5 写成。CentOS 6 的设置会有所不同。

2. SASL 是什么,我又为何需要它?

根据缺省值,postfix 采用 $mynetworks 参数进行访问控制,也就是谁能通过邮件服务器发放或传寄电邮。除你检查发放电邮的用户的 IP 地址隶属于 $mynetworks 所指定的获信任网络外,它不会进行其它验证。

如果你所落实的邮件服务器内所有用户都只属于同一个网络,你大概不需要采用 SASL 或 SSL/TLS。不过,假若你有流动的用户想在总部以外的地方运用邮件服务器,我们需要一个机制来验证他们是获信任的用户,好让他们能通过邮件服务器发放电邮。

SASL(简单鉴定和安全层)提供一个以名称及密码来验证让用户的机制。最负盛名的 SASL 方案要算是 Cyrus SASL 提供的库,但 dovecot 亦内置了它自行实施的 SASL,而既然我们已经运行 dovecot,我们不妨利用它作 SASL 之用,免除要安装及设置另一个组件。

3. 还有 SSL/TLS 呢?

可见 SASL 能够提供一个机制,通过用户名称及密码来验证那些想利用邮件服务器来发放邮件的远程用户。此外,远程用户亦可以利用 dovecot 所提供的 IMAP 及/或 POP3 机制来访问邮件。然而,我们面对的一个问题就是这些机制都以纯文本在互联网上传送用户名称及密码(SASL 可支持如 DIGEST-MD5 等加密的验证方法,但它们并未必获电邮客户端软件广泛支持)。这构成一个安全性危机,因为任何人都有可能拦截这些信息并盗取登录数据,所以我们须要将连接加密。SSL(安全套接层)及较新的 TLS(传输层安全)提供一个机制将两台主机之间的通讯加密,在这个例子中就是我们的邮件服务器及远程客户端。SSL 由第 3.1 版开始被 IETF 换名为 TLS。

4. 在 postfix 设置 SASL

要在 postfix 内设置 SASL,我们须要在 /etc/postfix/main.cf 内加入以下改动:

smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous

以及在 /etc/postfix/main.cf 内的 smtpd_recipient_restrictions 部份加入 permit_sasl_authenticated(假如你没有一个 smtpd_recipient_restrictions 部份,以下例子亦合适):

smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination

接着我们须要在 /etc/dovecot.conf 内的 authentication processes 部份设置 auth default。请按情况删除注释及/或加入以下内容(请小心,因为这部份有大量注释,某些项目可能已经存在,而其它例如 socket listen 却是被注释掉,而须要删除注释的):

auth default {
    mechanisms = plain login
    passdb pam {
    }
    userdb passwd {
    }
    user = root
    socket listen {
      client {
        path = /var/spool/postfix/private/auth
        mode = 0660
        user = postfix
        group = postfix
      }
    }
}

重新引导 dovecot 并重新装入 postfix 的设置:

service dovecot restart
postfix reload

/!\ 注:至于 CentOS 6 采用的 dovecot 2.x,请参阅:http://wiki2.dovecot.org/HowTo/PostfixAndDovecotSASL

5. 测试 SASL

既然我们已经设置了 SASL,我们便须要测试它能否正常运作。为了进行测试及/或考虑到本地安全为理由,你也许可以把 $mynetworks 限制为 127.0.0.0/8 来强制采用 SASL 验证,否则未有正确设置的本地客户端依然可以通过 smtpd_recipient_restrictions 内的 permit_mynetworks 设置来转发邮件。

/!\ 注:以下段落「过份操努」。CentOS 中附带了一个特定的工具,设有一个 openssl 的选项来测试这些以及其它东西。请参阅:man s_client这里所讨论的一个较简单方法采用了它。

我们可以 telnet 进入服务器并尝试以目己的用户名称及密码来进行验证。然而我们的用户名称及密码必须以 Base64 来编码(注:我们的用户名称及密码是被编码,而不是加密,解读它亦易如反掌,所以此刻它并不安全)。利用 perl,你可以按照以下方式产生一句以 Bas64 编码、包含我们的用户名称和密码的符串(在样例里我用了 test 这个用户及 test1234 作为密码)

$ perl -MMIME::Base64 -e 'print encode_base64("\000test\000test1234");'
AHRlc3QAdGVzdDEyMzQ=

作为参考,我们以 Base64 编码的符串可以这样被解读:

$ perl -MMIME::Base64 -e 'print decode_base64("AHRlc3QAdGVzdDEyMzQ=");'
testtest1234

对于那些没有 perl 的人们,这里备有一个网上的 Base64 编码器。

利我们用以 Base64 编码的符串来测试验证:

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
EHLO example.com
250-mail.example.com
250-PIPELINING
250-SIZE 20480000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN AHRlc3QAdGVzdDEyMzQ=
235 2.0.0 Authentication successful
quit
221 2.0.0 Bye
Connection closed by foreign host.

假如一切正常的话,我们应该会看见 AUTH PLAIN LOGIN(及 AUTH=PLAIN LOGIN),表示了邮件服务器现正提供 smtp 验证,而我们应该可以运用 Base64 编码的用户名称及密码来成功地验证。

现在我们可以设置邮件客户端在发放邮件时采用验证(用户名称及密码)。

Thunderbird:「编辑」 > 「帐户设置」,然后在左边面版选择「寄件服务器(SMTP)」。选择 smtp 服务器并点击「编辑」,然后在「安全与验证」部份,选择「采用名称及密码」并输入你的用户名称。

Outlook Express:「工具」 > 「帐号」 > 「邮件」页签 > 选择一个帐户并点击「内容」。然后在「服务器」页签里,于「外寄邮件服务器」之下,选择「我的服务器须要验证」。

如果我们发放一封测试消息并追查 maillog,假设一切是正常的话,便应该看见我们的邮件客户端以 SASL 进行验证:

tail -f /var/log/maillog
Mar 18 13:25:56 mail postfix/smtpd[22400]: connect from mail[127.0.0.1]
Mar 18 13:26:02 mail postfix/smtpd[22400]: 105892006E: client=mail[127.0.0.1], sasl_method=PLAIN, sasl_username=ned
Mar 18 13:26:02 mail postfix/cleanup[22404]: 105892006E: message-id=<47DFC2E4.30402@example.com>
Mar 18 13:26:02 mail postfix/qmgr[22338]: 105892006E: from=<ned@example.com>, size=518, nrcpt=2 (queue active)
Mar 18 13:26:02 mail postfix/smtpd[22400]: disconnect from mail[127.0.0.1]
Mar 18 13:26:02 mail postfix/local[22405]: 105892006E: to=<ned@example.com>, relay=local, delay=0.26,
       delays=0.11/0.02/0/0.14, dsn=2.0.0, status=sent (delivered to maildir)
Mar 18 13:26:06 mail postfix/qmgr[22338]: 105892006E: removed

6. 产生 SSL 凭证

在我们可以采用 SSL 或 TLS 把连接加密之先,我们必须产生用来辨认我们的服务器及确定加密协议的 SSL 凭证。SSL 凭证包含一对公钥及私钥,它们可以是由自己或是被信赖的凭证机构所签署的。你可以免费从 CAcert 取得被签署的凭证,而这里有一份指引。然而,很不幸地最常用的网页浏览器、电邮客户端、及操作系统暂时都不包括 CAcert 在被信赖的凭证机构之内,因此取得由 CAcert 签署的凭证所带来的好处相对减少,因为它在很多系统上不会被自动接纳。你应该采用自己签署的凭证抑或是由被信赖凭证机构所签署的凭证,很视乎你的服务器有何用途 —— 假如你的用户信赖你,那么自己签署的凭证很可能已经能满足你的需要。

OpenSSL 提供了很多方法来产生 SSL 凭证。你可以直接利用 openssl,或者使用它提供的脚本,如 /etc/pki/tls/misc/CA 或 /usr/share/doc/dovecot-1.0/examples/mkcert.sh。然而,对 openssl 的初用者来说,最简单的方法就是执行 genkey 并遵从划面上的提示。

enkey 是 crypto-utils 组件的一部份,并可以在命令行(以 root 身份)安装及运行。以下样例会为 mail.example.com 产生一双有效 1 年的金钥:

yum install crypto-utils
genkey --days 365 mail.example.com

/!\ 注:CentOS 6.4 及往后版本内的 nss 并不支持 md5。因此,genkey 这个指令也许不能运作。详情及权宜之计已收录在 NSS_3.14 的发行注记内。随著CESA-2013:1144 的发行,md5 已再度备受支持。

请根据划面上的提示输入有关你的地区的信息。最重要的项目是 common name,因为它必须与用户访问服务器时用的名称吻合。在我们的例子中,这就是 mail.example.com,但它很大机会是你为邮件服务器在 dns mx 记录内输入的名称。假设你的 smtp 及 imap/pop3 服务都是在同一台服务器上运行,你可以让它们采用同一张凭证,而客户端在 imap/pop3 及寄件 smtp 服务器的设置中都输入 mail.example.com。如果你拥有独立的 smtp 及 imap/pop3 服务器,你便须要为它们创建个别的凭证。要是你想从一个被信赖凭证机构取得一张凭证,genkey 亦可以为你创建一份凭证签署申请(CSR)。最后,有一选项可让你以密码将私钥加密。请勿将你的私钥加密,要不然无次服务器(或服务)重新引导时便会停下来等候你输入密码,在一台远程的服务器上这样有点不切实际!

凭证的金钥对将会被创建在下列位置:

/etc/pki/tls/certs/mail.example.com.cert  # 公钥
/etc/pki/tls/private/mail.example.com.key  # 私钥

你可把凭证保留在现有位置,或者将它们复制/迁移往别处。私钥必须只属于 root 及授权给他读/写(0600)。

7. 在 postfix 内设置 SSL/TLS

既然我们已经创建了凭证,我们就可设置 postfix 采用它们把 SASL 验证工作阶段加密。我们须要在 /etc/postfix/main.cf 加入以下内容:

smtpd_tls_security_level = may
smtpd_tls_key_file = /etc/pki/tls/private/mail.example.com.key
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.example.com.cert
# smtpd_tls_CAfile = /etc/pki/tls/root.crt
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_tls_cache
tls_random_source = dev:/dev/urandom
smtpd_tls_auth_only = yes

在 postfix 2.3 里 'smtpd_tls_security_level = may' 取代旧有的 'smtpd_use_tls = yes' 并启用 tls。我们注释掉 smtpd_tls_CAfile 但在使用已签署的凭证时却须要利用该设置来指定发行机构的凭证。'smtpd_tls_loglevel = 1' 会将 tls 的工作阶段记录在 postfix 的邮件日志(将它设为第 0 级停止记录 TLS,而第 2 级对侦错或许会有帮助)。smtpd_tls_session_cache 设置把 TLS 的工作阶段金钥暂存 1 小时,这个推荐是出于重复地为每次连接协商 TLS 工作阶段金钥的代价相对昂贵。

最后那个设置,smtpd_tls_auth_only = yes,强迫 SASL 验证采用 TLS 并禁止纯文本验证发生,除非已经创建了一个 TLS 工作阶段。(在测试时注释掉 smtpd_tls_auth_only = yes 也许会有用,好让我们能测试 SSL/TLS 能否运作,但当 SSL/TLS 失效时仍有纯文本 SASL 验证作为后备)。

请物忘记重新装入 postfix 的设置:

postfix reload

现在我们可通过 telnet 进入服务器并检查 postfix 有否提供 TLS:

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
EHLO example.com
250-mail.example.com
250-PIPELINING
250-SIZE 20480000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
quit
221 2.0.0 Bye
Connection closed by foreign host.

如果一切如期运作,我们应该会看见服务器提供 STARTTLS,而且因为我们指定 'smtpd_tls_auth_only = yes',纯文本 SASL 验证(AUTH PLAIN LOGIN 及 AUTH=PLAIN LOGIN)已不再被支持。

最后我们须要设置我们的邮件客户端在发放电邮时进行加密:

Thunderbird:「编辑」 > 「帐户设置」,然后在左边面版选择「寄件服务器(SMTP)」。选择 smtp 服务器并点击「编辑」,然后在「安全与验证」部份,于「使用安全连接」栏选择「TLS」(或「尝试使用 TLS」)。

Outlook Express:「工具」 > 「帐号」 > 「邮件」页签 > 选择一个帐户并点击「内容」。然后在「高级」页签里,于「外寄邮件服务器(SMTP)」之下,选择「这个服务器需要安全连接 - SSL」。

如果我们发放一封测试消息并追查 maillog,假设一切是正常的话,便应该看见一个 TLS 工作阶段被创建,用来把之后的 SASL 验证加密:

tail -f /var/log/maillog
Mar 21 01:51:42 jessie postfix/smtpd[1893]: connect from unknown[192.168.0.20]
Mar 21 01:51:42 jessie postfix/smtpd[1893]: setting up TLS connection from unknown[192.168.0.20]
Mar 21 01:51:44 jessie postfix/smtpd[1893]: TLS connection established from unknown[192.168.0.20]:
       TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)
Mar 21 01:51:44 jessie postfix/smtpd[1893]: D756E2006F: client=unknown[192.168.0.20], sasl_method=PLAIN, sasl_username=ned
Mar 21 01:51:44 jessie postfix/cleanup[1897]: D756E2006F: message-id=<47E314AE.1070702@example.com>
Mar 21 01:51:44 jessie postfix/qmgr[711]: D756E2006F: from=<ned@example.com>, size=511, nrcpt=2 (queue active)
Mar 21 01:51:45 jessie postfix/local[1898]: D756E2006F: to=<ned@example.com>, relay=local, delay=0.17,
       delays=0.13/0.02/0/0.02, dsn=2.0.0, status=sent (delivered to maildir)
Mar 21 01:51:45 jessie postfix/qmgr[711]: D756E2006F: removed
Mar 21 01:51:45 jessie postfix/smtpd[1893]: disconnect from unknown[192.168.0.20]

8. 在 dovecot 内设置 SSL/TLS

dovecot 在安装时已缺省启用 SSL/TLS 的支持。dovecot 甚至乎附带它自己的 SSL 凭证,它在这份教学文档的较早部份已被我们重新命名及被我们的凭证所替换。我们所关注的设置都收藏在 /etc/dovecot.conf 内:

protocols = imap imaps pop3 pop3s
#disable_plaintext_auth = no
#ssl_disable = no
ssl_cert_file = /etc/pki/tls/certs/mail.example.com.cert
ssl_key_file = /etc/pki/tls/private/mail.example.com.key
ssl_cipher_list = ALL:!LOW:!SSLv2 

作出修改后请重新引导 dovecot 服务:

service dovecot restart

假要采用 SSL/TLS 加密,你必须启用 imaps(993 端口)及/或 pop3s(995 端口)协议。被注释的设置代表缺省值。当被设为 yes 时,disable_plaintext_auth 将会停用一切纯文本的验证方法(除非连接来自那些被视为安全的 IP)。ssl_cipher_list 这个设置让我们可以列出可供应用的加密方法。除了缺省的清单外,我们禁用所有低限能的加密方法,及较不安全的 SSLv2 协议。

我们有数个方法可以应付本地及远程的安全性考虑。如果要所有本地及远程连接都强制加密,我们可以简单地停用不安全的 imap 及 pop3 协议,单单提供 imaps 及 pop3s,并设置 'disable_plaintext_auth = yes'。另外,我们或者会想容许本地用户采用纯文本的 imap 及 pop3 协议,但依然坚持远程用户采用加密。达至这样的方法是允许一切连接至 imaps 及 pop3s(分别为 993 及 995 端口),但通过防火墙局限 imap 及 pop3 端口(分别是 143 及 110)的连接至本地网络。譬如:

# IMAP(S)
# IMAP 只接受本地网络的连接
iptables -A INPUT -i eth0 -p tcp -s 192.168.0.0/24 --dport 143 -j ACCEPT
# IMAPS 接受所有连接
iptables -A INPUT -i eth0 -p tcp --dport 993 -j ACCEPT
#
# POP3(S)
# POP3 只接受本地网络的连接
iptables -A INPUT -i eth0 -p tcp -s 192.168.0.0/24 --dport 110 -j ACCEPT
# POP3S 接受所有连接
iptables -A INPUT -i eth0 -p tcp --dport 995 -j ACCEPT

请勿忘记在所围的防火墙设置合适的规则并转接适当的端口。

最后,我们必须设置我们的邮件客户端采用 SSL 加密。

Thunderbird:「编辑」 > 「帐户设置」,然后在左边面版选择邮件帐户的「服务器设置」。在「服务器设置」部份,选择「使用 SSL 安全连接」。请留意端口现在应该变为 993 或 995,分别对应 IMAPS 或 POP3S。

Outlook Express:「工具」 > 「帐号」 > 「邮件」页签 > 选择一个帐户并点击「内容」。然后在「高级」页签里,于「内送邮件 - POP3」之下,选择「这个服务器需要安全连接 - SSL」。请留意端口现在应该变为 993 或 995,分别对应 IMAPS 或 POP3S。

如果我们追查 maillog 并在邮件客户端内检查新邮件,便应该看见 TLS 加密正被运用中:

# tail -f /var/log/maillog
Mar 21 15:10:15 jessie dovecot: pop3-login: Login: user=<ned>, method=PLAIN, rip=::ffff:192.168.0.20, lip=::ffff:192.168.0.2, TLS
Mar 21 15:10:15 jessie dovecot: POP3(ned): Disconnected: Logged out top=0/0, retr=0/0, del=0/0, size=0

假若 TLS 没有出现在首行的最尾,那应事情并不对劲,而连接亦未被加密。

9. 连结

http://wiki.dovecot.org/HowTo/PostfixAndDovecotSASL

http://wiki.dovecot.org/SSL/DovecotConfiguration

http://www.postfix.org/SASL_README.html#server_sasl

http://www.postfix.org/TLS_README.html#server_tls

http://postfix.state-of-mind.de/patrick.koetter/smtpauth/

……

Translation of revision 28

zh/HowTos/postfix sasl (last edited 2014-12-27 08:52:01 by HaoLee)