概念解釋
SPF:Sender Policy Framework,直譯過來就是發件人保證框架.出現的主要原因是SMTP協議的缺陷.XMTP中,發件人的郵箱地址是可以偽造
的,因而SPF的出現就是防止偽造發件人.SPF的記錄實際上就是DNS服務器上面的一個記錄.如果郵件服務器收到了一封郵件,來自主機的IP是45.76.210.63
并且生成發件人為yusengy@info
.郵件服務器會去查詢yusengy@info
的SPF記錄.如果SPF記錄的IP為45.76.210.63
,那么就認為是合法的,否則通常都會被退信.SPF記錄一般如下.

DKIM:DomainKeys Identified Mail.功能目的與SPF相似,主要是讓收件人可以通過加密解密的方式來得知發件人是否是真實的.原理就是在電子郵件的開頭插入一段簽名,然后接收方通過從DNS查詢得到公鑰以后,以進行驗證,與SSH的公鑰和密鑰類似.
記錄一般如下:

PTR:反向域名解析,可以通過發件人的IP地址反向得知域名,也是一種用以判斷發件人是否正常的方式.
MUA:Mail User Agent.用戶郵件代理,用戶通過MUA接收發送郵件.例如Outlook, formail等.
MTA: Mail Transfer Protocol.郵件傳輸代理,是SMTP的一種實現.常用的MTA有sendmail
,Postfix
.本例中使用Postfix
.MTA僅僅負責郵件的傳輸,MDA負責把接收到的郵件保存在硬盤中.
MDA: Mail Deliver Agent,郵件分發代理.負責將接收到的郵件保存在郵件服務器上.sendmail以及Postfix默認使用的MDA是procmail
.
MRA: Mail Receive Agent,郵件接收代理,用來實現IMAP,POP3協議,負責與MUA
交互,將服務器上的郵件通過IMAP以及POP3傳輸給客戶端.本例中使用的MRA是Dovecot
.
LMTP:Local Mail Transfer Protocol.本地郵件傳輸協議,是SMTP協議的擴展.本例中用與同一主機內郵件傳輸.
Postfix:一個開源的MTA服務器,負責通過SMTP協議管理發送到本機的郵件以及由本機向外發送的郵件.與sendMail相似.現今流行的服務器套件例如Zimbra,IRedMail內部都采用Postfix作為MTA.
Dovecot:一個開源的IMAP以及POP3服務器.通常工作是驗證用戶身份以及郵件的處理.
MySQL:存取用戶信息,監聽的域名信息,用戶郵箱地址以及登錄密碼等.
郵件協議簡述
SMTP
SMTP全稱是Simple Mail Transfer Protocol,直譯過來就是簡單郵件傳輸協議, 由RFC5321定義.主要的工作就是把郵件信息從發件人的郵件服務器中傳送到接收人的郵件服務器中,偶爾我們使用MUA來發送郵件的話,也承載傳輸用戶郵件到發件服務器的功能, 因而也稱作推
協議,順帶提一下,SMTP協議的出現是比HTTP還早的,最早在1982年中發布第一版的RFC.
因而因為提出的年代久遠,所以在當時有許多問題都考慮不全面并且也有這許多的限制,例如SMTP要求信息內容需要是7位的ASCII
來承載,因而我們在發送以及接受的時候,都需要將其編碼解碼.
另外,SMTP還存在一個問題就是沒有對發送方進行一個身份驗證,雖然在早期的互聯網環境沒有很大問題,但是在垃圾郵件滿天飛的今天,這卻是一個明顯的短板.因而衍生出了SPF
, DKIM
, DMARC
等一系列用來驗證發件人身份的方法.
POP3
POP3,Post Office Protocol Version 3,直譯就是郵局協議第三版,由RFC1939進行定義.這個協議的主要服務于用戶管理郵件服務器上面的電子郵件.具體過程是:當外來郵件發送到收件人的郵件服務器上時,收件人可以使用郵件客戶端連接郵件服務器,把未閱讀的郵件服務器以及部分信息拉取回本地進行處理.在拉取的過程中,我們可以選擇拉取完刪除
以及拉取完不刪除
兩種方式,不過現在默認一般都是拉取完不刪除,方便我們在其他的地方也能對郵件進行閱讀以及管理.
IMAP
IMAP, Internet Message Access Protocol, 直譯過來就是網絡信息訪問協議,可能和現在主流的翻譯不太一致,不過能理解其意思就好.相對于pop3協議所有郵件的管理都需要下載下來進而管理,IMAP提供了用戶遠程訪問郵件服務器的途徑,因而通過IMAP,用戶可以直接管理郵件服務器上的郵件.
工作原理
電子郵件架構

