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/lib/postfix/smtpd_tls_cache
tls_random_source = dev:/dev/urandom
tls_random_exchange_name = /var/lib/postfix/prng_exch
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-tw/HowTos/postfix sasl (last edited 2015-01-02 03:48:08 by TimothyLee)