How To Install and Configure Postfix as a Send-Only SMTP Server on Ubuntu

How To Install and Configure Postfix as a Send-Only SMTP Server on Ubuntu


Introduction


Postfix is an MTA (Mail Transfer Agent), an application used to send and receive email. In this tutorial, we will install and configure Postfix so that it can be used to send emails by local applications only – that is, those applications installed on the same server that Postfix is installed on.

Why would you want to do that?


You installed applications which need to send email notifications.

Prerequisites


You have a valid domain name pointing to your server installed with Postfix.

Steps


  • Install Postfix
  • sudo apt-get install mailutils
    
    Near the end of the installation process, you will be presented with a window " Near the end of the installation process, you will be presented with a window "Postfix Configuration". Choose the recommended option "Internet Site". Then, you will be prompted to input the domain name. Input the domain name which will be used for the server. For ex., urmailer.com.
  • Configure Postfix
  • To send emails only from the server on which it is running, that is, only be the MTA for the local applications. Modify /etc/postfix/main.cf with the following:
    mailbox_size_limit = 0
    recipient_delimiter = +
    inet_interfaces = loopback-only
    
    After that change, restart Postfix by typing:
    sudo service postfix restart
    
  • Test That the SMTP Server Can Send Emails
  • To send a test email, type:
    echo "This is the body of the email" | mail -s "This is the subject line" user@gmail.com
    
    Now check the email address where you sent the test message. You should see the message in your inbox. If not, check your spam folder: To resolve this,
    • register a freeDNS
    • modify /etc/postfix/main.cf
    • # appending .domain is the MUA's job.
      mydomain = tuple.com
      append_dot_mydomain = yes
      
      
    • restart
    • sudo postfix check
         sudo service postfix restart
      
  • To configure Postfix so that system-generated emails will be sent to your external email address
  • You need to modify the /etc/aliases file as the following:
    # See man 5 aliases for format
    postmaster:    root
    root:          xxx@gmail.com
    
    For the change to take effect, run the following command:
    sudo newaliases
    
    You may now test that it works by sending an email to the local root:
    echo "This is the body of the email for root" | mail -s "This is the subject line" root
    
    This will send the mail to root@urmailer.com
  • Protect Your Domain from Spammers
  • This makes it difficult to send spam with an address that appears to originate from your domain.

main.cf 基本設定

postfix 最被人稱道的地方在於設定檔的可讀性很高(當然是跟 sendmail 比),而在主要設定檔 main.cf 中,需要自行定義的東西並不多,而且這些參數就算不去定義,依照預設值也可以運作:
由本機寄出的郵件要使用哪個網域名稱
在 sendmail 中,這個功能稱為網域偽裝,也就是說可以和主機名稱不同,例如:本機名稱為 mail.spps.tp.edu.tw,而寄出的郵件其寄信人欄位則可以使用 spps.tp.edu.tw。(須配合 DNS MX 紀錄使用)
myorigin=spps.tp.edu.tw
不使用簡略名稱時,設定如下:
myorigin=mail.spps.tp.edu.tw
由於主機名稱與網域名稱會被反覆引用在不同設定值,為了簡化設定,通常是定義成變數:
myhostname = mail.spps.tp.edu.tw
mydomain = spps.tp.edu.tw
#myorigin=$mydomain
myorigin=$myhostname (省略時,視同此設定)
當然以上變數也可以省略不定義,這種情況下 postfix 將使用 gethostname( ) 函數取得系統設定。如果是將 myorigin 省略,則視同為設定 $myhostname。
另外,如果本機是某個網域的 mail gateway,也就是說網域內的所有郵件伺服器,都必須把它們的郵件送到本機來處理,這種情形可以使用 masquerade_domains 讓 postfix 自動缺省寄信人欄位中的郵件主機名稱,例如:
masquerade_domains = $mydomain
假設 $mydomain 是 spps.tp.edu.tw,則無論信件是來自 stu.spps.tp.edu.tw 或 mail.spps.tp.edu.tw 都將會被改寫成 spps.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.spps.tp.edu.tw
#relayhost = [172.16.1.7]
哪些情況須通知管理員
所謂管理員是指 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)。
郵件標頭過濾
標頭過濾所過濾的對象,除了郵件標頭外,更擴大範圍到附加檔案的 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 通訊協定連上伺服器提出寄信請求時,針對用戶端輸入的指令進行過濾。在 Linux 系管師進階班講義 中已經詳細論及在 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/accessreject(採用白名單政策)
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 查看或閱讀 /usr/share/doc/postfix-1.xx/README_FILES/FILTER_README!
同步處理限制
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 = 1000sqmgr 模組每 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 = 1000dead 清單的大小,也就是說第 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 = 60sqmgr 每隔 60 秒嘗試驅動 agent 進行分信。

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

