發新話題
打印

[轉貼]建置 POSTFIX 伺服器

[轉貼]建置 POSTFIX 伺服器

http://newweb.syups.tp.edu.tw/documents/memo/postfix/postfix.htm

建置 POSTFIX 伺服器

作者:李忠憲 shane@mail.syups.tp.edu.tw

2003/12/09

增補第九章:2004/01/05

增補第零章及內容更新:2006/05/29

前言

postfix 是除了 sendmail 以外 ,最被廣泛採用的 Linux 郵件伺服器,一般使用的觀感不外乎兩點:

一、安全:垃圾信過濾機制較聰明,就算什麼都沒設定,也能濾掉許多 sendmail 擋不到的信。

二、簡單:不需設定,伺服器就能正常運作。

雖然以上談的這兩點特色,到底是好是壞還有許多爭議,但 Postfix 對於郵件服務需求量不高的校園來說,可以說是一個很好的選擇!

零、Email 概論

電子郵件系統的發展源遠流長,早從網路萌芽階段就被實作出來,早期電子郵件系統僅能應用在 ARPANET 網路上(1972年,當時 Internet 尚未誕生,而且是使用 FTP 傳送電子郵件),要參與交換郵件的主機都 必須安裝 sendmail,要看信必須 telnet 到主機上才看得到。在 1982 年時因應網際網路的誕生,制定 SMTP 協定作為不同網路系統間交換郵件的標準,當然在這樣的年代裡,還沒有病毒信和垃圾郵件的困擾。

電子郵件普及後,連一般無法安裝 sendmail 的工作站也都有寄信和收信的需求,這時就需要開發另一種通訊協定來應付這個需求,那就是 POP,這個通訊協定用來讓工作站可以連上郵件伺服器,進行帳號認證 後下載自己的郵件,並能將伺服器上的郵件刪除,目前是使用第三個版本稱為 POP3。另外還有一個功能更強大的通訊協定稱為 IMAP,它除了收信外還可以進行郵件信箱管理,多半應用於網頁郵件系統,例如:SquirrelMail......

由於 SMTP 通訊協定當初在設計時並未考量使用者認證的問題(僅設計了遞送功能),另一方面能提供使用者認證的 POP3 卻又未設計信件上傳功能(僅設計下載功能),這樣的設計當然會衍生出許許多多的弊病,包括郵件來源不明、反覆多餘的濾信機制、郵件轉寄難以防堵、郵件帳號驗證有安全漏洞、郵件容易偽裝......等等弊端。 而這些問題必須從通訊協定著手改良才行,目前新版的 SMTP 通訊協定已經加入了認證與安全連線機制足以應負此需求。

在實際安裝設定之前,先介紹幾個電子郵件系統的術語:

MUA 使用者透過這個程式與郵件伺服器溝通,包括收信(以 POP3 連接收信伺服器程式 imapd)或寄信(以  SMTP 連接 MTA),例如: Outlook Express......等。
MTA 使用 SMTP 通訊協定將信件傳遞到不同郵件主機上面,例如: sendmail, postfix, Qmail......等。
MSA 是新版 sendmail 發展給 SSMTP 進行 TLS/SSL 連線的 client 端代理器。
MDA 收到信後將信件分配到不同使用者信箱內,算是 MTA 的一個子系統,譬如 BBS 從定義上來說也算是 MDA 的一種,有些 MDA 被設計來進行濾信動作,它們必須在 local 端運作,因此又被稱為 LDA,例如: procmail......等。
MailBox 尚未被使用者下載的郵件,會暫存在伺服器的硬碟空間裡,稱之為信箱。所有使用者信箱的總合必須約等於該分割區總容量的一半,以避免造成信箱尚有空間但郵件系統卻無法運作的現象。
ail Gateway 是一種特殊的郵件伺服器,通常扮演代理器的角色,負責統籌某機構內所有信件的收發,並分配郵件給下屬的郵件伺服器群,透過這個機制能夠加速郵件的交換,並且能夠進行一致的濾信控制。

對於大型組織來說,通常會有超過一臺郵件伺服器,如果每一台都要做郵件過濾,管理上很麻煩,因此市面上已出現硬體式郵件閘道器,所有的郵件寄入或寄出都會經由該設備過濾後再分送給下屬的郵件伺服器群。

sendmail 是最多人使用的 MTA,但同時也是安全漏洞最多的系統,如果您安裝 sendmail,那麼定期更新程式是免不了的工作,為了這個原因有許多版本的 Linux 改採 postfix 作為內建電子郵件程式,如果安全需求更高則可以考慮購買商業版的 Qmail,這支程式到目前為止尚未被發現有任何安全漏洞。

使用 Mail Gateway 時必需配合進行 DNS MX 記錄的設置,以 Windows DNS Server 為例:

MX紀錄中應包含三個欄位,首先是郵件遞送的目的地(以網域名稱來判斷),接著指定要處理信件的伺服器,最後是優先權,數字越小越優先。一般觀念中認為 MX 紀錄是用來讓對方寄信給我們時查詢用的,這觀念並不完全正確,事實上當我們的郵件主機嘗試寄信時也會去查詢 MX 紀錄,以便得知是否要將信件傳遞給某台特定主機由它統一轉信,這個機制是用來實作 mail gateway,請用這個觀點來理解 sendmail.mc 中跟 MX 相關的選項,這樣觀念才不會錯誤!

電子郵件結構

下表是一封完整的電子郵件內容,你可以在 Outlook Express 中在信件上按右鍵選「內容」來觀察。當電子郵件在郵件主機之間傳送時,會紀錄詳細的傳送過程,稱為信封。信封是給郵件主機追蹤郵件用的,對一般使用者意義並不大,因此通常會被 MUA 應用程式隱藏起來。信件本文則又區分為標頭和內文兩個部分:標頭用來紀錄寄信人、收信人、信件主旨、優先等級......等等,這些都會用來作為郵件索引,以方便使用者搜尋想閱讀的信件;郵件內文就是我們看到的信件內容,它允許使用 MIME 格式來夾帶檔案。

當郵件主機收到信件時,會先進行郵件剖析將各個部分分離出來以方便進行後續處理,所謂後續處理包含:網址偽裝、使用者偽裝、檢查寄信人及收信人欄位是否完整、檢查是否轉信、分信、濾信......等等,這些過程將在後面討論。

mail envelope(信封)
Return-Path: <syups@mail.syups.tp.edu.tw>
Received: from www.syups.tp.edu.tw (www.syups.tp.edu.tw [192.57.1.2])
(authenticated bits=0) by mail.syups.tp.edu.tw (8.12.8/8.12.5) with ESMTP id h2K3qpnv011171 for <shane@mail.syups.tp.edu.tw>; Thu, 20 Mar 2003 11:52:51 +0800
Received: from mail pickup service by www.syups.tp.edu.tw with Microsoft SMTPSVC;
Thu, 20 Mar 2003 11:40:30 +0800
mail content件本文)
 
Header(信件標頭)
From: syups@mail.syups.tp.edu.tw
To: <shane@mail.syups.tp.edu.tw>
Subject:
修繕報修通知
Date: Thu, 20 Mar 2003 11:40:30 +0800
Body(信件內文)
●報修者姓名:林XX,他的E-mail是:xxx@mail.syups.tp.edu.tw
●問題類別:技術問題
●問題主旨:114電腦一直當機
●問題內容:
●請系統管理師盡速處理。
<<附加檔案>>

啟動收信服務

Fedora 已經不再使用過去 RedHat 時代所提供的 Super Server(xinetd)簡易服務,而改採安全性更高的 dovecot 獨立伺服器,設定上更為簡易,風格也近似於 Postfix 的設定方式,打開設定檔 /etc/dovecot.conf

通訊協定設定

#protocols = imap imaps pop3 pop3s 設定要啟動的通訊協定,建議使用 pop3s

認證方式設定

#disable_plaintext_auth = no 關閉純文字驗證功能,建議設為 yes
#ssl_disable = no 關閉加密驗證功能,建議設為 no
#ssl_cert_file = /etc/pki/dovecot/certs/dovecot.pem 指定 SSL 驗證金鑰(公鑰)
#ssl_key_file = /etc/pki/dovecot/private/dovecot.pem 指定 SSL 驗證金鑰(私鑰)

登入牢籠設定

#login_dir = /var/run/dovecot/login 登入殼層後所對應的資料夾,設定給 chroot 使用
#login_chroot = yes 開啟登入牢籠,會用上述路徑取代 / 目錄
#login_user = dovecot 登入後模擬成 dovecot 身份,請檢查 /etc/passwd 是否有此帳號

如果要啟用 SSL 安全通道,讓支援 spop3 simap MUA 能夠以更安全的方式來連線,請執行底下的指令,以便建立安全憑證,並將此兩個金鑰檔案的權限設定為 600

cd /etc/pki/dovecot
openssl req -new -x509 -nodes -out certs/dovecot.pem -keyout private/dovecot.pem -days 3650

配合 pop3s 的使用,請修改 outlook express 的設定:

手動測試 POP3 Server

想要知道 POP3 伺服器是否有正常運作,我們可以直接以 telnet 來進行測試,這樣做可以直接看到伺服器的反應,假設使用 Outlook Express 來測試,則伺服器的回應訊息大部分都會被隱藏起來,只有當發生問題時才會以蹦現視窗通知你,這樣比較無法達到學習效果,請依照下列步驟實際操作練習:

$ telnet 127.0.0.1 110
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
+OK Dovecot ready. //POP3 歡迎訊息
AUTH //查詢認證機制
+OK
PLAIN
//支援的認證方式為純文字密碼
.
AUTH PLAIN
//提出純文字認證
+
USER shane //送出登入帳號
+OK
 ASS mypasswd //送出密碼
+OK Logged in.
LIST  //列出信件
+OK 1 messages:
1 741 //第一封信共 741 Byte
.
RETR 1 //下載第一封信
+OK 741 octets
Return-Path: <mailer@mail.syups.tp.edu.tw>
Received: from www.syups.tp.edu.tw (www.syups.tp.edu.tw [192.57.1.2])
by mail.syups.tp.edu.tw (8.12.8/8.12.5) with SMTP id h2EAfGkf032324
 for <shane@mail.syups.tp.edu.tw>; Fri, 14 Mar 2003 18:41:16 +0800
From: mailer@mail.syups.tp.edu.tw
Received: from mail pickup service by www.syups.tp.edu.tw with Microsoft SMTPSVC;
Fri, 14 Mar 2003 18:29:44 +0800
To: <shane@mail.syups.tp.edu.tw>
Date: Fri, 14 Mar 2003 18:29:44 +0800
X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4807.1700
Message-ID: <WWWXf2LTrpwACkWHZqY000008b2@www.syups.tp.edu.tw>
X-OriginalArrivalTime: 14 Mar 2003 10:29:44.0578 (UTC) FILETIME=[A1464E20:01C2E]
Status:

寄信單位:5年04班 寄信人:黃元奎

.
DELE 1 //刪除第一封信件
+OK Marked to be deleted.
QUIT //結束連線
+OK Logging out, messages deleted.
Connection closed by foreign host.

壹、從 sendmail 到 postfix

Fedora 系統預設的郵件伺服器是 sendmail ,而 postfix 是不會自動安裝的,如果一開始安裝系統時忘記把 postfix 裝起來,可以使用下列指令將套件補上:

yum install postfix

為了便利於 sendmail 和 postfix 之間進行切換,系統提供一個工具可以自動幫我們作設定遷移,依底下指令安裝:

yum install system-switch-mail*

要進行郵件伺服器切換,請輸入 system-switch-mail 然後選擇您想要用的伺服器(sendmail 或是 postfix),雖然選好後系統會自動將伺服器關閉和啟動好,但我們依然可以使用底下指定來確定這件事(假設您選中的是 postfix):

service sendmail stop

service postfix start

這樣 postfix 應該就會開始提供服務了!什麼都不用設定就會跑了嗎?是的!

postfix 本身也可以當成指令來執行,例如:

postfix check    檢查設定檔是否正確,相關資料夾是否建立,檔案擁有人和存取權限是否正確

postfix start        啟動服務

postfix stop        停止服務

postfix abort        立刻中斷服務

postfix reload       重新載入設定檔

postfix flush        將佇列中的郵件全部寄出去

 

貳、postfix 工作流程

sendmail 與 postfix 最大的不同在於程式結構上,sendmail 以一支大程式來執行所有的工作,而 postfix 則改採模組化設計,這種設計的好處在於:程式碼容易維護、模組功能有彈性容易擴充。底下將針對組成 postfix 的每個模組,它們各是負責哪些工作,又如何與其它模組配合,作一個解說。