這里我們使用的MTA是Postfix
, MRA是Dovecot
.我們可以繪制出郵件服務器接受郵件,用戶查收郵件以及用戶發送郵件的過程.
郵件服務器接受收件
假設yusen@Foxmail.com
發送一封郵件到yusen@yusengy.info
中.

- foxmail.com服務器會通過DNS查詢到yusengy.info的MX記錄,然后找到Postfix所在的服務器所在.
- 郵件通過SMTP協議發送給Postfix.
- Postfix通過MySQL查詢,yusengy.info是否是本機需要處理的域名.
- MySQL通過查詢返回確認信息給Postfix.
- Postfix得到確認信息,把郵件傳送給Dovecot,由Dovecot的LMTP服務來說投遞工作.
- Dovecot把郵件的內容保存在對應的路徑中.
用戶查收郵件過程

- MUA向Dovecot請求IMAP連接.
- Dovecot發送自己的SSL證書.
- MUA發送用戶的賬號密碼.
- Dovecot得到賬號密碼向MySQL查詢.
- MySQL返回查詢結果.
- 如果賬號密碼正確,Dovecot讀取在改用戶路徑下的信息.
- 得到最新的郵件以及其他的一些統計信息.
- 通過IMAP協議發送給MUA.
用戶發送郵件的過程

- MUA請求Postfix建立SMTP連接
- Postfix發送SSL證書給MUA
- MUA發送賬號密碼給Postfix,請求驗證
- Postfix請求Dovecot驗證賬號密碼.
- Dovecot請求MySQL查詢結果
- MySQL返回查詢結果.
- Dovecot返回Postfix賬號密碼驗證結果.
- Postfix返回MUA賬號密碼驗證結果.
- MUA使用SMTP協議發送郵件到Postfix.
- Postfix把郵件進行發送.接下來的步驟參考前面服務器查收郵件的過程.
郵件服務器搭建
根據上面的幾個示意圖,我們已經基本了解了郵件發送,接收的一般流程以及每個組件在其中的職責.了解清楚以后,郵件服務器的搭建則會變得簡單了許多,明白其中每一步的作用以及緣由.
前期準備
設置hostname
CentOS7,可以通過hostnamectl set-hostname hostname
命令設置hostname,并且修改hosts文件.這里域名是yusengy.info.
hostnamectl set-hostname mail.yusengy.info
為什么要設置hostname呢?因為一般情況下,Postfix在與其他的SMTP服務器進行通信的時候,會使用hostname來表名自己的身份.主機名有兩種形式,單名字
與FQDN(Fully Qualified Domain Name)
.如果SMTP服務器不是用FQDN來表明身份,則有可能會被拒收.
修改防火墻開放端口
修改防火墻開發相應的端口,分別是25, 465, 587, 110, 995, 143, 993
.
域名解析配置

安裝Postfix, Dovecot以及數據庫
由于Centos中默認的源沒有MySQL,因而使用MariaDB代替,實際使用與MySQL一致.
首先更新系統yum update -y
.把系統的一些組件更新到最新,然后需要修改一些CentOS的源設置,因為CentOS默認源里面的Postfix默認是不能和MariaDB協同工作的,因而我們需要安裝擴展源里面的Postfix.
修改: /etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-$releasever - Base
exclude=postfix
#released updates
[updates]
name=CentOS-$releasever - Updates
exclude=postfix
修改完畢以后,我們讓擴展源生效,并且安裝我們所需要的應用以及服務.
yum --enablerepo=centosplus install postfix
yum install dovecot mariadb-server dovecot-mysql
接下來,我們配置MariaDB數據庫來處理虛擬域名以及用戶信息.
數據庫配置
MariaDB的安裝配置與MySQL有些許不同,MySQL是在安裝的時候設置root的密碼,而MariaDB則是在安裝完畢后設置密碼.
首先我們啟動MariaDB.
systemctl enable mariadb.service
systemctl start mariadb.service
然后,我們進行一些初始化的配置.
mysql_secure_installation
輸入mysql_secure_installation
后,我們可以修改root用戶的密碼,禁止外部使用root登錄,刪除匿名賬戶以及刪除test表等等.
數據庫概覽
創建mail數據庫用以處理郵件相關的業務.并且創建郵件管理員.
GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost' IDENTIFIED BY 'mys123123';
GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost.localdomain' IDENTIFIED BY 'mys123123';
FLUSH PRIVILEGES;
這里的mys123123
替換成自己的密碼.我這里只是做示范使用,實際中應該使用強度更大的字符串作為密碼.