當 cleanup 模組進行郵件過濾之前,會先依據各種設定檔呼叫 rewrite 模組,進行郵件地址改寫,這樣做的好處是可以減少標頭欄位的變化,使得過濾快取(請參考 duplicate_filter_limit 的說明)能更有效率的工作。
郵件地址改寫的順序如下:
將 @hosta,@hostb:user@site 改寫成 user@sitepostfix 不支援前面那種特殊寫法
將 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 是 sendmail 最被廣泛使用的一項功能,除了用來對應虛擬信箱外,也可以運用在郵遞清單或郵件討論群組上面,由於 postfix 在這部分完全與 sendmail 相容,因此我們就不再討論它,詳細應用方法請參考 Linux 系管師進階班講義 一文。
請在 main.cf 中加入這一行,以便啟用 alias:
alias_maps = hash:/etc/aliases
如果您的系統僅支援 dbm 格式的別名,或是透過 NIS 伺服器查詢別名,請使用底下的參數:
alias_maps = dbm:/etc/aliases, nis:mail.aliases
全名對應
這個設定檔用來建立使用者全名與帳號的關係,例如:
shane@mail.spps.tp.edu.tw    shane.lee@mail.spps.tp.edu.tw
如果網域名稱,已經設定在 $myorigin、$mydestination 或 $inet_interfaces 中就可以省略不寫,直接使用底下的語法:
shane    shane.lee
此外這個設定檔還有一個用途,就是當網域遷移時,可以將信件對應到新的網域名稱,例如:
@mail.spps.tp.edu.tw    @tp.edu.tw
當本機是 mail gateway 時,透過底下的設定可以得知哪些使用者是位於哪一台郵件主機上,分信時會分給該主機,而非分到本機的 /var/spool/mail 中:
shane    @stu.spps.tp.edu.tw(帳號相同時)
shane    s60101@stu.spps.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 設定檔的應用範例,意思是要將寄給 @spps.tp.edu.tw 網域的信件,傳送到 172.16.1.6 這台主機的 8025 port:
spps.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 作業系統時沒有一併安裝,事後要補裝有一點小麻煩,安裝所需的套件如下(省略版次號碼,請自行按 TAB 補字,依順序安裝),這些 RPM 檔案可以從 RedHat 光碟取得:
perl
perl-Time-HiRes
perl-Digest-HMAC
perl-Net-DNS
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.spps.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/ 查看詳細的內容。首先請按這裡下載 clamav 的 RPM,使用底下指令進行安裝:
rpm -Uvh 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 模組的依存性很高,安裝異常複雜,開發者也未提供自動安裝程序,僅有一份簡要說明,使用開發者建議的方式裝,會因為 CPAN.pm 模組與特定模組有相依性的問題,導致某些特定模組安裝失敗,即使能成功安裝完成,也須自行修改 amavisd 原始程式碼,同時也無法將執行程序自動化,為了避免麻煩,請按照底下筆者的建議來安裝,安裝方式如下:
  1. 安裝 amavisd 之前,請用底下指令先安裝 CPAN:
    perl -MCPAN -e 'install CPAN';
    安裝 cpan 時,會問一大堆問題,通通按 Enter 就可以了,一直到出現底下問題:
    (1) Africa
    (2) Asia
    (3) Central America
    (4) Europe
    (5) North America
    (6) Oceania
    (7) South America
    Select your continent (or several nearby continents) [] 問所在地區,請選 2
    (1) China
    (2) Indonesia
    (3) Israel
    (4) Japan
    (5) Malaysia
    (6) Philippines
    (7) Republic of Korea
    (8) Russian Federation
    (9) Saudi Arabia
    (10) Singapore
    (11) Taiwan
    (12) Thailand
    Select your country (or several nearby countries) [] 問所在國家,請選 11
    (1) ftp://cpan.cdpa.nsysu.edu.tw/pub/CPAN
    (2) ftp://ftp.isu.edu.tw/pub/CPAN
    (3) ftp://ftp.nctu.edu.tw/UNIX/perl/CPAN
    (4) ftp://ftp.tku.edu.tw/pub/CPAN/
    (5) ftp://ftp1.sinica.edu.tw/pub1/perl/CPAN/
    Select as many URLs as you like,
    put them on one line, separated by blanks [] 問最近的映射站台,請選 5(中研院)
    剩下的其餘問題全部按 Enter 即可
  2. CPAN 裝好後,請先將剛才下載回來的 amavisd tarball 解壓縮:tar xzvf amavisd-new-
  3. 切換目錄到解開的資料夾:cd amavisd-new*
  4. 測試所需套件是否已經安裝:./amavisd debug
  5. 回應訊息如下(由於每台機器已安裝的 perl 模組不一樣多,因此底下訊息僅供參考):
    ERROR: MISSING REQUIRED BASIC MODULES:
    Time::HiRes
    IO::Wrap
    IO::Stringy
    Unix::Syslog
    Mail::Field
    Mail::Address
    Mail::Header
    Mail::Internet
    MIME::Words
    MIME::Head
    MIME::Body
    MIME::Entity
    MIME::Parser
    Net::Server
    Net::Server::PreForkSimple
    MIME::Decoder::Base64
    MIME::Decoder::Binary
    MIME::Decoder::Gzip64
    MIME::Decoder::NBit
    MIME::Decoder::QuotedPrint
    MIME::Decoder::UU
    BEGIN failed--compilation aborted at ./amavisd line 123.
  6. 使用 cpan 指令安裝缺少的模組,例如:
    cpan Time::HiRes
  7. 由於某些模組可能被包在一起,安裝時會一併裝好,所以每安裝好一個就應該回頭來檢視一下,還剩哪些模組需要安裝,請再一次執行:./amavisd debug
  8. 反覆步驟 6~7 一直到出現其它錯誤訊息為止(假如很不幸剛好有的話!)例如:
    Can't locate Convert/TNEF.pm in @INC (@INC contains: /usr/lib/perl5/5.8.0/i386.........(略)
    BEGIN failed--compilation aborted at ./amavisd line 3525.
  9. 這也是相依套件沒裝好造成的(amavisd debug 模式沒發現此問題,而是由 perl 編譯器發現的),處理方式與前面相仿,只是要特別注意錯誤訊息中的 Convert/TNEF.pm 檔案,在進行安裝時要寫成:
    cpan Convert::TNEF
  10. 反覆執行 ./amavisd debug 一直到出現底下錯誤訊息為止
    find_program_path: relative paths not implemented: ./amavisd
  11. perl 模組的安裝到此已經完成,接下來就是建立服務帳號及相關目錄,這部分的工作可以透過筆者自行開發的安裝程式來作,請按這裡下載 install.sh,將該程式放在解開的 amavisd-new* 資料夾中並執行:
    chmod 755 install.sh
    ./install.sh
  12. 安裝完成後,amavisd 會自動被設定為開機啟動,你可以使用 ntsysv 或 chconfig 指令來修改啟動設定,也可以手動控制:service amavisd start
