本文上篇先從單機 Mail Server 收發郵件開始談起,進而提到使用客戶端收信相關的設定與調整,以及郵件安全相關的議題。
本文中篇接著從 Mail Server 之間遞送信件開始介紹,以及 sendmail 設定、郵件 RELAY 概念等等。
我們將會從A郵件主機傳送信件給B郵件主機,需求及測試環境介紹如下:
範例中B主機 IP 為 172.18.0.108(承接上一期的相關資訊)。
B主機將會設定成接收郵件地址為 @foo.com.tw 結尾的信件,例如接收寄給 foo@foo.com.tw 的信件。
A主機將會寄送 @foo.com.tw 結尾的測試信件,例如寄給 foo@foo.com.tw 的信件。
上述信件最終應該要被B主機收下來,接著派送到 foo 收件匣(/var/spool/mail/foo)。
要完成上述的測試最好需具備設定與測試 DNS Client / Server 基本能力。
底下針對有可能阻礙信件傳遞的錯誤訊息以及相關處理方法來做說明。
我們在A主機使用指令「mail foo@foo.com.tw」寄信給 foo@foo.com.tw 時,不久後會收到退信,寄信的動作在上一集已經介紹過,示範如下圖:

收到退信內容寫著:
(reason: 550 Host unknown) 550 5.1.2 <foo@foo.com.tw>;... Host unknown (Name server: foo.com.tw: host not found)
主要是因為 foo.com.tw 沒有 MX 紀錄也沒有 A 紀錄的關係。

郵件傳遞優先使用 MX 紀錄來找出目標主機,假如我們要寄給 foo@gmail.com 則是優先查出 gmail.com 的 MX 紀錄,這 MX 紀錄後面會有一個數字,然後接著有個主機名稱,這個主機名稱解析出來的 IP 才是我們送信出去的目標主機。
使用指令「dig gmail.com mx」查出 gmail 有五筆 MX 紀錄,MX 紀錄後面的數字以較小的優先存取,其中以 gmail-smtp-in.l.google.com 數字最小(數字是5)如下圖:

這個 gmail-smtp-in.l.google.com 解析出來的 IP 是 209.85.201.114 才是我們送信出去的目標主機。
有了以上的觀念以後,當A主機寄信給 foo@foo.com.tw 時,我們希望「dig foo.com.tw mx」能夠正確的解析,例如:
『foo.com.tw. 86400 IN MX 10 mail.foo.com.tw.』
『mail.foo.com.tw. 86400 IN A 172.18.0.108』(172.18.0.108 是B主機 IP)
為簡化測試環境,筆者讓A、B這兩台主機都詢問同一台名稱解析伺服器,接著要能提供上述的解析,此伺服器需管理 foo.com.tw 這個 zone 並編寫上述相關紀錄,Linux DNS Server 架設筆者曾經介紹過,故簡介如下:
編寫 /etc/named.conf 加入
zone "foo.com.tw" in {
type master;
file "foo.com.tw.db";
};
管理 foo.com.tw 這個 zone 如下圖:

編寫 /var/named/foo.com.tw.db 檔案內容
$TTL 1D
@ IN SOA ns1 root 2009011411 43200 21600 3600000 86400
IN NS ns1
IN MX 10 mail
mail IN A 172.18.0.108
ns1 IN A 172.18.0.231
重點在於 mail 相關的那兩行,其中第一行開頭的空格要記得空,這樣才代表同樣是上一行的開頭,也就是上上一行的 @ 代表這個 zone 為 foo.com.tw.,這行的意思是 foo.com.tw. 有個 MX 紀錄,此 MX 紀錄為數字 10 指到 mail 這個名稱;接著第二行的 mail 有個 A 紀錄指到B主機的 IP 是 172.18.0.108。

重新啟動 named 後,就可以在A主機使用「dig foo.com.tw mx」查到我們希望查到的結果(也就是下述這兩筆紀錄)如下圖:
『foo.com.tw. 86400 IN MX 10 mail.foo.com.tw.』
『mail.foo.com.tw. 86400 IN A 172.18.0.108』(172.18.0.108 是B主機 IP)

