從零開始學習郵件系統搭建(一)——正經發出一封郵件

前言
??出于工作原因,需要學習搭建郵件系統,為避免日后生疏最后忘了,這里會記錄我整個學習的過程,包括中途遇到的不解之處,當然也會有一個完整的搭建成品的步驟。
??網上可以搜到不少關于搭建郵件系統的文章,質量參差不齊,我本人更希望自己能了解到更多,而非照著步驟啪啪啪搭起來就算了,以后遇到問題能比較有效率地尋找解決方案。
??我一邊學一邊寫,會有理解錯的,后面發現之后會回頭修正。歡迎留言指教。

涉及到概念(ps.直接看沒意思,先往后看,發現不明白的英文名詞再回來這里查一下吧

名詞 解釋
MUA (Mail User Agent)用戶郵件代理,用于接收郵件。如foxmail。
MTA (Mail Transfer Agent)郵件傳輸代理,用于接受發郵件請求并完成轉發動作。如postfix。
MDA (Mail Delivery Agent)郵件分發代理,用于將收到的郵件放進收件人信箱中,具有過濾郵件、自動回復等功能。主要的MTA程序都有自己的MDA功能,也有比較強大的第三方MDA,如procmail等。
MRA (Mail Retrieval Agent)郵件取回代理,提供POP3/IMAP協議,供MUA將郵件取出。
POP3 (Post Office Protocol version 3)郵局協議第三版。對于MUA拉取郵件到本地的動作,可以設置拉取完后刪除拉取后不刪除兩種方式,對郵件得下載到本地后才能進行管理。
IMAP (Internet Message Access Protocol)網絡信息訪問協議。相比POP3,可以遠程訪問郵件服務器管理郵件,不一定非得先下載到本地。
SMTP (Simple Mail Transfer Protocol)簡單郵件傳輸協議。MTA就是SMTP的一個實現,端口號通常是25。SMTP協議很早就出現,比HTTP還早,本身沒有對發送方的身份驗證,所以后來出現了SPF、DKIM、DMARC等東西來彌補。
Postfix 一個開源的MTA程序,負責通過SMTP協議發送郵件。是作者為改善sendmail開發的,如今很流行。
Cyrus SASL Cyrus Simple Authentication and Security Layer的縮寫,是一個輔助的程序,針對SMTP的認證,它提供了saslauthd來進行賬號密碼的比對。同時,它還支持兩個auxprop插件:sasldb,sql。反正就是提供不同方式的身份驗證。后面會學到。
Dovecot 一個開源的MRA程序,按照POP3/IMAP協議提供郵件取回服務,同時,它支持對用戶的身份進行驗證,貌似也用來協助SMTP服務器做身份驗證,而且可以做到比cyrus sasl更好?
MX 郵件交換記錄。發郵件時,會根據收件人的地址后綴去DNS服務器查到MX記錄,從而定位收件地址所在的服務器。
SPF (Sender Policy Framework)是為了防范垃圾郵件而提出來的一種DNS記錄類型。用于登記某個域名擁有的用來外發郵件的所有IP地址。收方郵件服務器可能會根據發方的域名向DNS服務器索取發方域名的SPF記錄,跟發方的IP做匹配,如果不能匹配上,那么說明這封郵件并非由真正的發方域名的服務器發出,可能會判為垃圾郵件。
PTR 反向域名解析。使得可以通過發方的IP地址反查到域名,也是判斷發件人是否正常的手段。

名詞的解釋暫時到這可好。。另外還有那些DKIM之類,后面要用到的時候再說。

郵件系統工作原理圖(ps.看這些圖覺得煩,不妨先往后看吧,想要知道收發郵件的背后機制時再來這看

在簡書里的這篇文章從零開始郵件服務器搭建給出了幾個流程圖,特別好地描述了郵件系統的工作原理,推薦過去閱讀,有對流程圖的詳細講解。我在下面僅把圖貼過來(如果原作者不愿我把圖貼過來,可以留言,我刪掉便是),方便查看。

郵件系統架構
郵件服務器接收郵件
用戶查收郵件過程
用戶發送郵件過程

我的工作環境

服務器是阿里云上的ECS,操作系統是Centos 6.8 (64位)。下面一步步地,是從一個全新的由公共鏡像創建的ECS開始,慢慢部署,琢磨。
首先,為了好看些,vim /etc/bashrc,末尾添加如下代碼:

#color

use_color=true

if ${use_color}; then
    if [[ ${EUID} == 0 ]]; then
        PS1='\[\033[01;31m\]\h\[\033[01;34m\] \W \$\[\033[00m\] '
    else
        PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
    fi
else
    if [[ ${EUID} == 0 ]]; then
        PS1='\u@\h \W \$ '
    else
        PS1='\u@\h \w \$ '
    fi
fi

alias ll='ls -la'
alias vi='vim'
alias grep='grep --color=auto'

然后,修改hostname,比原先那樣好看些吧。雖說Postfix會用到hostname,但因為可以在main.cf(如果你不知道這個,可以先不管,后面會說)里設置,所以,在這里修改hostname,我能想到的好處,就是好看。

vim /etc/sysconfig/network;
        HOSTNAME=mail.howard.org  #修改HOSTNAME
vim /etc/hosts;
        127.0.0.1 mail.howard.org #hosts對hostname本身關系不大,增加一個mapping,便于有些程序對hostsname的dns解析
hostname mail.howard.org;  #上面的修改需要重啟主機才會生效,這里不想重啟,就直接改內存里的吧。需要退出當前會話重新進來才可以看到生效

hostname要符合FQDN,可以自行百度。比如我這臺主機是郵件服務器,hostname寫成mail.howard.org,屬于全稱域名,domain就是howard.org,主機名是mail,表示該主機在域名樹的位置。

安裝一些軟件

說明:下面安裝的軟件并非全是必要的,為了后面的學習,先裝好了。

  1. 安裝postfix:yum install postfix。我執行后發現是升級原有的版本。設置開機啟動chkconfig postfix on。
  2. 刪除原有的sendmail:rpm -e sendmail or yum remove sendmail。本ECS沒安裝sendmail。
  3. 安裝Cyrus SASL相關的軟件:yum install cyrus-sasl cyrus-sasl-plain cyrus-sasl-md5 cyrus-sase-sql。
  4. 安裝dovecot:yum install dovecot。我安裝的版本是1:2.0.9-22.el6。
  5. 安裝MySQL:
    由于ECS默認的源里的MySQL版本是5.1,所以下面先改一下源:
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm;
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm;
rpm -ivh *.rpm;
vim /etc/yum.repos.d/remi.repo;
        [remi] enabled=1 #設置remi下面的enabled為1
yum install mysql-server; #現在安裝的就是MySQL 5.5版本了
/etc/init.d/mysqld start; #啟動MySQL
chkconfig mysqld on; #開機自啟動
mysql -u root -p; #看到等待輸入密碼時,直接按回車,因為是全新安裝的,所以無密碼。進入后是mysql的控制臺
mysql>drop database test; #刪除test庫
mysql>use mysql; #切換至mysql庫
mysql>delete from user where user=''; #刪除匿名賬戶
mysql>update user set password=PASSWORD('123456') where user='root';#設置root密碼
mysql>grant all privileges on *.* to root@'%' identified by '123456';#設置允許遠程訪問(為方便才這樣設的,你懂的)
mysql>flush privileges;
mysql>exit

配置防火墻

對于阿里云主機,用安全組也是不錯的選擇。下面是配置iptables的方法:

ll /etc/sysconfig/iptables; #本ECS默認沒有配置iptables,所以這里會報找不到文件
iptables-save > /etc/sysconfig/iptables; #生成一個空的iptables配置文件
#在 :OUTPUT ACCEPT [107:11768] 跟 COMMIT 兩行之間插入下面這些內容
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 993 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 995 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
#上面的意思大概就是,只對tcp連接開放22、3306、25、993、995端口,其他都封禁。
/etc/init.d/iptables restart

下面先讓服務器可以正常發出一封郵件

先試試現在能不能發送了(ps.簡直迫不及待啊):
  1. 啟動postfix:/etc/init.d/postfix start;
  2. netstat -tlnp |grep 25;可以看到
  3. yum install telnet; 我發現ECS還沒安裝telnet。
  4. 用telnet跟postfix交互(不妨tail -f /var/log/maillog看輸出什么log,有時候很有用)
  5. 檢查一下收件箱,收到啦!雖然是在垃圾箱里。是的,只需要postfix就可以完成發郵件的事情了!不過我們也發現了兩個問題,一個是這個過程沒有要求驗證身份;第二是在/var/log/maillog里輸出不少warning。下面就來做一些配置,讓發郵件這個任務完成得更好些。
修改默認郵件傳輸代理(MTA)

輸入alternatives --display mta;看第一行,發現提示的第一行是mta - 狀態是自動。現在我們把它改成手動的強制指向postfix:alternatives --config mta,根據提示,選擇sendmail.postfix。這個時候再輸入alternatives --display mta;,現在可以看到第一行是mta - 狀態是手工。

配置postfix(/etc/postfix/main.cf)

關于postfix的歷史以及/etc/postfix目錄下各個文件的意義,請移步看鳥哥的文章鳥哥的Linux私房菜(第二十二章)。這里備注一下修改main.cf需要注意的地方:

  • #符號是注釋符
  • 給變量賦值的寫法,要注意=號的兩邊得留空格。而這一行的開頭不能有空白字符。
    如:myhostname = xxx.xxx.com
  • 可以使用$符號來延伸變量的使用,如:myorigin = $myhostname
  • 如果要給一個變量賦值多個值,值的寫法建議用逗號+空格來隔開。
    如:mydestination = $myhostname, $mydomain, xxx.xxx.com
  • 可以使用多行來表示同一個設定值,只要前一行末尾有逗號,下一行開頭有空白字符就行。所以前面說第一行的開頭處不要有空白字符。
  • 如果對同一個設定項重復做了配置,以后面寫的配置為準。
  1. 好習慣:cd /etc/postfix; cp main.cf main.cf.bk;
  2. vim main.cfps.如果沒能搜到,就在文件末尾添加即可。下面所有的設定項可以通過man 5 postconf查看其意思。
      #主機名;會被后面很多設定項引用,務必使用FQDN,即完整主機名
myhostname = mail.howard.org
      #域名;一般是主機名去掉第一個.前面那截剩下的,后面也被很多設定項引用
mydomain = howard.org
      #發信源主機;對應郵件標頭上的“mail from”;默認是$myhostname,如果多臺主機使用同一個domain,那么就設成$mydomain
myorigin = $mydomain
      #postfix的監聽接口(極重要);默認只開放給本機localhost,如果要監聽整個internet的話,設為all
inet_interfaces = all
      #postfix的監聽IP協議;默認是all,即同時監聽IPv4和IPv6,但如果服務器的網絡只支持IPv4,那就設為ipv4
inet_protocols = ipv4
      #能收信的主機名(極重要);DNS里的MX指向的主機名也要寫到這里來
      #如果是郵件域網關,要加上$mydomain,其他smtp節點就不用加$mydomain
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
      #約定信任的用戶端(極重要);規定了本MTA可以為哪些用戶端進行Relay,就是轉發了
      #千萬不要open relay,會讓本MTA的IP進入各種黑名單
      #鳥哥建議下面這種寫法,使用hash:/etc/postfix/access,就是個外部的哈希庫,可以參考鳥哥的文章??了解詳情
mynetworks = 127.0.0.0/8, hash:/etc/postfix/access
      #默認收件箱在/var/spool/mail/user,所有的郵件存放在同一個文件中
      #改成Maildir/,收件會存儲在/home/user/Maildir/下,每封郵件是一個文件
      #如果改成Mailbox,則收件會存儲在/home/user/Mailbox中,都存在同一個文件里
home_mailbox = Maildir/
      #在SMTP服務器的歡迎標語上顯示軟件版本。Postfix本身對這個不關心
smtpd_banner = $myhostname ESMTP
      #郵件最大尺寸,單位:字節
message_size_limit = 52428800
      #規定收件箱最大容量,單位:字節
mailbox_size_limit = 1073741824
  1. 由于mynetworks設置了hash:/etc/postfix/access,所以需要執行postmap hash:/etc/postfix/access來生成對應的哈希庫/etc/postfix/access.db
  2. /etc/init.d/postfix restart #使配置文件生效 因為改動了inet_protocols和inet_interfaces,所以需要restart,否則reload即可
  3. 設置DNS解析,增加兩條A記錄,之所以其中一個主機記錄寫mail,因為前面$myhostname寫了mail.howard.org啊。
  4. 設置DNS解析,增加一條MX記錄,記錄值跟$myhostname一致。因為只有一條MX記錄,所以MX優先級是多少不重要啦。
  5. 設置DNS解析,增加一條SPF記錄,其實是按照SPF格式增加一條TXT記錄,有助于提高服務器IP信譽度。


  6. 發工單跟阿里云請求對ECS的IP跟主機域名mail.howard.org做反向域名解析,有助于提高服務器IP的信譽度。
關于MX、SPF、PRT

這幾項基本都是為了增加服務器IP的信譽度的。參考這個文章的說法:如何避免你外發的電子郵件被誤判為垃圾郵件?;蛘咦约壕W上搜搜,很容易能理解。

關于Open Relay

在上面配置/etc/postfix/main.cf時提到Open Relay這個名詞。
??如果開啟了Open Relay,那么就是說,任何人都可以連接到你的SMTP服務器進行轉發將郵件寄出去,不管客戶端是哪里、發件人是誰、收件人是誰。這種情況是非常糟糕的,意味著你的服務器會成為發送垃圾郵件的人的工具。所以絕大部分收件服務器對Open Relay的SMTP服務器是零容忍,同時也有不少第三方機構會掃描記錄這種SMTP服務器,并將其扔進黑名單。所以,不要開啟Open Relay。
??那么怎么禁止Open Relay呢?換句話說就是,要怎么配置才可以限制不能被隨意利用來轉發郵件。無非就是,限制客戶端、限制發件人、限制收件人等等。其實,現在安裝的postfix,默認就是禁止Open Relay的。下面我們來理解一下是怎么禁止Open Relay的。
??在我的理解里,主要通過配置下面三個設定項來達到目的:smtpd_recipient_restrictions,smtpd_client_restrictions,smtpd_sender_restrictions。通過man 5 postconf可以查到這三個設定項的含義和用法。通過postconf -h xxxx可以查看當前配置里設定項xxxx的值。

  • smtpd_client_restrictions,用于限制發信客戶端,默認值是空,即不限制。如果是給內部固定的主機使用,可以設置這項,比如:smtpd_client_restrictions = permit_mynetworks, reject_unknown_client_hostname
  • smtpd_sender_restrictions,用于限制發信人,對應MAIL FROM指令的內容,默認值是空,即不限制。 由于MAIL FROM可以很簡單地偽造,所以,這個參數沒啥作用其實,一般不用。
  • smtpd_recipient_restrictions,用于限制收件人,對應RCPT TO指令的內容,默認值為permit_mynetworks, reject_unauth_destination,即允許客戶端屬于mynetworks的發信請求,拒絕不在mydestination范圍內的發信請求,這兩條約定不是且的關系,而是先到先得的關系,即如果發現符合mynetworks,那么不管是否符合mydestination,都通過;相反,就算不符合mynetworks,但因為第一項并沒有明說拒絕,那么繼續用第二項做判斷,可見permit_xxxreject_xxx的區別哈。這個設定項被用得比較多,對于版本號小于2.10的postfix,還有需要注意設置不當導致relay open的問題,參考《Postfix SMTP relay and access control》

搭配上面這三個設定項的,一般是mynetworksmydestination。所以這兩項的設定極其重要。

正經地發送一封成熟的郵件

行文至此,對于一個內部使用的SMTP服務器已經完成,可以通過mynetworks設置只允許內部使用。那么下面來正經地發一封郵件,還是用telnet,順便看一下有哪些常見的格式。

正經發一封郵件

最后

上面那個截圖,用的都是英文,如果要用中文的話,需要指定編碼格式之類,挺煩的,不妨打開一封從公共郵件商寄過來的郵件,看看郵件源代碼。不過后面我們會借助第三方的軟件來幫忙發送郵件,所以我們這里點到即止吧。
??現在已經可以正常發郵件了,還缺點啥呢?兩點:1、如果我想用如foxmail之類的郵件客戶端來遠程連接到本郵件服務器發郵件,要怎么做呢?畢竟前面的配置,限制了mynetworks,但我們郵件客戶端這頭的IP可是不定的啊。2、通過TCP來連接這個SMTP服務器發郵件,這中間的數據傳輸都是明文的,如果郵件內容很私密很重要,就會擔心被竊聽,怎么辦呢?我打算在下一篇文章里了解怎么解決這兩個問題。

<br />
<br />
<br />

參考的文章:
  1. 鳥哥的Linux私房菜(第二十二章)
  2. 從零開始郵件服務器搭建
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容