底下先說明收信流程:

  trivial-
rewrite(8)
Network -> smtpd(8)  
^
|
|
v
  \
Network -> qmqpd(8) -> cleanup(8) -> incoming
  /
  pickup(8) <- maildrop
  ^
|
Local -> sendmail(1) -> postdrop(1)
  • 當郵件由本機寄出時,系統執行 sendmail 程式,由於之前執行過 redhat-switch-mail 的關係,此時 sendmail 已經連結到 postfix 套件中的 postdrop 模組,postdrop 模組負責將郵件存放在 maildrop 資料夾中,等候 pickup 處理, pickup 模組會對郵件內容進行檢查以保護 postfix 系統不會受到異常郵件的攻擊(譬如利用欄位溢位來破解主機)。
     
  • 如果郵件是來自網路,postfix 套件中的 smtpd 將會接收郵件並進行郵件過濾,為了應付千變萬化的偽裝郵件, 我們可以透過修改 UCE 設定檔(例如:來自 sendmail 的 access 設定檔),設定黑名單(或透過 RBL 黑名單資料庫取得黑名單)、 DNS 反查驗證,甚至是使用正規表達式來作字串匹配檢查等方法,進行嚴格的過濾。
     
  • 一般 SOHO 族為了節省連線費用會去修改 MTA 設定採用離線寄信方式,平時對外網路關閉,信件堆積在 Mail Server 上,然後在特定時間接通網路並將佇列中的信件同時寄出,這類信件將由 qmqpd 處理。
     
  • 郵件有時候是來自 postfix 本身的 bounce 模組(合併到 trivial-rewrite 中),例如:當郵件無法遞送時,bounce 模組會自動產生訊息以便通知寄信人。
     
  • 郵件有時候也會由 LDA 產生,這種情形包括:根據 alias 別名資料庫改寫過收信人的郵件,或是根據每個使用者的 .forward 檔案指定的轉信信箱修改過收信人欄位的郵件。
     
  • 除了上述因無法遞送郵件引發的問題會由 bounce or defer 模組產生通知郵件外,其它的錯誤產生時 postfix 也會自行產生通知郵件,例如:SMTP 通訊協定逾時,UCE 過濾規則衝突......等等,當然這種情形下只會通知 postmaster。
     
  • 以上五種情況產生的新郵件,最後都會由 cleanup 模組進行最後的處理。首先它會補足郵件中所缺少的欄位(例如來自 console 的信件將會缺省 From: 欄位),並根據完整郵件地址格式改寫寄信人欄位(例如:shane@localhost 改成 shane@mail.syups.tp.edu.tw),接著讀取 canonical(全名對應) 和 virtual(虛擬對應) 設定檔,根據該設定改寫收信人欄位,所有動作處理完畢後,郵件被放置在 incoming 資料夾中,接著就會通知 qmgr 模組有新郵件到了,由 qmgr 模組開始進行送信作業 。
     
  • 當 cleanup 模組進行郵件欄位改寫時,實際上是呼叫 trivial-rewrite 模組來處理,和 sendmail 不同的是,postfix 並未發展出一套複雜的巨集語言來進行 rewrite 作業,相反的是採用單純的查表法來解決, 這也是 postfix 被批評功能不如 sendmail 強大的原因。postfix 開發者似乎認為 sendmail 之所以漏洞百出,與 rewrite 巨集語言大而無當有直接相關(純屬猜測)。

底下說明寄信流程:

  trivial-
rewrite(8)
  smtp(8) -> Network
/
 
^
|
|
v
- lmtp(8) -> Network
/
incoming -> active -> qmgr(8) --- local(8) -> File, command
 
^
|
|
v
  \
- virtual(8) -> File
  deferred   \
  pipe(8) -> Command
  • qmgr 模組是整個郵件伺服器的核心,它將視情形呼叫 local、smtp、pipe 等 agent 模組來進行寄信作業。local 模組就是 LDA ,它負責將信件分到使用者信箱。smtp agent 顧名思義是指 smtp client 程式,而非 smtpd ,它負責將信件透過 SMTP protocol 遞送到網路上的遠端郵件伺服器。pipe 則用來處理傳真或其它封閉式網路系統的特殊電子郵件通訊協定。

    當郵件出現遞送失敗的情形,qmgr 就會將郵件放在 deferred 佇列中,以避免延遲其他郵件的遞送作業 。

    正在處理中的郵件會被放在 active 佇列中,該佇列僅存放少量郵件,因為若郵件過多的話將會耗用大量記憶體,來自 incoming 資料夾的郵件,會根據郵件到達的順序依序進入 active 佇列, 而 deferred 佇列的郵件,將會暫停四個小時後,重新排入 active 佇列。

    此外,qmgr 也會根據 relocated 設定檔來產生通知郵件,relocated 設定檔用來設定帳號或網域遷移的通知訊息,當 qmgr 發現某封郵件的收信人已經遷移,則會呼叫 trivial-rewrite 模組處理 。
     

  • 在遞送郵件的過程中,qmgr 會呼叫 trivial-rewrite 模組解析收信人地址,通常 trivial-rewrite 模組只會區分該信件是要到達本機,還是需要 relay,當然我們也可以透過 transport 設定檔,來要求 trivial-rewrite 模組針對特定收信人地址遞送給特定主機(通常用來設定將 mail gateway 收到的郵件轉給下屬郵件伺服器)。
     
  • 當信件無法遞送時(收信人或收信主機不存在或無回應), qmgr 將障礙情形以郵件型式遞交給 cleanup 模組(在收信流程中)進行收信作業,同時將原始郵件排入 deferred 佇列,等待延遲遞送。如果無法遞送的原因是通訊協定或設定檔錯誤造成的(這種情形應通知管理員,而非寄信人),則信件將直接由 qmgr 處理。
     
  • local 模組主要工作是將信件寫入使用者的信箱中,或是根據 alias 設定或 .forward 設定改寫收信人欄位,並遞交給 cleanup 模組進行收信作業,除了 postfix 預設的 local 模組外,其它在 sendmail 中經常使用的外掛 LDA,例如:procmail,也可以繼續使用,這是因為 postfix 的 local 模組是與 sendmail 完全相容的。

其它幕後常駐模組:

  • master:這個模組用來監督 postfix 所有模組,是否有依照 master.cf 中的設定執行,並會將超過上限的行程砍掉,以符合效能調校相關設定值。
     
  • flush:這個模組用來維護 incoming 郵件佇列的雜湊表,它將郵件依收信目的地分類,建立多個雜湊表,當用戶端送出 ETRN syups.tp.edu.tw 要求時,則可以將要給 syups.tp.edu.tw 的郵件(有些可能還在排隊)優先全部寄出。
     
  • showq:當執行 mailq 指令查詢郵件佇列時,就是由此模組提供資訊。
     
  • proxymap:用來讀取設定檔,並提供設定檔內容給其他模組,之所以這樣做是為了減少硬碟 I/O 存取。
     
  • spawn:用來呼叫執行外掛程式,透過這個機制我們可以執行非 postfix 提供的過濾程式,例如:病毒過濾。
     
  • pipe:這個模組用來將郵件佇列中的郵件資料結構,傳輸給外部程式處理。當 master 模組啟動外掛程式時,透過 pipe 當白手套就可以和外掛程式憑藉參數傳遞來溝通。
     

常用工具:

  • postalias:當我們執行 newalias 時,由於 redhat-switch-mail 的作用,其實是在執行 postalias,所以你也可以不用因此改變使用習慣喔!如果要透過這個指令建立 alias 雜湊表,指令格式比 newalias 稍微複雜一點:postalias hash:/etc/postfix/aliases,這道命令也可以用 postmap 代替,例如:postmap hash:/etc/postfix/aliases,其餘功能請參閱 postmap 的介紹
  • postcat:這是一個郵件 RFC 格式的剖析器,可以將原始郵件顯示成容易閱讀的格式,有點像用 outlook express 看到的郵件內容(假如在郵件上按右鍵查看原始內容,你可能就看不懂了),請指定要查看的檔名:postcat /var/spool/postfix/deferred/filename,由於檔名都是使用郵件編號,如果不知道要看的郵件是什麼檔名時,可以用下面的指令查詢!
  • postqueue:用來取代 mailq 指令,使用 -p 參數就可以將郵件依編號順序列出郵件標頭(寄信人、收信人和信件主旨),方便檢索查詢!-s 參數則是列出前面介紹過的 flush 雜湊表!
  • postsuper:這個命令用來操作佇列中的郵件,只有 root 身分才能執行。參數 -d 用來刪除郵件,-r 用來將郵件排到不同佇列裡(maildropincomingactivedeferred),-p 當伺服器(或 postfix 程式)當掉重開機後,用來救回工作到一半的郵件。
  • postconf:這是個功能強大的工具,除了可以用來顯示 main.cf 的詳細設定內容外(自動分類),也可以用來除錯,甚至還提供修改設定的介面,例如:postconf -e mynetworks=168.100.189.0/25,這個功能如果結合 script 程式來運作,就能做到透過網頁遠端管理設定擋。我們經常使用 -n 參數,來查看 postfix 設定檔擺放的位置!
  • postmap:用來取代 makemap 指令,並且與 makemap 完全相容,支援的檔案類型包括:btreedbmhash當用來建立資料庫檔案時,語法如後:postmap hash:/etc/postfix/access,這個工具也可以用來查詢資料庫檔案裡的內容,例如:postmap -q 172.16.2 hash:/etc/postfix/access,這會列出該行的規則,如要刪除可以使用:postmap -d 172.16.2 hash:/etc/postfix/access,透過這個方式能及時有效的管理,不用重新修改 access 文字檔,然後雜湊成資料庫,然後重新啟動 postfix 載入設定檔等等,作一連串動作!

 

叁、main.cf 基本設定

postfix 最被人稱道的地方在於設定檔的可讀性很高(當然是跟 sendmail 比),而在主要設定檔 main.cf 中,需要自行定義的東西並不多,而且這些參數就算不去定義,依照預設值也可以運作:

由本機寄出的郵件要使用哪個網域名稱

在 sendmail 中,這個功能稱為網域偽裝,也就是說可以和主機名稱不同,例如:本機名稱為 mail.syups.tp.edu.tw,而寄出的郵件其寄信人欄位則可以使用 syups.tp.edu.tw。(須配合 DNS MX 紀錄使用)

myorigin=syups.tp.edu.tw

不使用簡略名稱時,設定如下:

myorigin=mail.syups.tp.edu.tw

由於主機名稱與網域名稱會被反覆引用在不同設定值,為了簡化設定,通常是定義成變數:

myhostname = mail.syups.tp.edu.tw
mydomain = syups.tp.edu.tw

#myorigin=$mydomain
myorigin=$myhostname (省略時,視同此設定)

當然以上變數也可以省略不定義,這種情況下 postfix 將使用 gethostname( ) 函數取得系統設定。如果是將 myorigin 省略,則視同為設定 $myhostname。

另外,如果本機是某個網域的 mail gateway,也就是說網域內的所有郵件伺服器,都必須把它們的郵件送到本機來處理,這種情形可以使用 masquerade_domains 讓 postfix 自動缺省寄信人欄位中的郵件主機名稱,例如:

masquerade_domains = $mydomain

假設 $mydomain 是 syups.tp.edu.tw,則無論信件是來自 stu.syups.tp.edu.tw 或 mail.syups.tp.edu.tw 都將會被改寫成 syups.tp.edu.tw。

如果你有某些帳號不想要讓 mail gateway 改寫寄信人欄位,可以設定底下的參數(預設不啟用):

masquerade_exceptions = root

啟用 masquerade_domains 功能只會改寫郵件標頭與信封裡的寄信人欄位,如果想要將收信人欄位一併改寫,可以設置底下的參數:

masquerade_classes = envelope_sender, envelope_recipient, header_sender, header_recipient

注意:這樣做將使得 mail gateway 無法寄信給下屬郵件伺服器,因為收信人欄位 @ 後面的郵件網域名稱會被改寫成 mail gateway 自己。

要接收來自哪些網路介面的郵件

底下參數是用來定義要處理來自哪些網路介面的郵件,當未定義時,預設會處理本機所有網卡(可以使用 ifconfig 觀察),如果定義成 localhost ,則只有 loopback 介面會接受郵件,也就是說只接受主控台或 WEB 介面寄出的郵件,不接受 MUA(outlook express)遞交的郵件:

inet_interfaces = localhost

設定成 all ,表示接受所有網路介面的郵件,這是預設值:

#inet_interfaces = all