安裝外掛延伸套件
雖然在前面已經將 amavisd 裝好,也可以運作了,但考量到掃毒成效,某些延伸套件也不得不安裝,舉例來說:有人把中毒的檔案壓縮後再寄出,假如沒有安裝解壓縮引擎,那豈不是掃不到病毒。要知道掃毒是透過病毒特徵的比對,一個被壓縮過的檔案,病毒特徵都消失了,當然就比對不出來!因此要讓 amavisd 發揮最大的掃毒效益,就必須安裝各種解壓縮軟體,底下是各種 Linux 版的解壓縮程式(非 RH 9.0 的使用者請下載 Tarball 安裝),你可以連到 ftp://ftp.spps.tp.edu.tw/linux/compress/ 下載,或點選下列的超連結:
壓縮格式官方網站下載
filehttp://sourceforge.net/project/showfiles.php?group_id=16036RPM
compressRPM
gziphttp://www.gzip.org/RPM
bzip2http://sources.redhat.com/bzip2/RPM
nomarchhttp://rus.members.beeb.net/nomarch.htmlTarball
arcRPM
lhahttp://www2m.meshnet.or.jp/~dolphin/lha/lha-unix.htmRPM
unarjRPM
arjhttp://www.arjsoft.com/RPM
rar, unrarhttp://www.rarsoft.com/RPMRPM
zoohttp://www.unitedlinux.com/RPM
cpiohttp://www.gnu.org/software/cpioRPM
lzophttp://www.lzop.org/RPM
freezeRPM
除了檔案壓縮部分的外掛套件外,如果希望讓 amavisd 幫我們管理 SpamAssassin,請使用 cpan 指令安裝該套件的 perl 模組,指令如下:
cpan Mail::SpamAssassin
如果想要利用 SQL 資料庫來存取中毒或垃圾郵件的黑、白名單,還必須安裝 DBD::DBI,這並不是一個模組,而是一群 perl 模組,你必須根據你使用的資料庫系統來挑選安裝,例如:mysql:
cpan DBD::mysql
如果要針對不同的使用者帳號進行不同的過濾規則設定,而使用者帳號是透過 LDAP 提供,你還必須安裝底下的模組:
cpan Net::LDAP
當然以上談到的三個外掛套件: SpamAssassin、mysql、openldap,也必須安裝才行,並不是只裝 perl 模組就可以運作,由於以上三個功能是針對 ISP 的需求而開發,校園內應該還用不上,所以不去理會也就算了!
外掛掃毒引擎
amavisd 支援 21 種掃毒軟體(包含前面介紹過的 clamav),其中大多數都是商業軟體,包括國內知名的趨勢科技和美國的賽門鐵克公司(當然得買 Linux 版,Windows 版的授權並不通用),因此在這裡我們就不介紹了!
修改 amavisd.conf
amavisd 的設定分為底下八個部分,說明如下:
Essential daemon and MTA settingsamavisd deamon 的基本設定以及指定使用何種 Mail server,需要手動修改的就是這個部分。
MTA specificSMTP 通訊協定相關設定,使用預設值即可。
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 = 'spps.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.spps.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: //服務已經啟動,來寄信試試看
250 2.1.0 Sender test@example.com OK
RCPT TO:
250 2.1.5 Recipient postmaster OK
DATA
354 End data with .
Subject: test1

test1
.

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

MAIL FROM:
250 2.1.0 Sender test@example.com OK
RCPT TO:
250 2.1.5 Recipient postmaster OK
DATA
354 End data with .
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.spps.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 /usr/share/ssl/certs
make /etc/postfix/server.pem
現在必備的憑證檔案都有了,我們可以設定 mian.cf 讓 postfix 讀取憑證:
smtpd_tls_cert_file = /etc/postfix/server.pem
smtpd_tls_key_file = $smtpd_tls_cert_file
連線測試
我們設定好相關參數後,可以將 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.spps.tp.edu.tw ESMTP Postfix
ehlo test.com //由於是從主控台連線,並不會過濾 HELO 命令字串
250-linux.spps.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

留言

熱門文章