mail數據庫中一共有4個表,分別是
虛擬域名
, 郵件轉發
, 用戶信息
以及傳輸路徑
四個表..
虛擬域名表
該表存儲的是本地服務器用來接收郵件的域名.

創建語句
CREATE TABLE domains (domain varchar(50) NOT NULL, PRIMARY KEY (domain) );
郵件轉發
可以用來轉發郵件.

CREATE TABLE forwardings (source varchar(80) NOT NULL, destination TEXT NOT NULL, PRIMARY KEY (source) );
用戶表
用來存儲用戶的賬號密碼.這里密碼使用加密的方式進行存儲.

CREATE TABLE users (email varchar(80) NOT NULL, password varchar(20) NOT NULL, PRIMARY KEY (email) );
傳輸路徑表
傳輸表可以用來指定郵件的傳輸路徑.

CREATE TABLE transport ( domain varchar(128) NOT NULL default '', transport varchar(128) NOT NULL default '', UNIQUE KEY domain (domain) );
在配置完數據庫以后,我們最好修改數據庫的配置文件,只允許本地訪問數據庫,提高安全性.
修改: /etc/my.cnf
bind-address=127.0.0.1
如果數據庫存儲在其他的服務器上面,我們這里的bind-address地址在后面配置Postfix的時候需要相應的修改.不過為了安全起見,最好還是不要讓數據庫能夠直接在外部可以訪問.
配置完成了以后,我們則可以重啟數據,使配置生效.
systemctl restart mariadb.service
配置Postfix
配置完成了數據庫以后,我們就需要配置Postfix,讓其可以與MariaDB協同工作.因為Postfix尋找域名,用戶賬號等需要通過數據庫來完成,但是其本身是不知道如何查詢的,因而需要我們定制化.
下面的配置中,把mys123123替換成前面數據庫管理員設置的密碼
創建虛擬域名配置
創建:/etc/postfix/mysql-virtual_domains.cf
user = mail_admin
password = mys123123
dbname = mail
query = SELECT domain AS virtual FROM domains WHERE domain='%s'
hosts = 127.0.0.1
創建郵件轉發配置
創建:/etc/postfix/mysql-virtual_forwardings.cf
user = mail_admin
password = mys123123
dbname = mail
query = SELECT destination FROM forwardings WHERE source='%s'
hosts = 127.0.0.1
創建虛擬郵箱配置
創建: /etc/postfix/mysql-virtual_mailboxes.cf
user = mail_admin
password = mys123123
dbname = mail
query = SELECT CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') FROM users WHERE email='%s'
hosts = 127.0.0.1
創建電子郵件與文件映射
創建:/etc/postfix/mysql-virtual_email2email.cf
user = mail_admin
password = mys123123
dbname = mail
query = SELECT email FROM users WHERE email='%s'
hosts = 127.0.0.1
創建完畢以后修改權限以及分配用戶組.
chmod o= /etc/postfix/mysql-virtual_*.cf
chgrp postfix /etc/postfix/mysql-virtual_*.cf
同時,我們創建一個新的用戶組以及用戶,用來處理郵件.所有的虛擬郵箱,都會存在這個用戶的home
目錄下.
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /home/vmail -m
接下來,我們對Postfix總體進行配置.把下面的mail.yusengy.info
替換成你的hostname.如果你希望使用自己的SSL證書,則把/etc/pki/dovecot/private/dovecot.pem
替換成你的證書路徑.
postconf -e 'myhostname = mail.yusengy.info'
postconf -e 'mydestination = localhost, localhost.localdomain'
postconf -e 'mynetworks = 127.0.0.0/8'
postconf -e 'inet_interfaces = all'
postconf -e 'message_size_limit = 30720000'
postconf -e 'virtual_alias_domains ='
postconf -e 'virtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_forwardings.cf, mysql:/etc/postfix/mysql-virtual_email2email.cf'
postconf -e 'virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains.cf'
postconf -e 'virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailboxes.cf'
postconf -e 'virtual_mailbox_base = /home/vmail'
postconf -e 'virtual_uid_maps = static:5000'
postconf -e 'virtual_gid_maps = static:5000'
postconf -e 'smtpd_sasl_type = dovecot'
postconf -e 'smtpd_sasl_path = private/auth'
postconf -e 'smtpd_sasl_auth_enable = yes'
postconf -e 'broken_sasl_auth_clients = yes'
postconf -e 'smtpd_sasl_authenticated_header = yes'
postconf -e 'smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination'
postconf -e 'smtpd_use_tls = yes'
postconf -e 'smtpd_tls_cert_file = /etc/pki/dovecot/certs/dovecot.pem'
postconf -e 'smtpd_tls_key_file = /etc/pki/dovecot/private/dovecot.pem'
postconf -e 'virtual_create_maildirsize = yes'
postconf -e 'virtual_maildir_extended = yes'
postconf -e 'proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $virtual_mailbox_limit_maps'
postconf -e 'virtual_transport = dovecot'
postconf -e 'dovecot_destination_recipient_limit = 1'
修改Postfix配置,添加Dovecot服務.
修改:/etc/postfix/master.cf
dovecot unix - n n - - pipe
flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient}
把submission
以及smtps
部分給反注釋了,讓這部分代碼生效.