設定成 $myhostname ,表示要透過 DNS 反查 IP,當本機 IP 是由 DHCP 指派,而 DNS 又未被設定成 DDNS ,將會出現不可預期的錯誤!

#inet_interfaces = $myhostname

底下範例將會在有 DNS A 紀錄的 IP 以及 loopback 介面上提供服務,在這裡我們可以發現 main.cf 中有許多設定可以設定超過一個值,將它們用逗號區隔開來就行了!(底下的設定範例不適用於 postfix 本身兼 Proxy/NAT 的場合)

#inet_interfaces = $myhostname, localhost

哪些網域的郵件是給本機的

定義在這裡的網域將被視為是 local 網域,換句話說寄到這些網域的郵件,會被接收並分信給本機使用者,相當於 sendmail 中的 local-host-name,當不使用 DNS MX 紀錄時,設定如下:

mydestination = $myhostname localhost.$mydomain

如果有使用 DNS MX 紀錄,需修改為:

mydestination = $myhostname localhost.$mydomain $mydomian

假設該伺服器在 DNS 上有多筆 A 或 CNAME 紀錄,則須將這些紀錄也寫上去,例如:

mydestination = $myhostname localhost.$mydomain $mydomian www.$mydomain ftp.$mydomain

哪些郵件要進行 relay

在 postfix 中針對轉信網域作處理的參數有兩個,一是用來定義區域網路網段的 mynetworks,另一個是 relay_domains,未被定義在這些參數裡的網域,系統將拒絕轉信!

首先說明區域網路網段設定,相關參數共有兩個:mynetworks_style 和 mynetworks,前者用來宣告區域網路類型,subnet 代表是子網路,這也是預設值,當未作設定時,postfix 將會自行根據 ifconfig 上登記的 IP 和 網路遮罩作運算,自動求出子網路的範圍;如果設定成 class,則是不理會遮罩,自動信任同一個 class 等級的電腦,如果該伺服器使用撥接上網,這樣設定將使得同一家 ISP 的撥接用戶,都可以利用本伺服器轉信,這是非常危險的(除非你是 ISP 公司);設定成 host 則僅該單機可以寄信。

#mynetworks_style = class
#mynetworks_style = subnet
#mynetworks_style = host

mynetworks 用來設定區域網路的 IP 範圍,剛才設定的 mynetworks_style 將會被此參數取代,如果省略不設定,則由 mynetworks_style 來決定要 binding 的網卡:

mynetworks = 168.100.189.0/25

mynetworks = 168.100.189.10

除了上面範例,設定成網段或單機外,也可以指定多個網段(多重 NAT 網域時),或是使用設定檔(條列式),或使用雜湊表(makemap hash postmap hash:)。

#mynetworks = 168.100.189.0/28, 127.0.0.0/8
#mynetworks = $config_directory/mynetworks
#mynetworks = hash:/etc/postfix/network_table

有關轉信網域設定,以上兩個參數就已經足以應付各種需求,但為了與 sendmail 相容,仍然保留 relay_domains 設定,與 sendmail 不同的是,如果該 relay_domain 在 DNS 上有定義 MX 紀錄,將會被 postfix 故意忽略,而當成 mynetworks 來處理,這是為了避免被利用作為廣告信回信站台;relay domain 對 sendmail 而言是雙向的,但對 postfix 而言則是單向的,只能寄信,而且不代收回信,因此如果要將本機設定成 mail gateway,請使用 mydestination,而不要使用 relay_domains(注意:很多介紹 postfix 的文章都犯此錯誤!)建議不要設置此參數,使用預設值「不啟用」。

#relay_domains = $mydestination

當本機為某台 mail gateway 的下屬伺服器時,必須要將所有寄出的信件交給 mail gateway 代轉,這時可以設定 relayhost 為該 mail gateway 的 IP。預設不啟用。

#relayhost = $mydomain
#relayhost = mg.syups.tp.edu.tw
#relayhost = [172.16.1.3]

哪些情況須通知管理員

所謂管理員是指 postmaster 信箱,請修改 alias 設定將 postmaster 對應成管理人員真正使用的信箱,以免警告郵件沒人理會堆積在伺服器上。

notify_classes 參數用來決定哪些情形下須通知管理員,預設值如下:

notify_classes = resource, software

可以使用的參數包含:

bounce 當一般郵件無法交遞時,產生警告信給管理員(會附上原始郵件的內文)。注意:郵件無法交遞時,本來就會寄警告信給發信人,管理員收到的不過是複製版本。
2bounce 當寄給發信人的警告信無法交遞時, 產生警告信給管理員。
delay 當對方站台忙碌要求延遲遞送時,產生警告信給管理員(不會附上原始郵件)。
policy 對方寄信的要求因為不符合安全規範已經被回絕(被規則過濾掉),此時會產生警告信給管理員。
protocol 通訊協定錯誤時產生警告信通知管理員,我們比較感興趣的是對方使用了哪些不合法的 SMTP 命令。
resource 系統資源短缺導致信件無法寄出,例如:硬碟 I/O 錯誤,此時會產生警告信給管理員。
software 軟體安裝不全或程式錯誤造成的問題,產生警告信給管理員。

 Proxy/NAT 地址

當郵件伺服器位於 proxy 或 NAT 防火牆的後方時,此參數用來設定真實 IP,以避免因為 MX 紀錄與本機 IP 不同,將該信誤判成需 relay 到其它 MX 的郵件;當本機所查詢的 DNS 其回覆 MX 紀錄為虛擬 IP 時,此種現象將不會發生,因此在 NAT 虛擬網域中,架設內部專用 DNS 是非常重要的,請參考網路名稱系統一文的解說。預設值為:

proxy_interfaces =

當本機被設定成其它網域的備份 MX 時,如果未設置此參數,郵件會成為伺服器間互踢的皮球(去詢問其它網域的 DNS 查詢 MX 紀錄時,查到的必然是真實 IP,即使有架設內部專用 DNS 也沒用)。設定如下:

proxy_interfaces = 163.21.166.7

 

肆、main.cf UCE(unsolicited commercial email)過濾

過去這類的郵件被稱為垃圾郵件,比較正式的稱呼是 SPAM 郵件,postfix 則稱此種郵件為 UCE,有那麼一點縮小打擊範圍的含意,因此使用 UCE 過濾並無法解決其它問題郵件(例如:匿跡郵件、病毒郵件、郵件炸彈)所帶來的困擾,請不要期望過高。

儘管如此,與 sendmail 使用 access 來進行存取控管相比較,postfix 的 UCE 過濾顯然要精細得多,彈性也比較好,以外掛方式讀取過濾規則使得管理員能隨時修改設定,並將它模組化,可以說是 postfix 最大的優點。

如前所述,postfix 並不使用複雜的巨集語言來進行規則運算,而是採用較為單純的查表法來控制,但各位可不要小看它,它所支援的查表方式可謂琳瑯滿目,諸如:欄位比對( 純文字檔,欄位以逗號或空格或定位點區隔)、DBM 檢索、HASH 雜湊、NIS 查詢、RBL 查詢.....等,比對規則也可以選擇採用正規表示法(regexp)或是 perl 改良過的正規表示法(pcre)。

UCE郵件測試

[root@linux postfix]# telnet 172.16.11.1 25
Trying 172.16.11.1...
Connected to 172.16.11.1.
Escape character is '^]'.
220 linux.syups.tp.edu.tw ESMTP Postfix
helo test.com  //嘗試假冒自己身份為 test.com
250
linux.syups.tp.edu.tw
mail from: spam@test.com
//假裝寄信人是 spam@test.com
250 2.1.0 ok
rcpt to: spam@test.com
//假裝收信人也是 spam@test.com
250 2.1.5 o
//居然被接受了,其實因為是從 127.0.0.1 連線,所以會這樣
quit
221 2.0.0 Bye
Connection closed by foreign host.

這次改從遠端連線進行測試
220 linux.syups.tp.edu.tw ESMTP Postfix
helo test.com //假冒自己的身分
250
linux.syups.tp.edu.tw
mail from: spam@test.com
//假裝寄信人是 spam@test.com
250 2.1.0 ok
rcpt to: spam@test.com
//假裝收信人是 spam@test.com
550 5.7.1 Error: Proper authentication required.
//果然被要求要先登入
rcpt to: shane@mail.syups.tp.edu.tw
//改寄給本機帳號 shane@mail.syups.tp.edu.tw
250 2.1.5 ok
//被接受了,事實上單純收信是不會擋 RELAY
data
  //開始輸入信件內容
354 End data with <CR><LF>.<CR><LF>
From: 你抓不到我@test.com //透過這個標頭, 可以取代 MAIL 指令所設定的郵件信箱地址,以避免被追蹤
To: 你抓不到我@test.com //透過這個標頭, 可以取代 RCPT 指令所設定的郵件信箱地址,以避免被追蹤
S
ubject: hello

hi, how are you?
.
//結束輸入,必須另起一行然後輸入英文句號
250 2.0.0 Ok: queued as 88AF713C0152 //信件被接受並進行分信處理
quit
221 2.0.0 Bye
Connection closed by foreign host.

認證模式測試
220 ESMTP
auth plain
//嘗試以純文字密碼登入
503 5.5.1 Error: authentication not enabled //這種驗證機制已經被移除
auth login
//嘗試以 POP3 方式登入
334 VXNlcm5hbWU6
//提示輸入 username,之所以看不懂是因為被編成 base64
shane //直接輸入帳號是錯的 ,請先使用底下指令找出 base64 字串:
perl -MMIME::Base64 -e 'print encode_base64("要編密的字串");'

334 UGFzc3dvcmQ6
//提示輸入 password
123456 //與帳號相同,要先編成 base64
535 5.7.0 Error: authentication failed
//由於 base64 編碼事實上並未加密,最好還是不要用
auth digest-md5
//嘗試其他驗證機制存不存在
504 5.3.3 Error: AUTH mechanism digest-md5 not available
starttls
//嘗試 TLS/SSL 安全傳輸層有沒有啟用
454 4.3.3 Error: TLS not available after start
...........................................................

 

郵件標頭過濾

標頭過濾所過濾的對象,除了郵件標頭外,更擴大範圍到附加檔案的 MIME 標頭,使得過濾可以更精確的進行,而不會因規則過於模糊,殃及無辜的郵件。用過 procmail 的使用者要特別注意:附加檔案檔名或檔案類型是在此過濾,而非在郵件內文過濾。設定方式如下:

header_checks = regexp:/etc/postfix/header_checks
header_checks = pcre:/etc/postfix/header_checks

在外掛設定檔 header_checks(可以改用其它檔名)中,當字串比對命中時,可以採取各種處理動作,包括:

REJECT 拒收信件。
OK 跳過符合條件的標頭不作後續檢查,在 sendmail 中一旦 OK 該信件就會被接受,但在 postfix 中,OK 僅用來跳過該標頭的後續比對,萬一有其它標頭被拒絕,該封郵件一樣會被拒絕。
IGNORE 從郵件刪除該標頭。
WARN 附加警告訊息。
HOLD 放回佇列,等候處理。
DISCARD 直接將郵件丟棄,不回應拒收訊息。
FILTER transport.nexthop 呼叫外掛過濾程式,進行郵件內文剖析過濾。外掛過濾程式可以是任何一種可執行的檔案,例如:shell script。該程式必須先定義在 master.cf 中,模擬成一個 socket 來執行(由 master 模組負責伺服監聽),當需要呼叫它執行時,postfix 中的 clearup 模組會將整封郵件丟到指定的 port 號,master 模組監聽到訊息後會執行相對應的過濾程式。

header_checks 的範例如下:

/^Subject: Make Money Fast/ REJECT
/^To: friend@public.com/ REJECT

如果未設置此參數,則郵件標頭過濾功能將會關閉不啟用,這是系統預設值。

郵件內文過濾

這是用來過濾所有標頭過濾沒檢查到的郵件內容,設定方式與前面相同:

body_checks = regexp:/etc/postfix/body_checks
body_checks = pcre:/etc/postfix/body_checks

如果未設置此參數,則郵件標頭過濾功能將會關閉不啟用,這是系統預設值。

用戶端過濾

當用戶使用 SMTP 通訊協定連上伺服器提出寄信請求時,針對用戶端輸入的指令進行過濾。在前面的UCE郵件測試中已經詳細論及在 SMTP 連線階段中出現的各種欺騙伺服器的手法, postfix 提供非常詳盡的設定可以針對這些問題加以預防。使用用戶端過濾時,必須將 smtpd_delay_reject = yes 設定上去,這是系統預設值。當設定成 no 時,雖然效率較高,但是這樣做將會使得 HELO 網域偽裝、送信人信箱偽裝、寄信人信箱偽裝以外的其它過濾功能失效。