處理好名稱解析後,於A主機再度寄信給 foo@foo.com.tw 後,這次不會收到退信,且B主機也沒收到信件,這時候信件是佇列(Queue)在A主機這邊,使用指令「mailq」觀察到原因是『Deferred: mail.foo.com.tw.: No route to host』如下圖。

這時候應該是被B主機防火牆擋到的關係,在B主機使用指令「system-config-securitylevel」開啟 Mail(SMTP) 即可。

處理好防火牆之後,指令「telnet mail.foo.com.tw 25」得到訊息已由原先『No route to host』成為『Connection refused』。
下圖為處理B主機防火牆之前與之後,在A主機執行「telnet mail.foo.com.tw 25」的情況。

至於寄測試信的狀況也類似,在「mailq」中看到『(Deferred: Connection refused by mail.foo.com.tw.)』如下圖。

此時信件應該已經要送往B主機但拒絕連線,所以我們來到B主機觀察 sendmail 運作情況:
指令「chkconfig --list sendmail」觀察出 sendmail 開機預設是『啟動』(on)。
指令「/etc/init.d/sendmail status」觀察出 sendmail 現在是正在跑的狀態(running)。
在 Process 裡面也是有看到 sendmail 的蹤影,指令「ps ax | grep sendmail」訊息『sendmail: accepting connections』。

重點來了!指令「netstat -na | grep -E -w '25|Foreign'」觀察到聆聽的 Local Address 欄位值是 127.0.0.1:25,這樣的結果說明了只有在B主機上自己存取自己 IP 為 127.0.0.1 的 25 埠會通,其他來自於外部主機存取這台的 25 埠則不通(Connection refused)。
Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN

要使得B主機能夠接收外來的信件,簡單說就是要把上述『127.0.0.1:25』處理成『0.0.0.0:25』即可,這樣就可以被其他外部主機存取這台的 25 埠,要完成這項設定,建議要先學會 sendmail 較為特殊的設定方式。
sendmail 設定之所以特殊是因為他不去直接修改設定檔案(檔名 sendmail.cf)反而是先編輯 mc 檔(檔名 sendmail.mc)再使用 m4 程式依照 sendmail.mc 的設定產生出 sendmail.cf 檔案。
所幸的是在近年的 Linux(當然也包括 RHEL5)已經有做好配套措施,讓我們不必記得 m4 指令要如何下也不需記得搭配哪些參數,取而代之的是 make 這個指令,依照系統提供的 Makefile 去執行相關指令,用以簡化設定難度。
接著就以將『127.0.0.1 改成 0.0.0.0』為例來做示範:
切換目錄到 /etc/mail(指令「cd /etc/mail」)
編輯 sendmail.mc 檔案內容,找到下述這行
DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl
改成這樣(主要是將 127.0.0.1 改成 0.0.0.0)
DAEMON_OPTIONS(`Port=smtp,Addr=0.0.0.0, Name=MTA')dnl
沒有意外的話,改好後執行 make 指令這樣就會產生 sendmail.cf 檔案。
在執行 make 指令之後,若訊息警告要利用 sendmail.mc 產生 sendmail.cf 還缺少 sendmail-cf 套件的話,使用指令「yum install -y sendmail-cf」補裝套件即可,提示的訊息如下:
WARNING: 'sendmail.mc' is modified. Please install package sendmail-cf to update your configuration.

補裝完成 sendmail-cf 套件後,二度執行 make 就不會有警告訊息,接著在執行「make restart」的前後,使用先前介紹的 netstat 指令觀察到已經由先前 127.0.0.1:25 改變成後來 0.0.0.0:25。

如果有仔細觀察 /etc/mail/sendmail.cf 檔案的修改時間(使用 ls -l 指令)就可以發現他是在我們執行 make 時被修改的。
搞定了將 sendmail 設定成會聆聽所有介面後,回到A主機再度寄信測試,這次得到的訊息是
(reason: 553 5.3.5 system config error) 553 5.3.5 mail.foo.com.tw. config error: mail loops back to me (MX problem?) 554 5.3.5 Local configuration error

造成這個訊息以測試環境來說有兩個狀況:
情況一是由A主機發測試信給B主機,但『兩台主機擁有同樣的主機名稱』所造成。
情況二則是直接在B主機發測試信給自己,此時已經避開『兩台主機擁有同樣的主機名稱』問題,真正的問題在於B主機並不覺得這封信件應該由自己主機收下來。
針對以上兩個狀況我們分開說明。
RedHat 系列的 Linux 預設主機名稱是「localhost.localdomain」,也因此兩台測試主機都是叫做這個名字,我們選擇先改其中一台的主機名稱使之錯開,就可以繼續測試下去,在此我們選擇修改A主機的主機名稱成『mail.foo2.com.tw』,改主機名稱的操作就當作複習吧!
RedHat 系列的 Linux 改主機名稱是修改「/etc/sysconfig/network」以及「/etc/hosts」檔案內容,改完後要記得重新開機(指令 reboot)使之生效(或是把所有運作中的 Process 都重新啟動)。
編輯 /etc/sysconfig/network 由原先
HOSTNAME=localhost.localdomain
改成
HOSTNAME=mail.foo2.com.tw
編輯 /etc/hosts 由原先
127.0.0.1 localhost.localdomain localhost
『增加』成
127.0.0.1 mail.foo2.com.tw mail localhost.localdomain localhost

改完主機名稱再次寄送測試信時,出現『reason: 550 5.7.1 <foo@foo.com.tw>... Relaying denied. IP name lookup failed [172.18.0.191]』訊息,重點在於 Relaying denied(拒絕轉遞)以及何謂 RELAY 這個部份,解說如下:

A主機將信件依照流程送到了B主機,B主機按照我們的需求『應該』要將信件視為這台郵件主機的且收下來才對,但是在這個『應該』做的動作還沒調整之前,B主機會覺得這封信件不是給他的而將要把他轉出去,這個幫忙轉遞信件的動作,就是 RELAY,而預設郵件主機僅允許 RELAY 來自 127.0.0.1 的信件,至於來自於外部 IP 的轉遞信件預設不允許 RELAY 的(例如由B主機發送過來的信件)。
預設 RELAY 都不開放給外部主機主要是因為垃圾信(廣告信、釣魚信)猖獗的關係,如果主機遇到被當成廣告信件(釣魚信件)發送的跳板時,很有可能就是 RELAY 設定不當所造成。關於 RELAY 的部份,此處先行介紹觀念,設定的部份則留到後續再做完整的介紹。
要讓B主機認為 @foo.com.tw 結為是他的信件,需設定B主機的 /etc/mail/local-host-names 加入 foo.com.tw 即可,設定好記得重新啟動 sendmail 使之生效。

由A主機寄給B主機的 foo@foo.com.tw 信件終於傳到B主機 foo 收信匣 /var/spool/mail/foo!感覺如何呢?事實上整個郵件遞送的流程是固定且有邏輯的,照這樣來看要查出一封寄不到對方信箱的信件似乎並不難,而要查出寄出去卻石沉大海的信件也應該不難才對,其實困難的部份在於能否取得遞送之間所有的郵件主機相關紀錄。
若是發生由A公司寄給B公司信件不通或是消失不見,建議兩方都需協助調查結果才會圓滿,例如提供相關的那幾行 maillog(sendmail 在 /var/log/ 目錄)給雙方互相檢視也是一種處理方法,筆者在此主要是將重點放在郵件傳遞不通時的問題處理與故障排除、郵件遞送之間的觀念釐清等等。
上述我們都是使用指令直接在 Linux 主機上發送信件,實務上的一般用戶是開啟圖形郵件軟體(例如:Outlook)來收發信件,這個部份的解說將會在下集介紹。