重啟Postfix,至此,Postfix就基本配置好了.
systemctl enable postfix.service
systemctl start postfix.service
Dovecot配置
修改:/etc/dovecot/dovecot.conf
這里把域名yusengy.info
替換成自己配置的域名.
protocols = imap pop3
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = maildir:/home/vmail/%d/%n/Maildir
ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
ssl_key = </etc/pki/dovecot/private/dovecot.pem
namespace {
type = private
separator = .
prefix = INBOX.
inbox = yes
}
service auth {
unix_listener auth-master {
mode = 0600
user = vmail
}
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
user = root
}
service auth-worker {
user = root
}
protocol lda {
log_path = /home/vmail/dovecot-deliver.log
auth_socket_path = /var/run/dovecot/auth-master
postmaster_address = postmaster@yusengy.info
}
因為數據庫存儲有用戶的賬號密碼信息,Dovecot的職責之一就是驗證用戶的賬號密碼,因而我們還需要創建一個配置文件以讓Dovecot與數據庫進行交互.
老慣例,這里也是把密碼進行替換一下.
創建:/etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = host=127.0.0.1 dbname=mail user=mail_admin password=mys123123
default_pass_scheme = CRYPT
password_query = SELECT email as user, password FROM users WHERE email='%u';
修改文件所屬的用戶組以及訪問權限
chgrp dovecot /etc/dovecot/dovecot-sql.conf.ext
chmod o= /etc/dovecot/dovecot-sql.conf.ext
設置Dovecot在開啟啟動并且啟動其服務
systemctl enable dovecot.service
systemctl start dovecot.service
然后我們觀察一下var/log/maillog
,確定現在Dovecot沒有錯誤

然后我們使用Telnet檢測一下pop3服務是否正常.
yum install telnet
telnet localhost pop3
如果你看到的和我看到的差不多,那么說明是pop3服務是正常的.

檢測Postfix
接下來則是檢測Postfix是否運行正常.
telnet localhost 25
ehlo localhost
如果連接成功,同時輸入EHLO
命令有如下返回值,則Postfix是正常的.

創建用戶
接下來,我們在MariaDB中的郵箱中加入新的用戶,用作日常郵件的發送.
這里的用戶密碼不要使用明文存儲.
USE mail;
INSERT INTO domains (domain) VALUES ('yusengy.info');
INSERT INTO users (email, password) VALUES ('yusen@yusengy.info', ENCRYPT('123123'));
quit
到這個時候,我們的郵件服務器基本就已經搭建完成了,接下來只要使用MUA進行連接,就能像正常的郵件服務器一樣使用了.

嘗試給自己的QQ郵箱發件看看.是不是收到了?

如果被拒收或者在垃圾箱中,說明我們的郵件發送服務器信譽不夠,這個時候我們需要給我們的郵件服務器做一些條件,例如添加SPF
, DKIM
以及DMARC
等,以提高郵件發送的成功率,當這些完成以后,基本能夠直接發送到收件箱了.具體可以參考這一篇文章
郵件服務器添加SPF,DKIM,DMARC,PTR
至此,一個郵件服務器的搭建就完成了,通過這一系列的操作,是否對于電子郵件協議又有了進一步的理解呢?
如果沒有在收件箱收到發送的郵件,可以在垃圾箱看看,或者看看是否退件了.這篇文章中我們沒有談到SPF
, DKIM
以及DMARC
驗證的配置,因而有可能會被QQ郵箱退件的,畢竟現實中的郵件服務器還需要一系列的配置,以提高送達率.這些我在后面會開一篇文章專門講解.
如果MUA提示發件失敗,那么我們可以查看一下日志,分別在/var/log/maillog
以及/home/vmail/dovecot-deliver.log
,找到對應的錯誤,然后回到文章相應的地方看看是否配置錯了.如果還無法解決,可留言私信,我看到了會進行相應的解答.