用戶端過濾能夠使用的過濾功能,包括:

reject_unknown_client 用戶端之 IP 或 Domain name 無法從 DNS 查詢驗證時,拒絕連線。
permit_mynetworks 符合 $mynetworks 定義的用戶端允許連線。
reject_rbl_client 從 SPAM 資料庫站台驗證用戶端網域名稱,符合時拒絕連線,當這種情況發生時,postfix 將會依照 default_rbl_reply 的設定回覆相關訊息,也可以依照 rbl_reply_maps 的設定根據不同用戶端給予不同訊息,事實上我們根本不需要設置此兩個參數(除非想將訊息改成中文)。這個參數必須放在最後面,當成過濾政策。
reject_rhsbl_client 同上,使用另一種 SPAM 資料庫站台。這個參數必須放在最後面,當成過濾政策。
check_client_access 根據 access 設定過濾存取權限,與 sendmail 中的 access 資料庫相容。 可以省略參數名稱,直接寫檔名,例如:hash:/etc/postfix/access。
permit 允許連線,設定在過濾規則的最後面,表示未被之前的規則拒絕的用戶端一律允許連線,也就是採用黑名單政策。
defer 延遲連線,設定在過濾規則的最後面,表示未被之前的規則拒絕或接受的用戶端,必須在稍後重新接受檢驗,也就是採用拖延政策。
reject 拒絕連線,設定在過濾規則的最後面,表示未被之前的規則接受的用戶端一律拒絕連線,也就是採用白名單政策。
warn_if_reject 被拒絕時產生警告訊息,這是用來測試過濾規則用的。
reject_unauth_pipelining 當用戶端持續一直傳送 SMTP 命令時,拒絕其連線,這可以防止某些軟體一次寄送大量郵件。

使用用戶端過濾跟稍後會介紹的各種 SMTP 過濾,可以把規則依照前後順序編排成一組規則鍊(寫成一行,中間用逗號隔開或從逗號後面分行),由於組合出來的過濾功能並非單獨運作的,因此順序非常重要!

smtpd_client_restrictions = reject_rbl_client dialup.ecenter.idv.tw(台灣免費的 SPAM 資料庫:擋撥接發廣告信)

smtpd_client_restrictions = reject_rbl_client relays.ordb.org(國外免費的 SPAM 資料庫:擋開放轉信的伺服器)

smtpd_client_restrictions = reject_rbl_client spam.ecenter.idv.tw(台灣免費的 SPAM 資料庫:擋寄廣告信的信箱)

smtpd_client_restrictions = hash:/etc/postfix/access, reject(採用白名單政策)

smtpd_client_restrictions = permit_mynetworks,reject_unknown_client

smtpd_client_restrictions = permit_mynetworks,hash:/etc/postfix/client_checks,reject_unknown_client,reject_unauth_pipelining

是否要求使用 HELO 命令

當啟用此功能時,將要求用戶端進行連線時須先傳送 HELO 字串,稍後我們可以根據 HELO 字串傳回來的網域名稱進行過濾,由於某些寄信程式不會傳送 HELO 命令,這樣做有可能會使得這些用戶端程式無法正常寄信。預設值是:

smtpd_helo_required = no

HELO 命令過濾

用來過濾 HELO 命令後面的網域名稱是否允許其連線,能夠使用的過濾功能,包括:

reject_invalid_hostname 網域名稱字串不符合文法時,拒絕其連線。
reject_unknown_hostname 網域名稱無法從 DNS 查到 A 或 MX 紀錄時,拒絕其連線。
reject_non_fqdn_hostname 網域名稱不是完整 FQDN 格式時,拒絕其連線。
check_helo_access 根據 access 設定過濾存取權限。
其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 請參考前面的說明。

設定範例:

smtpd_helo_restrictions = permit_mynetworks, reject_invalid_hostname

信封標頭欄位過濾

此功能用來過濾郵件的信封標頭是否符合 RFC 821 之規定,預設是不啟用此過濾。因為目前最多人使用的 MUA 是 outlook express,它會使用許多額外的標頭來進行郵件控制,例如:大家熟知的要求回覆功能,如果啟用此參數將使得這些信件被拒絕無法寄出。

strict_rfc821_envelopes = yes

寄信人過濾

此功能並非過濾郵件標頭裡的寄信人欄位,而是過濾 mail from: 命令後面的字串,預設值是不過濾,但由於廣告信寄信程式為了能順利寄信,經常會偽造此字串,建議應該啟用。

可以使用的選項包括:

reject_unknown_sender_domain 寄信人的網域名稱無法從 DNS 查詢驗證時,拒絕連線。
reject_rhsbl_sender 寄信人信箱如果被紀錄在 SPAM 資料庫站台,就拒絕他連線。
check_sender_access 根據 access 設定過濾存取權限。
reject_non_fqdn_sender 寄信人的網域名稱不是完整 FQDN 格式時,拒絕其連線。
reject_sender_login_mismatch 寄信人信箱與登入的帳號不吻合時,拒絕其連線。須配合 SASL 使用者認證機制使用(SMTP AUTH)。

配合 smtpd_sender_login_maps 指定的對應表,可以讓登入帳號與使用的信箱作對應,例如:shane 帳號可以用 webmaster 信箱寄信。

其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 請參考前面的說明。

設定範例如下:

smtpd_sender_restrictions = reject_rhsbl_sender dsn.rfc-ignorant.org(國外免費的 SPAM 資料庫:擋寄廣告信的信箱)

smtpd_sender_restrictions = hash:/etc/postfix/access, reject_unknown_sender_domain

smtpd_sender_restrictions = permit_sasl_authenticated,reject_unknown_sender_domain,reject_non_fqdn_sender

收信人過濾

此功能並非過濾郵件標頭裡的收信人欄位,而是過濾 rcpt to: 命令後面的字串,預設值是不過濾,但由於廣告信寄信程式為了能順利寄信,經常會偽造此字串,建議應該啟用。

可以使用的選項包括:

permit_auth_destination 收信人網域符合 $relay_domains、$mydestination、$inet_interfaces、$vitual_alias_domains、$virtual_mailbox_domains 的定義時,接受連線。
reject_unauth_destination 收信人網域不符合上述設定時,拒絕連線。
permit_mx_backup 當從 DNS 上查到本機為收信人網域的備份 MX 時,接受連線。使用此功能有安全漏洞,可以配合 permit_mx_backup_networks = 172.16.0.0/16 來檢查主要 MX 是否在該網段內,來加強過濾功能(避免被不信任的網域設定為轉信 MX)。
check_relay_domains 允許代收要給 relay_domians 的信件。
check_recipient_access 根據 access 設定過濾存取權限。
check_recipient_maps

當收信人網域不符合 permit_auth_destination 之要求,或是收信人信箱不符合 $local_recipient_maps、$virtual_alias_maps、$virtual_mailbox_maps、$relay_recipient_maps 的定義時,拒絕連線。此參數可以放在收信人過濾規則的最後面,當作過濾政策。

reject_unknown_recipient_domain 收信人的網域名稱無法從 DNS 查詢驗證時,拒絕連線。
reject_rhsbl_recipient 收信人信箱如果被紀錄在 SPAM 資料庫站台,就拒絕他連線。
reject_non_fqdn_recipient 收信人的網域名稱不是完整 FQDN 格式時,拒絕其連線。
其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 請參考前面的說明。

設定範例如下:

smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination,reject_non_fqdn_recipient

ETRN 命令過濾

用來過濾哪些網域或哪些用戶端,可以使用 ETRN 命令。ETRN 命令用來一次處理大量郵件,當某個用戶端使用 ETRN 時,有時候會影響到其它用戶寄信的效能,通常只有撥接用戶、幫撥接用戶轉信的 mail gateway、郵件討論群組(mailing list)或電子報發行站台,需要使用此功能。postfix 的預設值是所有用戶端都可以使用 ETRN 命令。

能使用的特殊參數只有 check_etrn_access,其餘與用戶端過濾參數相同,請自行查閱前文。設定範例如下:

smtpd_etrn_restrictions = permit_mynetworks, hash:/etc/postfix/etrn_access, reject

 

伍、效能調校

在這一章中,所有未特別說明的參數,都是設定在 main.cf 中!

行程限制

系統預設行程限制(default_process_limit)為 100,也就是說同時可以收發總共 100 封郵件,如果發現伺服器效能很差,可以嘗試降低此數值,請修改 master.cf:

# ==========================================================================
# service type private unpriv  chroot wakeup maxproc command + args
#                  (yes)    (yes)    (yes)    (never)  (100)
# ==========================================================================
    . . .
    smtp      inet      n           -               -               -               10               smtpd
    . . .

以上各欄位意義說明如下:

service 識別名稱
type 服務類型總共有三種:inet、unix、fifo。inet 是指透過網路介面 sockets 提供服務(例如:127.0.0.1:25),unix 指使用 unix sockets 提供服務(直接呼叫執行),fifo 是指使用 pipe name 提供服務(例如:網路傳真) 。
private 切斷對外服務,預設值是 yes。注意:inet 類型無法設定成 yes。
unpriv 不要以 root 身分執行,而是以 $mail_owner 身分執行。預設值是 yes。
chroot 開啟郵件暫存資料夾時,要不要將該資料夾變成根目錄,這是為了防止與 postfix 無關的資料夾遭到入侵者以 $mail_owner 身分闖入。預設值是 yes。注意:pipe、virtual 和 local 模組無法設定成 yes。
wakeup 服務每隔多久喚醒一次,預設值是 0(不喚醒)。只有 pickup、qmgr 和 flush 模組需要設定喚醒週期。
maxproc 最大執行緒。
command + args 該服務執行的命令及參數。

master.cf 除了控制 postfix 各個模組的運作方式外,也可以加入外掛過濾引擎,postfix 希望透過這個方式與其它程式設計專家合作,後文將介紹兩個經常使用的過濾程式 SapmAssassin 以及病毒過濾軟體 amavisd + clamav。更詳細的內容可以自行到 www.postfix.org 查看!

同步處理限制

postfix 採用同步處理限制來進行流量調整和控制,當 postfix 寄信到某個郵件主機時,首先傳兩封信過去(initial_destination_concurrency = 2),如果一切正常則逐步增加每次傳送的量,一直到傳輸失敗或者是到達同步上限每次 20 封信(default_destination_concurrency_limit = 20)。

如果想要針對不同 agent 來設定同步上限,也可以使用底下的參數(未設定的參數將會沿用 default_destination_concurrency_limit 限制):

local_destination_concurrency_limit = 2
uucp_destination_concurrency_limit = 2
smtp_destination_concurrency_limit = 10

收信人限制

這是指一封信可以寄給多少人,postfix 預設可以處理 50 個收信人(default_destination_recipient_limit = 50),如果一封信的收信人超過 50 人,postfix 會自動將此信複製成很多份,以 50 人為單位分批寄送。

和同步處理限制一樣,可以針對不同 agent 來設定不同上限:

uucp_destination_recipient_limit = 2
smtp_destination_recipient_limit = 10

延遲傳送

當郵件伺服器使用撥接線路連線時,由於部分時段處於斷線狀態,當 postfix 處理信件時會因為無法收發信件,持續產生錯誤訊息,為了避免發生這個現象,我們可以設定 defer_transports = smtp 來告知 postfix,要從 smtp agent 傳送出去的郵件暫時不要傳送。這些郵件可以等到上線後,再以 ETRN 指令全部寄出。

如果本機是前述郵件伺服器的 mail gateway,由於該伺服器只有部分時段上線,因此有可能 mail gateway 已經累積許多信件等待傳送給它,為了避免 mail gateway 持續嘗試傳送,可以設定:

defer_transport = hold

接著在 /etc/postfix/transport 設定:

customer.com   hold:[gateway.customer.com]

這個設定的意思是,要給 customer.com 的郵件先暫存在 gateway.customer.com,等待前者上線後再全部傳送給它(使用 ETRN 命令)。

設定好後,還需修改 master.cf,找到 smtp 行程設定(可參考前面小節),將 smtp 改為 hold 即可:

hold   unix   -   -   n   -   -   smtp

傳送失敗處理

當郵件傳送失敗的時候,負責傳送郵件的 Agent 會將郵件退回給 qmgr 模組,qmgr 模組則會計算從郵件到達到現在的時間間隔,依此時間間隔將郵件排入延遲傳送佇列中,以等待下次傳送。

如果該封郵件傳送到一半的時候失敗了,也就是說有些收信人有收到,有些沒有。這種情況下,除了將該郵件排入延遲傳送佇列外,也會將傳送失敗的對象排入 dead 清單一段時間,在這段時間內如果有其它郵件要傳送給這些對象時,就會直接排入延遲傳送佇列,而不用徒勞無功地去嘗試傳送!

底下是有關於郵件傳送失敗處理的相關效能設定:

queue_run_delay = 1000s qmgr 模組每 1000 秒(約 16 分鐘)檢查一次 defer 佇列,查看是否有郵件須排入 active 佇列
maximal_queue_lifetime = 5d 無法傳送的信件在 defer 佇列裡最多保存 5 天,超過時間則退給寄信人
minimal_backoff_time = 1000s 傳送失敗的郵件至少在 defer 佇列中暫停 1000 秒,而且被排入 dead 清單的收信人至少也要待 1000 秒,也就是說在這段時間內不再嘗試寄信給他
maximal_backoff_time = 4000s 傳送失敗的郵件最多在 defer 佇列中等待 4000 秒(約 1 小時)
qmgr_message_recipient_limit = 1000 dead 清單的大小,也就是說第 1001 個傳送失敗的對象,不會被排入 dead 清單

拖延戰術

當懷有惡意的用戶端連續傳送大量郵件時,postfix 為了處理這些郵件耗掉太多資源,導致無法正常工作,這也就是經常被討論的「阻斷服務攻擊」。

postfix 的設計者認為阻斷服務攻擊是不可能被解決的,因為我們無法單從郵件區分出它是惡意或善意,但是我們可以透過一些手段來降低損害。postfix 採用的方法是針對每條連線,設定一個連線錯誤計數器( session error count),當用戶端連線時,開啟計數器,如果用戶端傳送不存在的 SMTP 命令(這絕對是惡意想阻斷服務),或是超過字數限制的長字串(記憶體溢位攻擊)、超過一行的標頭(引發郵件剖析錯誤),計數器就會不斷累加。當郵件交寄成功時,計數器才會歸零重新計算。

現在我們只要根據計數器採取適當的處理動作就行了:

smtpd_soft_error_limit = 10 當計數器到達 10 時,就暫停該連線一段時間
smtpd_hard_error_limit = 100 當計數器到達 100 時,直接斷線
smtpd_error_sleep_time = 5s 每次暫停 5 秒鐘

 

陸、資源管制

postfix 可以在記憶體有限的系統上執行,而不會影響其它服務的效能,這是因為 postfix 提供的記憶體管理功能非常有彈性,可以依據各種需求加以調整。

每封郵件用量限制

當 postfix 處理郵件時,必須將郵件暫存於郵件佇列中,其中 maildrop 和 incoming 佇列使用硬碟,而 active 和 deferred 則使用記憶體,每封暫存在佇列中的郵件耗用多少記憶體是由郵件資料結構來決定,幸運的是這個資料結構的欄位大小是可以微調的,透過這些微調就能決定 postfix 的最大記憶體用量了!

line_length_limit = 2048 從用戶端接收待寄郵件時,每行最多 2 KB
header_size_limit = 102400 每封郵件的標頭大小不得超過 100 KB
extract_recipient_limit = 10240 每封郵件的收信人欄位不得超過 10 KB
message_size_limit = 10240000 每封郵件(包含信封)的大小,不得超過 10 MB
queue_minfree = 當記憶體剩下多少 Bytes 時,才可以處理下一封郵件,預設是沒有限制。
bounce_size_limit = 50000 警告信的大小限制為 50 KB。

假設通通使用預設值,也就是所有參數都不設置,那麼處理一封郵件須耗用 10.05 MB,再加上 postfix 模組程式的大小,總共約 20 MB,這也就是 postfix 運行的最小需求了!

郵件數量限制

當前述用量限制設置完畢後,接著我們還可以針對郵件佇列一次要處理多少郵件作出限制,把每封郵件記憶體用量乘上郵件數量,就可以算出所需的記憶體總量,當記憶體足夠時,我們當然希望儘可能多處理幾封郵件來增進 postfix 的效能。

qmgr_message_recipient_limit = 1000 這個參數之前介紹過了,除了用來控制 dead 清單的大小外,也控制著處理中的郵件收信人總量,兩者的預設限制都是 1000。
qmgr_message_active_limit = 1000 最多同時處理 1000 封郵件。
duplicate_filter_limit = 1000 在進行收信人過濾時,要快取多少已通過過濾的清單,這個功能是用來提高過濾效能,預設要快取 1000 個不同收信人

時間限制

postfix 某些模組在運作時,會依照設定檔的要求讀取外掛程式或是執行 shell 命令,有些則是會讀取外部檔案。如果無限制的讓 postfix 等待外部命令執行完畢或等待外部檔案讀取完畢,將會因為這些外部程式的設計不當或 I/O 衝突,而導致 postfix 無法運作,因此就需要設定等候時間限制,超過此時間限制 postfix 將逕行處理下一個程序。

舉例來說:當 local agent 將郵件分到使用者信箱時,會透過 proxymap 模組讀取 alias 資料庫,接著根據 alias 設定讀取 :include: 檔案,最後讀取 .forward 檔案,前述動作中如果其中一個因為系統 I/O 忙碌無法於時間限制內讀取檔案,local agent 就會直接跳過進行下一個處理動作。

command_time_limit = 1000s 等候外部命令或 I/O 的時間不可超過 16 分鐘。
service_time_limit = 這個參數的目的是允許不同 service 採用不同時間限制,因此它會取代前述參數的設定,其中 service 就是 master.cf 中的第一個欄位。

檔案鎖定

當 local agent 要將信件分到使用者信箱時,有時候使用者正透過 POP3 讀取信箱,因此信箱被鎖定無法開啟,這種情況發生時,local agent 必須等候一段時間重新嘗試讀取檔案,但也不能一直等下去,所以必須要有一些限制。

postfix 支援兩種的檔案鎖定機制:一、使用系統函式 fcntl( ) 或 flock( ),二、使用 local file,postfix 將根據作業系統的不同,選擇其中一種或兩種並用。有關檔案鎖定機制在這裡不予討論,有興趣的讀者可以從「專業 Linux 程式設計」(Wrox 出版,碁峯翻譯經銷)一書一窺究竟。

deliver_lock_attempts = 5 檔案被鎖定時,嘗試讀取 5 次。
deliver_lock_delay = 1s 每次嘗試讀取前,先等候一秒鐘。
stale_lock_time = 500 當 lock file 存在超過 500s 時,強制刪除 lock file 解除其鎖定狀態。使用 lock file 其實是透過程式設計技巧來模擬檔案鎖定功能,它必須由程式設計師自行維護鎖定狀態,萬一有粗心的設計者鎖定檔案後忘記解除,或是程式當掉無法解除鎖定,都會造成檔案長期被鎖定的假象,所以需要此設定來排除問題。

行程自動回復

當行程或子執行緒因為某些原因當掉,例如:記憶體不足......等,這個時候 master 將會延遲一段時間後嘗試重新啟動該行程。當然,如果程式當掉的原因是 main.cf 檔案損毀所造成的,就算是不斷重複啟動也不能恢復正常,因此 postfix 也會將當掉的情形紀錄在系統日誌裡,以便管理員偵錯並人工修復。

fork_attempts = 5 行程當掉以後,會嘗試重新啟動它 5 次!
fork_delay = 1s 每次重新啟動前,先等候一秒鐘。
transport_retry_time = 60s qmgr 每隔 60 秒嘗試驅動 agent 進行分信。

 

柒、郵件地址改寫機制 vs 相關設定檔

當 cleanup 模組進行郵件過濾之前,會先依據各種設定檔呼叫 rewrite 模組,進行郵件地址改寫,這樣做的好處是可以減少標頭欄位的變化,使得過濾快取(請參考 duplicate_filter_limit 的說明)能更有效率的工作。

郵件地址改寫的順序如下:

@hosta,@hostb:user@site 改寫成 user@site postfix 不支援前面那種特殊寫法
site!user 改寫成 user@site 將 UUCP 格式改寫為現在使用的地址格式
user%domain 改寫成 user@domain 將 % 符號去掉
user 改寫成 user@$myorigin 從 local 寄出的信會缺少網域名稱,在此補上
user@host 改寫成 user@host.$mydomain 將 hostname 改成 FQDN 型式
user@site. 改寫成 user@site 去掉絕對網域名稱後面的小數點

根據各種設定檔改寫郵件地址:alias、canonical、virtual。

alias、canonical、virtual 等設定檔是為了達成某些特殊功能而設計的,底下說明這些設定檔的功能以及如何啟用它們:

別名對應

alias 是 postfix 最被廣泛使用的一項功能,除了用來對應虛擬信箱外,也可以運用在郵遞清單或郵件討論群組上面。請在 main.cf 中加入這一行,以便啟用 alias:

alias_maps = hash:/etc/aliases

如果您的系統僅支援 dbm 格式的別名,或是透過 NIS 伺服器查詢別名,請使用底下的參數:

alias_maps = dbm:/etc/aliases, nis:mail.aliases

postfix 檢查信件的收信人欄位,當發現收信人的網域名稱為本機網域時就會開始進行分信,分信之前會先檢查上述設定中的 aliases 別名檔,看看是否有定義虛擬信箱,所謂虛擬信箱就是指不是給使用者使用的信箱,這些信箱多半是有特殊用途,例如:系統管理用的、郵寄群組用的......等等。也有人把 aliases 別名檔拿來作使用者全名的對應,例如:Shane.Lee 對應成 shane,雖然這也是一種應用的方式,但按照 postfix 設計的原意,應該是使用 canonical_maps 去做這件事比較正確,兩者不同的地方在於:canonical_maps 在進行收信人欄位改寫時就直接處理了,不會等到分信時才處理,而且按照 aliases 的設計必須要能載入 include、呼叫執行 script,並且允許使用遞迴別名,這都會降低處理效能,如果只是單純要作全名對應使用 aliases 比較不划算!

當安裝 postfix 時系統會自動幫我們產生 aliases 別名檔,同時會把系統上的服務帳號對應給 root,這是因為服務帳號是給應用程式使用的,當應用程式執行過程中遇到某些突發狀況時,或網路上的其他管理人員需要聯絡該應用程式的管理人時,有時候會寫信給這些帳號,透過 aliases 別名檔的作用,這些信件就會分到 root 的個人信箱以免沒人處理。但在實際環境中,為了避免 root 密碼在網路上傳輸遭到駭客竊取,不管是透過 outlook express 或是透過 web 介面,我們都不該去收取 root 的信件,而應該在 aliases 別名檔中將 root 的信件分給實際管理該機器的使用者,而且為了避免駭客查出誰是真正的管理員,這個檔案的權限應該設成 600。預設別名檔的內容節錄如下:

mailer-daemon: postmaster
postmaster:  root

# General redirections for pseudo accounts.
bin:       root
daemon:     root
============中間省略===================

# trap decode to catch security attacks
decode:    root

# Person who should get root's mail
#root:    marc <==請去掉 Remark 並改成自己的帳號

aliases 除了用來將虛擬信箱對應給真實的帳號外,還可以將信件儲存在備份檔案內,這樣做的目的是希望信件被收走後,能留一份在伺服器上備查,例如我們可以把上例解說過的那一行改為:

root:   shane,/var/log/backup-mail

除此之外,aliases 也可以用來轉寄信件,例如下面的設定會將信件轉寄出去,以便確定 shane 一定收得到:

root:   shane,shane@ms8.hinet.net,shane@tmtc.edu.tw,shane@tp.edu.tw

當要轉寄的信箱很多時,全部寫成一行還真是困難,譬如想寄同一封信給全校所有老師,但不想一個一個輸入收信人地址,透過 aliases 的幫助,我們只需要建立一個 teachers 虛擬信箱,接著準備一個空的純文字檔,把全校老師的信箱地址打進去,一個地址一行,最後得留一行空白行。檔案做好後,我們就可以將別名設定改成:

teachers:   ":include:/etc/mail/all_teachers.txt"

利用這個方式,想同時寄信給一群人再容易不過了。但是,當郵寄群組很多而且成員相互重疊時,想要維護郵寄清單的正確性就會很痛苦,好比除了全校教師外,我們另外還有各學年教師、科任教師、行政人員、主任等多個清單,學期末老師退休或學期初新進同仁加入,這麼多清單要同時檢查修改不能遺漏,這工作可累了!還好 aliases 提供了遞迴參照的功能,讓我們可以將清單分批建立,並以參照方式建立聯集,工作就輕鬆多了,例如:

headship:    lee       (校長)
director:   suelan,wang,............(主任)
manager:    maggy,young,..........(組長)
grade1:    jack,mary,helen,........(一年級老師)
grade2:    vicky,johnny,,,,,,,......(二年級老師)
..........................................................(省略)
course:              sean,marc,.................(科任)
administer:     headship,director,manager(行政人員)
teachers:     administer,grade1,grade2,grade3,grade4,grade5,grade6,course(全校老師)

在更複雜的應用中,aliases 還可以結合外掛的程式提供更進階的服務,例如:郵件訂閱、自動回信系統(auto reply)、討論群組(mailing list......等。例如:

check:   "| perl /usr/libexec/subscribe.cgi"

如果您想建立討論群組但不想花精神維護它,建議您安裝 smart list,這個套件雖然陽春但是非常簡易好用,它包含:信件訂閱、取消訂閱、討論文件索引備分、查詢討論清單與內容、自動清除遞送有問題之訂閱者......等等。如果您想讓使用者都能建立自己的討論群組,majordomo 是第一選擇,這個軟體不但使用者眾多,相關統計分析工具也已經開發得很完善,這些套件在 rpm 安裝時會自動修改 aliases 組態,需要手動修改的情況不多,因此不在此討論。

設定好 aliases 之後,必須以 postalias 指令來雜湊該檔案以利 postfix 讀取應用,由於 aliases 是隨用隨讀,因此並不需要重新啟動 sendmail 伺服器,它會立即生效。

全名對應

這個設定檔用來建立使用者全名與帳號的關係,例如:

shane@mail.syups.tp.edu.tw    shane.lee@mail.syups.tp.edu.tw

如果網域名稱,已經設定在 $myorigin、$mydestination 或 $inet_interfaces 中就可以省略不寫,直接使用底下的語法:

shane    shane.lee

此外這個設定檔還有一個用途,就是當網域遷移時,可以將信件對應到新的網域名稱,例如:

@mail.syups.tp.edu.tw    @tp.edu.tw

當本機是 mail gateway 時,透過底下的設定可以得知哪些使用者是位於哪一台郵件主機上,分信時會分給該主機,而非分到本機的 /var/spool/mail 中:

shane    @stu.syups.tp.edu.tw(帳號相同時)
shane    s60101@stu.syups.tp.edu.tw(帳號不同時)

當設定檔輸入完成後,必須先將它雜湊成資料庫檔,以 btree 為例(與 sendmail 相容,但效能較差,建議改用 hash):

postmap btree:/etc/postfix/canonical    (檔名可隨個人喜好更改)

接著在 main.cf 中加入底下這一行:

canonical_maps = btree:/etc/postfix/canonical

如果只想對應收信人欄位則將該行,取代成:

recipient_canonical_maps = hash:/etc/postfix/recipient_canonical

如果只想對應寄信人欄位則將該行,取代成:

sender_canonical_maps = hash:/etc/postfix/sender_canonical

虛擬對應

虛擬對應僅能作用在信封裡的收信人欄位,而不會去改寫郵件標頭,與全名對應相比較,虛擬對應的功能只能算是半套。虛擬對應一般應用在兩種場合:一、將收信人欄位中的全名改成帳號名稱,二、虛擬網域對應。想啟用虛擬對應,請在 main.cf 中加入底下這一行:

virtual_maps = btree:/etc/postfix/virtual

至於虛擬對應的設定方式與全名對應相同,請自行參考前面小節的介紹。

使用者遷移

當使用者已經不再使用此信箱,而改用其它伺服器提供的信箱時,並無法通知所有的親朋好友,因此仍然會有不知情的寄信人寄信過來,這些郵件到底該如何處理呢?

首先使用者可以在自己的家目錄裡,建立 .forward 檔透過這個檔案將郵件轉寄到新的信箱,管理員也可以透過 alias 別名對應幫該名使用者轉信。雖然這樣做很人性化,但也有兩個缺點:一、轉信會增加 postfix 處理郵件的負擔,導致效能降低,二、親朋好友並不知道要把信寄去哪裡才對,所以不斷地麻煩我們!

另一種處理的方法是不要替已經搬走的使用者轉信,而直接告訴對方該寄去哪裡才對,如果管理員也不清楚新的郵件地址,直接告訴對方不要再寄信過來也可以,使用這個功能請在 main.cf 加入:

relocated_maps = hash:/etc/postfix/relocated

/etc/postfix/relocated 的設定範例如下:

shane    使用者 shane 已經不再使用此信箱了,請將信寄到 tp.edu.tw 主機,新帳號是 sean!

在提示訊息中,我們不使用 @ 符號(sean@tp.edu.tw)是為了避免被信箱收集者賣給廣告商。

郵件繞送路由

transport_maps 允許我們直接將符合條件的郵件,透過特定的方式,傳送到特定的主機,而不需要依靠 DNS MX 紀錄,譬如前面已經介紹過的延遲傳送機制外(給撥接用戶使用的),要啟用 transport 功能,請在 main.cf 加入:

transport_maps = hash:/etc/postfix/transport

底下是 transport 設定檔的應用範例,意思是要將寄給 @syups.tp.edu.tw 網域的信件,傳送到 172.16.1.6 這台主機的 8025 port:

syups.tp.edu.tw    smtp:[172.16.1.6]:8025

應用場合

優秀的管理員明白各種設定檔各有職司,就算功能可以互相取代,也不應該混合著用,例如:用全名對應機制作別名對應,用別名對應機制作虛擬對應......這樣作恐怕沒有人可以釐清其邏輯關係,將來要如何維護呢?

到底什麼情況下要用哪一種機制來實作呢?筆者給大家的建議是:

將寄信人帳號對應成全名 sender_canonical_maps = hash:/etc/postfix/sender_fullname
將收信人全名對應成帳號 virtual_maps = hash:/etc/postfix/recipient_loginname
網域名稱遷移 canonical_maps = hash:/etc/postfix/domain_canonical
使用者遷移 relocated_maps = hash:/etc/postfix/relocated
將寄給本機帳號的信件分到其它伺服器 canonical_maps = hash:/etc/postfix/individual_user
將寄給虛擬網域的郵件轉給真實存在的網域 virtual_maps = hash:/etc/postfix/virtual_domain
讓使用者登入後能改用虛擬信箱寄信 smtpd_sender_login_maps = hash:/etc/postfix/unaliases
將寄給虛擬信箱的郵件轉給真實存在的使用者 alias_maps = hash:/etc/aliases
將郵件導向某台郵件過濾或掃毒主機 transport_maps = hash:/etc/postfix/transport

 

捌、SpamAssassin

SpamAssassin 是一套用來協助過濾垃圾郵件的程式,它會針對信件中特定的比對樣式給予不同的分數,當分數超過指定的值後,該封郵件就會被當作垃圾郵件處理,處理方式依據設定的不同,會在信件標頭或郵件主旨欄位加入警告訊息。

SpamAssassin 必須結合 MTA 來使用,單獨執行並不能發揮其功效,由於該程式是使用 perl 語言開發,因此如果剛開始安裝 Linux 作業系統時沒有一併安裝 perl,事後要補裝有一點小麻煩(非 Fedora 用戶可以先安裝 CPAN 程序以便透用它簡化安裝:perl -MCPAN -e shell、install Mail::SpamAssassin)Fedora 用戶請直接利用 yum 指令安裝:

yum install spamassassin

安裝好之後,請以下列指令啟動 spamd:

service spamassassin start

並將該服務設定為開機自動執行:

chkconfig --level 3,5 spamassassin on

請用底下指令,測試程式能否成功執行:

spamc -c < /var/mail/username

假設該使用者信箱已經有至少一封郵件,這時 spamc 程式會將該郵件傳送給 spamd(127.0.0.1:783)進行評分,如果執行成功應該會看到 SpamAssassin 的評分,例如:

3.5/5.0

讓 postfix 使用 SpamAssassin 過濾郵件

確定 SpamAssassin 可以正常執行之後,接著設定 /etc/postfix/master.cf,請修改 smtp deamon 服務的設定:

smtp inet ............中間五個欄位不用修改............. smtpd -o content_filter=spamfilter:

這一行的意思是要求 smtpd 在進行完郵件過濾後,透過 spamfilter 服務進行郵件全文過濾,因此我們還得定義 spamfilter 這個服務(服務名稱可以自行修改):

spamfilter    unix    -    n    n    -    100    pipe  flags=Rq user=nobody  argv=/usr/sbin/spamc.sh -f ${sender} -- ${recipient}

這裡定義 spamfilter 服務使用 unix socket 執行,最多同時進行 100 封信的過濾(如果記憶體不足請自行修改數量),過濾方式是透過 pipe 模組以 nobody 身分啟動 /usr/sbin/spamc.sh ,並且將目前佇列中的郵件其資料結構中的 sender 和 recipient 參數讀出來,連同 -f 參數傳遞給 spamc.sh 當作參數。旗號 R 表示要把信封中的寄信人地址,以 Return-Path: 標頭插入郵件中。旗號 q 表示參數中的特殊字元要用單引號括住,以避免被當成控制字元處理,在這個例子中用來避免郵件地址中的 @ 符號被當成控制字元。

接著我們還得自己寫一支小小的 shell script,底下就是 /usr/sbin/spamc.sh 的內容(這個檔案須自己建立,之所以放在 /usr/sbin/ 是因為這個資料夾的權限比較嚴格,也比較安全):

#!/bin/sh
/usr/bin/spamc -t 30 -e /usr/sbin/sendmail -i "$@"

不要忘記將該程式的檔案權限改成 755。pipe 模組傳遞參數給上面這支程式時,會將整封郵件的內容放在鍵盤的 input 佇列中,所以我們使用 sendmail -i 這道指令將郵件從 input 佇列讀進來,"$@" 會被 spamc.sh 的參數巨集取代,也就是說 "$@" 會變成

 -f sender@sender.domain -- recipient@recipient.domain

這個字串將當成參數傳遞給 sendmail 程式,意思是要 sendmail 把參數傳遞進來的「寄信人地址 -- 收信人地址」寫在信封的寄信人欄位裡。 mail from: 與 rcpt to: 原始參數的取得,是由 smtpd 於建立 SMTP 連線時,將該字串擷取出來放在郵件佇列的資料結構中,當 master 喚醒 spamfilter 服務時,透過 pipe 當白手套,把郵件資料結構中的字串傳遞給 spamc.sh 再傳遞給 sendmail。透過這些程式的接力表演,現在終於可以把這兩個參數當作過濾的鍵值。

附帶一提:如果本機沒有安裝 SpamAssassin,想要透過別台主機安裝好的 SpamAssassin,這時候可以修改 spamc.sh:

/usr/bin/spamc -d 172.16.1.6 -p 783 -t 30 -e /usr/sbin/sendmail -i "$@"

使用這個方法前,請先確認從本機到過濾主機之間的防火牆已經放行 783 port。

刪除可疑的垃圾郵件

SapmAssassin 發現垃圾郵件的可疑對象時,會加入一個郵件標頭,至於要不要把信刪除,則不關它的事。我們可以透過 header_checks 機制(前參照第肆章 UCE 過濾的說明)把具有 SPAM 標頭的郵件刪除,然而這樣做是有風險的,萬一有些普通信件因為長得太像垃圾信而被標示為  SPAM,那就會一起被刪除!

如果你真的想刪除可疑郵件,請修改 main.cf 設置下列參數:

header_checks = pcre:/etc/postfix/header-checks

找到 /etc/postfix/header-checks 檔案,如果檔案不存在請自己建立,加入底下這一行:

/^X-Spam-Flag: Yes/    DISCARD

垃圾郵件經驗學習法

SpamAssassin 判斷一個檔案是否為垃圾郵件,是透過郵件特徵比對 hits 比率(原理有點像 proxy server),也就是說必須使用一段時間後,才能從經驗中學習到哪些郵件內容反覆不斷出現,而要學會哪些郵件是垃圾郵件,最有效的方法是透過一個垃圾信箱來學習,首先我們建立一個沒人使用的信箱,接著以此信箱為名義在 news server 上發表測試文章。

等幾天後,很快的就有一大堆垃圾信湧入該信箱,現在我們只要把信餵給 SpamAssassin 就行了,底下是透過 alias 來餵信:

fakeuser: "| /usr/bin/spamassassin -r -w fakeuser"

上面這個範例中,指令參數 -r 的意思是要 SpamAssassin 把所有郵件的寄信人信箱當成垃圾信的樣板,從此以後符合此樣板的郵件都會被認為是垃圾信。-w fakeuser 是一個非必要參數,SpamAssassin 將會以 fakeuser 信箱的名義寄警告信給對方,但是因為對方使用偽裝過的信箱,因此也沒有人會收到警告信,寄了也是白寄,建議不要使用此功能。

除了透過 alias 可以餵信給 SpamAssassin 外,我們也可以利用 .forward 來餵信,請在 fakeuser 的家目錄建立此檔案,檔案內容如下:

| /usr/bin/spamassassin -r -w fakeuser

決定 SPAM 過濾門檻

SpamAssassin 根據郵件比對的得分高低,來決定該郵件是否為垃圾信,預設值是 5 分,建議修改為 9 分。請修改 /etc/mail/spamassassin/local.cf:

required_hits 9.0

白名單與黑名單

SapmAssassin 維護一個郵件資料庫,該資料庫將透過自動學習機制來增減郵件的分數,分數高於門檻的稱之為黑名單,分數低於門檻的稱之為白名單,一旦某種特徵的郵件被評為黑名單,以後該信箱寄出的郵件都會被認為是垃圾郵件,雖然自動學習機制對於主動發現垃圾郵件很有用,但難免也有誤判的時候,這個時候我們可以透過以下命令來調整:

/usr/bin/spamassassin -W < message 將該郵件加入白名單
/usr/bin/spamassassin --add-to-blacklist < message 將該郵件加入黑名單
/usr/bin/spamassassin -R < message 將該郵件移出白名單
/usr/bin/spamassassin --add-addr-to-whitelist=mailbox 將該信箱加入白名單
/usr/bin/spamassassin --add-addr-to-blacklist=mailbox 將該信箱加入黑名單
/usr/bin/spamassassin --remove-addr-from-whitelist=mailbox 將該信箱移出白名單

當某個信箱被誤判為垃圾郵件,這時我們可以直接將該信箱加入白名單,例如:

/usr/bin/spamassassin --add-addr-to-whitelist=shane@mail.syups.tp.edu.tw

該信箱一旦加入白名單後,只能以手動方式移出或重新加入黑名單。如果要把具有某種特徵的郵件加入白名單(例如:校內郵件討論群組、校園電子報、網站更新通知......等,這類郵件經常被視為垃圾郵件),請先將該封郵件另存成純文字檔,接著將該信餵給 SapmAssassin 即可,請使用底下指令:

/usr/bin/spamassassin -W < myletter.eml

 

玖、amavisd-new + clamav

amavisd 用來將病毒過濾功能提供給郵件伺服器,和 SpamAssassin 一樣也是用 perl 開發的,而新版的 amavisd-new 更結合了 SpamAssassin 和許多額外設定,透過 perl Net::Server 模組提供類似 Apache prefork 模式的效能,可以應付 ISP 龐大的郵件處理量。也就是說,使用了 amavisd-new 就不需要再去作前一章提到的 SpamAssassin 相關設定,同時也可以根據自己的需求來制定執行緒的數量。

clamav 是一套免費的病毒過濾引擎,使用者不須註冊就可以透過病毒更新程式自動更新病毒碼,而且病毒碼的維護也很迅速,每天至少會更新一次。clamav 主要用來應付硬碟檔案的掃毒工作,它可以因應硬碟 IO 動作即時監控掃毒,也可以排程進行資料夾掃描,但它卻無法與各種伺服器結合一起運作,這也就是為什麼我們需要 amavisd 的原因。更精確的說,amavisd 只是將現成的掃毒功能提供給郵件伺服器而已,它自己並不會掃毒。

設定 clamav

在這裡我們將只介紹 clamav 的安裝程序,並不打算詳細介紹 clamav 的各種功能和使用方法,有興趣的學員可以自行到 clamav 的官方網站 http://www.clamav.net/ 查看詳細的內容。使用底下指令進行安裝:

yum install clamav*

安裝完成後,可以使用 man clamd 來查看詳細介紹,請使用 ntsysv 或底下指令將 clamav 設定為開機時自動啟動:

chkconfig --level 3,5 clamd on

手動啟動 clamav 的方式和其它的服務一樣:

service clamd start

要更新病毒碼可以使用 freshclam 指令,如果要讓它自動更新,則可以加以下參數(意思是每天檢查更新兩次):

freshclam -d -c 2

freshclam 也是一個 deamon ,因此我們可以使用 ntsysv 或底下指令將它設定為開機時自動啟動:

chkconfig --level 3,5 freshclam on

在網路上可以找到 OpenAntiVirus 發展計畫,該計畫以 GPL 精神來整合 Linux 平台上的各種掃毒引擎,clamav 只是其中之一,其它發展中的計畫還包括:mod_vscan(在 Apache 上掃毒)、squid_vscan(在 Proxy 上掃毒)、amavisd(在 Mail 上掃毒)、pop3_vscan(在 iptables 防火牆上直接掃掉郵件病毒),有興趣的學員不妨去逛一逛。

安裝 amavisd-new

amavisd-new 的原始套件可以在 http://www.ijs.si/software/amavisd/ 取得。這個套件與 perl 模組的依存性很高,安裝異常複雜,但在 Fedora中可以利用 yum 來輕鬆安裝:

yum install amavisd-new

安裝完成後,amavisd 會自動被設定為開機啟動,你可以使用 ntsysv 或 chconfig 指令來修改啟動設定,也可以手動控制:service amavisd start

安裝外掛延伸套件

雖然在前面已經將 amavisd 裝好,也可以運作了,但考量到掃毒成效,某些延伸套件也不得不安裝,舉例來說:有人把中毒的檔案壓縮後再寄出,假如沒有安裝解壓縮引擎,那豈不是掃不到病毒。要知道掃毒是透過病毒特徵的比對,一個被壓縮過的檔案,病毒特徵都消失了,當然就比對不出來!因此要讓 amavisd 發揮最大的掃毒效益,就必須安裝各種解壓縮軟體,底下是各種 Linux 版的解壓縮程式,請點選下列的超連結下載安裝:

壓縮格式 官方網站 下載
file http://sourceforge.net/project/showfiles.php?group_id=16036 RPM
compress RPM
gzip http://www.gzip.org/ RPM
bzip2 http://sources.redhat.com/bzip2/ RPM
nomarch http://rus.members.beeb.net/nomarch.html Tarball
arc RPM
lha http://www2m.meshnet.or.jp/~dolphin/lha/lha-unix.htm RPM
unarj RPM
arj http://www.arjsoft.com/ RPM
rar, unrar http://www.rarsoft.com/ RPM, RPM
zoo http://www.unitedlinux.com/ RPM
cpio http://www.gnu.org/software/cpio RPM
lzop http://www.lzop.org/ RPM
freeze RPM

外掛掃毒引擎

amavisd 支援 21 種掃毒軟體(包含前面介紹過的 clamav),其中大多數都是商業軟體,包括國內知名的趨勢科技和美國的賽門鐵克公司(當然得買 Linux 版,Windows 版的授權並不通用),因此在這裡我們就不介紹了!

修改 amavisd.conf

amavisd 的設定分為底下八個部分,說明如下:

Essential daemon and MTA settings amavisd deamon 的基本設定以及指定使用何種 Mail server,需要手動修改的就是這個部分。
MTA specific SMTP 通訊協定相關設定,使用預設值即可。
Logging 系統日誌設定,使用預設值即可。
Notifications/DSN, BOUNCE/REJECT/DROP/PASS destiny, quarantine 訂定郵件過濾政策,包括:郵件格式設定、警告訊息、中毒和垃圾郵件之處理......等等,請依需要修改,使用預設值也無妨(中毒時只警告不刪除)。
Per-recipient and per-sender handling, whitelisting, etc. 訂定中毒及垃圾郵件黑、白名單管理政策,是否要透過資料庫存取、特定網域或信箱排外處理......等等,不是 ISP 應該用不到,所以我們不去理會它!
Resource limits 系統資源限制,請依據自己的硬體設備等級來調整,原則上盡量調高效能才會好!(但會影響其它服務的效能)。
External programs, virus scanners, SpamAssassin 外掛套件的設定,由於程式會自動偵測,所以使用預設值就可以了!
Debugging 設定 debug 模式啟用時機,預設是不啟用。

在第一個部分一定得設定的有底下這些參數:

$MYHOME = '/var/amavis'; 指定 amavis 服務帳號的家目錄,如果是使用筆者寫的 script 安裝的,這個部分不用修改。
$mydomain = 'syups.tp.edu.tw'; 指定郵件伺服器網域。
$deamon_user = 'vscan'; 指定 amavisd 服務帳號,如果是使用筆者寫的 script 安裝的,這個部分不用修改。
$deamon_group = 'sweep'; 指定 amavisd 服務群組,如果是使用筆者寫的 script 安裝的,這個部分不用修改。
$forward_method = 'smtp:127.0.0.1:10025'; 根據郵件伺服器的不同,須設定呼叫 amavisd 的方式,預設值是給 postfix 用的,如果您的郵件伺服器不是 postfix 請依文件說明修改。
$max_servers = 2; 預設啟動幾個行程。
$max_requests = 10; 預設每個子行程最多服務幾次要求,超過次數該子行程會重新啟動。
$child_timeout = 5*60; 每次過濾郵件不可超過 5 分鐘,如果覺得病毒濾不乾淨,可以延長時間,預設是 8 分鐘。

在第四個部分底下這些參數,可依需要考量設定:

$hdr_encoding = 'iso-8859-1'; 指定標頭使用的字元集,使用預設值即可。因為中文字和其它雙位元字都會編成 quoted-printable code 再寄出,因此沒有相容上的問題,除非是想要啟用 utf8 才須修改。
$bdy_encoding = 'iso-8859-1'; 指定郵件內文使用的字元集,使用預設值即可。
$final_virus_destiny = D_BOUNCE; 設定中毒郵件處理方式,預設值是發出警告信。
$final_banned_destiny = D_BOUNCE; 當郵件夾帶禁止通行的檔案時(例如:exe、com、vbs、scr、pif....等等,稍後可自行修改)要如何處理,預設值是發出警告信。
$final_spam_destiny = D_REJECT; 設定垃圾郵件的處理方式,預設值是拒收(郵件內容將遭到刪除)。
$final_bad_header_destiny = D_PASS; 郵件標頭中含有非 ASCII 字元時,要如何處理?預設值是接受。這是垃圾信常用的招數,例如:收信人為「親愛的客戶@mail.syups.tp.edu.tw」之類的,雖然這些郵件仍然會由 postfix 再次過濾,但最好還是拒收。
當過濾政策設定為 D_PASS 時,可以利用底下參數來訂定要不要寄警告信,預設值是不要。
$warnvirussender = 1; 中毒郵件要不要警告寄信者,其實這個功能還蠻實用,但是會增加伺服器負擔。
$warnspamsender = 1; 垃圾郵件要不要警告寄信者,這個功能沒有用,不須設定。
$warnbannedsender = 1; 郵件夾帶禁止通行的檔案時要不要通知寄信者,其實這個功能還蠻實用,但是會增加伺服器負擔。
$warnbadhsender = 1; 郵件標頭含有非 ASCII 字元時要不要通知寄信者,這個功能沒有用,不須設定。
$warnvirusrecip = 1; 要不要警告收信者他收到的信有毒,這個功能其實沒什麼用,因為不管有沒有收到警告信,outlook express 都會自動打開中毒郵件。
$warnbannedrecip = 1; 要不要警告收信者他收到的附件檔案可能有危險,這個功能其實沒什麼用,理由同上。
$virus_admin = "virusalert\@$mydomain"; 請將管理員信箱改成自己常用的信箱或是透過 alias 對應到常用信箱。當管理員信箱有設定時,管理員會同步收到所有的警告信。
$banned_filename_re = new_RE(
qr'\.[a-zA-Z][a-zA-Z0-9]{0,3}\.(vbs|pif|scr|bat|com|exe|dll)$'i, # double extension
# qr'.\.(exe|vbs|pif|scr|bat|com)$'i, # banned extension - basic
# qr'.\.(ade|adp|bas|bat|chm|cmd|com|cpl|crt|exe|hlp|hta|inf|ins|isp|js|
# jse|lnk|mdb|mde|msc|msi|msp|mst|pcd|pif|reg|scr|sct|shs|shb|vb|
# vbe|vbs|wsc|wsf|wsh)$'ix, # banned extension - long
# qr'^\.(exe|zip|lha|tnef)$'i, # banned file(1) types
# qr'^application/x-msdownload$'i, # banned MIME types
# qr'^message/partial$'i, qr'^message/external-body$'i, # rfc2046
);
上面是用來設定哪些檔案要禁止通行,採用 pcre 的格式來設定,如果你對 perl 程式設計有概念,可以自行修改內容!

設定 postfix

首先在 master.cf 中先定義一個病毒過濾服務,服務名稱可以自己取:

amavis                   unix   -     -     -    -    2     smtp -o smtp_data_done_timeout=1200

這個服務將會執行 smtp client,配合稍後會作的 main.cf 的修改,該 smtp client 將會連往本機(127.0.0.1)的 10024 埠,該埠號已經 binding 給 amavisd。限制行程數為 2 個,這是為了配合 amavisd.conf 中的設定,如果覺得不夠用想修改,不要忘記一併修改 amavisd.conf。

在啟動外掛過濾程式的做法上與前面介紹過的 SpamAssassin 略有不同,後者是透過 pipe 模組呼叫一個外部 client 端程式(spamc)來啟動。而 amavisd 則由於本身並未提供 client 端程式,所以需要由 postfix 的 smtp 模組來代勞。

接著請在 master.cf 加入底下幾行設定:

127.0.0.1:10025    inet    n     -    -    -    -      smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000

這個設定用來啟動一個 smtp deamon,以便配合 amavisd 的過濾動作,郵件從 postfix 透過 smtp client 模組傳送給 amavisd 進行病毒碼比對,比對有結果後 amavisd 會將郵件送往 10025 埠,而這個埠已經定義給 postfix smtp deamon 模組,雖然該模組會讀取 main.cf 的設定,但上面所指定的參數將會取代 main.cf 的設定,仔細看其中 content_filter= 這個參數,就會發現 postfix 將不會再啟動其它的外掛過濾機制。

細心的學員應該會想到,一旦使用了 amavisd 就不會再進行 SapmAssassin 過濾!這也就是為何 amavisd-new 必須支援 SpamAssassin 的原因,當兩個過濾引擎結合為一體時,也就不必去煩惱如何整合的問題。

配合上述的修改,請將 main.cf 中的 content_filter 參數設定為:

content_filter = amavis:[127.0.0.1]:10024

上面這一行的服務名稱須與 master.cf 中的定義匹配,如果有改過服務名稱,不要忘記一併修改。另外埠號也是可以配合 amavisd.conf 來修改,在這裡就不討論了!

還記得在前一章設定 SpamAssassin 時,曾經修改過 master.cf 中的 smtp deamon 服務嗎?這個服務是 binding 在 25 埠的主服務,不同於剛才加入的 10025 埠的 smtp deamon,當時在修改時有指定  content_filter 參數,這將會取代 main.cf 中的參數設定,導致 amavisd 過濾引擎不會被啟動,請將該參數移除:

smtp inet ............中間五個欄位不用修改............. smtpd -o content_filter=spamfilter:

改為:

smtp inet ............中間五個欄位不用修改............. smtpd

當然 spamfilter 服務也不再起作用了,可以一併刪除,不刪除也無所謂!

過濾測試

底下來測試 amavisd 的過濾功能是否有正常運作,測試時我們需要一個帶有病毒的測試檔案,這個檔案可以到 http://www.eicar.org/anti_virus_test_file.htm 取得,檔名就叫 eicar.com(這隻病毒不具有破壞能力),由於筆者的電腦有裝防毒軟體,所以無法下載這個檔案,但我們也可以直接使用病毒特徵來測試,eicar.com 的病毒特徵如下:

 X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

我們先來測試進行郵件濾後處理的 postfix smtp deamon 是否正常:

> $ telnet 127.0.0.1 10025
220 yourhost.example.com ESMTP Postfix
quit //已正常啟動並能接受指令
221 Bye
Connection closed by foreign host.

接著測試 amavisd 的過濾功能:

> $ telnet 127.0.0.1 10024
220 [127.0.0.1] ESMTP amavisd-new service ready
MAIL FROM:<test@example.com> //服務已經啟動,來寄信試試看
250 2.1.0 Sender test@example.com OK
RCPT TO:<postmaster>
250 2.1.5 Recipient postmaster OK
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: test1

test1
.

*** 250 2.6.0 Ok, id=31859-01, from MTA: 250 Ok: queued as 90B7F16F //郵件已被接受,接著寄病毒信試試看

MAIL FROM:<test@example.com>
250 2.1.0 Sender test@example.com OK
RCPT TO:<postmaster>
250 2.1.5 Recipient postmaster OK
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: test2 - virus test pattern //在標頭後面輸入病毒特徵

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
.

*** 550 5.7.1 Message content rejected, id=16968-01 - VIRUS: EICAR-AV-Test //發現病毒,郵件已被攔截
*** 250 2.5.0 Ok, but 1 BOUNCE //發送警告信通知寄信人
*** 250 2.7.1 Ok, discarded, id=16984-01 - VIRUS: EICAR-AV-Test //中斷收信動作
*** 250 2.6.0 Ok, id=17041-01, from MTA: 250 Ok: queued as 3F1841A5F5  //警告信已經排入佇列

QUIT
221 2.0.0 [127.0.0.1] (amavisd) closing transmission channel
Connection closed by foreign host.

 

拾、寄信認證與安全連線

這裡所談的寄信認證就是指 SMTP AUTH,RedHat 所支援的 Cyrus SASL 機制可以透過三種方式進行 SMTP AUTH:

  • pwcheck:直接使用 /etc/shadow 進行帳號認證
  • pam:透過 PAM 模組可以使用 kerbros、LDAP、NIS、Samba、Radius......等認證機制
  • sasldb:使用 SASL 使用者資料庫進行認證

這三種方法中,以第一種方法最方便,因為我們不需要額外維護一個使用者設定檔(這意味著必須重設所有使用者的密碼),也不需要去設定複雜的 PAM 組態。

使用前兩種方法認證必須具備 root 身分才行,但是 postfix 預設是以 $mail_owner 來執行,所以只剩下第三種方法能利用。在這裡我們必須思考一個問題,postfix 之所以不用 root 身分執行是為了避免漏洞被駭客利用,但 postfix 本身已經提供 chroot 牢籠了,也就是說即使被駭客駭掉,也僅僅只能改變 /var/spool/postfix 內的檔案,頂多就是被利用來轉信而已,這樣還需要迴避使用 root 嗎?有關這個問題的答案,由於筆者才疏學淺,不敢給什麼建議!

先來談談第三種認證方式如何使用?

  1. 首先替所有使用者建立 SASL 密碼:saslpasswd -u realm -c user
  2. 所有帳號建好後,將 sasldb 拷貝到 chroot 牢籠中,以免 postfix 讀不到:
    cp /etc/sasldb /var/spool/postfix/etc/sasldb
  3. 接著修改該使用者資料庫的擁有人和權限:
    chgrp postfix /var/spool/postfix/etc/sasldb
    chmod g+r /var/spool/postfix/etc/sasldb

這種做法必須付出龐大的管理成本(例如:加入或移除使用者時),如果把 postfix 改成以 root 身分執行,這些問題就迎刃而解了。要讓 postfix 以 root 身分執行,請修改 master.cf:

smtp    inet    n    n    n    ...........後面欄位不用修改

接著修改 /usr/lib/sasl/smtpd.conf 的內容,將 sasldb 改成 pam,如下:

pwcheck_method: pam

有關 pam 的設定方法,在這裡不討論請自行參考系統文件,跟 SMTP AUTH 有關的設定放在 /etc/pam.d/smtp ,預設值是使用 Linux 系統認證,事實上 sendmail 就是使用這個方法。

啟用 SMTP AUTH

postfix 預設不啟用寄信認證機制,要讓 postfix 啟用 SMTP AUTH,請在 main.cf 中加入:

smtpd_sasl_auth_enable = yes

注意:如果你是使用 sasldb 帳號而非 Linux 帳號,請加入底下參數,此參數用來定義帳號的領域( realm),請與你設定的 sasl 使用者匹配(saslpasswd -u realm -c user):

smtpd_sasl_local_domain = $myhostname

啟用認證功能後,加入底下的過濾規則,將使得通過認證的使用者可以隨意寄信,而不會被過濾規則阻擋,同時其它未使用認證的使用者也能夠繼續寄信(但必須通過過濾):

smtpd_sender_restrictions = permit_sasl_authenticated ......原來設定的參數加在後面......

如果只想讓通過認證的人才能寄信,未通過認證者無法寄信,請設置底下的參數:

smtpd_client_restrictions = permit_sasl_authenticated

在 postfix 中設置此選項會有一些後遺症,假設你的伺服器是對外服務的,也就是說前端並沒有一台 mail gateway 作為白手套,這樣你的郵件伺服器將無法與其他伺服器交換郵件,這是因為這些伺服器並不知道要用什麼帳號密碼來登入你的郵件主機。

如果前端有一台 mail gateway 幫我們收信,然後再轉信給我們的主機,這個時候我們的伺服器一樣會要求 mail gateway 登入,我們可以在 mail gateway 上面設置底下參數,讓它能登入轉信:

smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/saslpass
smtp_sasl_security_options = noanonymous,noplaintext

第一個參數很容易與 smtpd_sasl_auth_enable 混淆,這個參數是用來告訴 postfix 連上別人的郵件主機時,要不要進行登入。第三個參數則與 smtpd_sasl_security_options 相仿,用來定義當入時所使用的密碼機制,請詳見後文的介紹!

這三個參數設置好後,還必須在 mail gateway 上建立 /etc/postfix/saslpass 帳號密碼對照表(底下範例所用的帳號必須先在自己的郵件主機上建立好):

mail.syups.tp.edu.tw    mailhub:password

密碼機制

Cyrus SASL 可以使用多種密碼機制,從最簡單的 PLAIN(純文字密碼)、LOGIN(POP3 密碼,編成 base64)...... 到安全的 DIGEST-MD5、CRAM-MD5,後兩者必須結合 SSL/TLS 安全連線才能使用。postfix 使用何種密碼認證是由 smtpd_sasl_security_options 參數來決定:

noplaintext 關閉純文字密碼認證功能(包含:PLAIN 和 LOGIN)
noactive 防止以暴力法破解密碼
nodictionary 防止以字典法破解密碼
noanonymous 禁止匿名登入
mutual_auth 只允許使用 SASL 2.0 認證方式

postfix 預設禁止匿名登入,但是允許使用純文字密碼,當然我們知道使用 PLAIN 和 LOGIN 一樣都不安全,因為密碼很容易被監聽封包的程式盜取。因此我們建議使用 SSL/TLS 安全連線來進行登入,底下參數將同時允許安全連線及一般連線:

smtp_sasl_security_options = noanonymous
smtpd_use_tls = yes

如果想禁止使用純文字方式登入,請將上面的參數改成像這樣,如果 openSSL 還沒安裝設定好,請不要啟用這個功能:

smtpd_tls_auth_only = yes

啟用安全連線

在 postfix 中啟用安全連線機制,你必須先安裝好 openSSL 套件,postfix 的 smtpd 模組在進行安全連線時會呼叫 Postfix/TLS 這個模組來管理安全通道,這就相當於 sendmail 中的 MSA。要啟用 MSA,請修改 master.cf:

smtps            inet    n       -       y       -       -       smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
submission    inet    n       -       y       -       -       smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes

另外在 MSA 進行連線時,需要使用亂數產生一次性密碼,該亂數可藉由系統亂數裝置 /dev/urandom 產生,如果你的系統找不到此裝置,請設定底下的模組程式來代替:

tlsmgr    fifo    -       -       y       300     1       tlsmgr

安裝憑證

postfix 安全連線必須要有金鑰才能運作,因此利用 openSSL 產生認證所需的金鑰和憑證(放在同一個檔案裡):

cd /etc/postfix
openssl req -new -x509 -nodes -out cert.pem -keyout key.pem -days 3650
chmod 600 *.pem

現在必備的憑證檔案都有了,我們可以設定 mian.cf 讓 postfix 讀取憑證:

smtpd_tls_cert_file = /etc/postfix/cert.pem
smtpd_tls_key_file = /etc/postfix/key.pem

連線測試

我們設定好相關參數後,可以將 postfix 重新啟動並使用 telnet 進行底下連線測試:

[root@linux postfix]# telnet 172.16.11.1 25
Trying 172.16.11.1...
Connected to 172.16.11.1.
Escape character is '^]'.
220 linux.syups.tp.edu.tw ESMTP Postfix
ehlo test.com //由於是從主控台連線,並不會過濾 HELO 命令字串
250-linux.syups.tp.edu.tw
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS //系統支援安全連線
250-AUTH PLAIN LOGIN //系統支援寄信認證功能
250-XVERP
250 8BITMIME

當 main.cf 啟用 smtpd_tls_auth_only = yes 參數時,進行底下測試:

auth plain //測試能否以純文字方式登入
538 Encryption required for requested authentication mechanism
starttls //測試能否開啟 SSL/TLS 連線
220 Ready to start TLS
quit

TOP

發新話題