郵件傳輸原理#
注:
- 灰色部分表示對方伺服器
- 僅供參考,有任何錯誤歡迎評論區討論!
基本定義#
- MUA(Mail User Agent)是郵件用戶代理,也就是用戶用來發送和接收郵件的軟體。MUA 通過 IMAP 或 POP3 協議與 MRA 通信
- MTA(Mail Transfer Agent)是郵件傳輸代理,也就是郵件伺服器上的軟體,負責通過 SMTP 協議發送和轉發郵件。MTA 會根據收信人地址中的域名查詢對應的郵件伺服器,並將郵件投遞給它
- MDA(Mail Delivery Agent)是郵件投遞代理,也就是負責將 MTA 接收到的郵件保存到本地磁碟或指定地方的軟體。MDA 通常會進行垃圾郵件和病毒掃描,並提供郵件過濾和自動回覆等功能
- MRA(Mail Receive Agent)是郵件接收代理,負責實現 IMAP 和 POP3 協議,與 MUA 進行互動。MRA 可以幫助用戶從郵件伺服器上下載或查看郵件
- SMTP(Simple Mail Transfer Protocol)是一種用於電子郵件傳輸的標準協議。它定義了如何在互聯網上傳輸和傳遞電子郵件。SMTP 是一個客戶端 / 伺服器協議,它使用 TCP 作為傳輸層協議,並使用 25 端口作為默認端口,ESMTP 通過 465/587 端口
- IMAP(Internet Message Access Protocol)是一種用於電子郵件存儲和訪問的標準協議。IMAP 協議通常使用 143/993 端口
- POP3(Post Office Protocol version 3)是一種用於電子郵件下載的標準協議,它允許用戶通過郵件客戶端從郵件伺服器上下載郵件到本地計算機。它使用 110 端口進行傳輸,與 SMTP 協議配合使用。POP3 協議的重點在於下載,不支持在伺服器上管理郵件
常用的 MUA 有:Outlook、Apple Mail、Mutt、Fairemail
常用的 MTA 有:Sendmail、Postfix
常用的 MDA 有:procmail、dropmail、Cyrus
常用的 MRA 有:dovecot、Fetchmail、Getmail
傳輸過程#
郵件傳輸的過程可以分為以下幾個步驟:
本過程將模擬 "小明" <[email protected]>
發送至 "小紅" <[email protected]>
1. 發送#
小明通過 MUA 編寫郵件,並指定[email protected]
,點擊發送,MUA 使用 SMTP (s) 將郵件發送到smtp.qq.com
的25/465/587
端口
2. 傳遞#
smtp.qq.com
檢查發信地址是否屬於該帳號,如果屬於,則繼續投遞。smtp.qq.com
使用 DNS 查詢[email protected]
對應域的 MX 記錄1。查詢成功後,將郵件轉發給gmail-smtp-in.l.google.com
。
3. 接收#
該過程最複雜,因為郵件發送並無驗證身份的機制,於是後續為了防止垃圾 / 釣魚 / 假冒詐騙郵件的傳遞,接收方 MTA 會進行各種檢查來確保郵件可信,MDA 也會在 MTA 確認收信後進行掃描病毒等處理過程。如果檢查未通過,重則直接退信,輕則歸入垃圾郵箱。
在接收郵件時,Gmail 會檢查 DKIM/SPF/DMARC 等記錄以及 PTR 記錄,以確保郵件的發送者是真實的、可信的,並且該郵件未被篡改。
DNSSEC
後續檢查基本基於 DNS 系統,因此
域名需支持 DNSSEC 以防記錄被篡改
具體來說,收信方 MTA 在接收郵件時,會按照以下流程進行驗證:
- DKIM 驗證:檢查郵件中是否包含了簽名過的 DKIM 頭部字段。如果有,Gmail 會使用公鑰對消息進行驗證,以確保該郵件是由相應域名的私鑰簽名的,防止郵件被篡改
- SPF 驗證:檢查郵件的發送伺服器是否在發送域名的 SPF 記錄中列出。如果不在,就會認為這封郵件可能是偽造的垃圾 / 釣魚郵件
- DMARC 驗證:檢查郵件是否符合發送域名的 DMARC 策略。DMARC 可以指定如何處理沒有通過 DKIM 或 SPF 驗證的郵件。例如,可以要求將這些郵件標記為垃圾郵件或拒絕投遞
- PTR 驗證:檢查郵件所在的 IP 地址是否與發送郵件的域名的 PTR 記錄相匹配。PTR 記錄通常用於反向 DNS 查詢,可用於驗證郵件發送方的身份
通過上述驗證,收信方 MTA 可以判斷郵件是否來自可信的發信人,並且可以有效地防止垃圾郵件、詐騙
4. 存儲#
gmail-smtp-in.l.google.com
確認接收郵件後,MDA 進行處理,例如掃描 / 自動回覆等。然後將其存儲在[email protected]
的郵箱中
5. 查看#
如果小紅用的是支持 Push 的 MUA (例如 Gmail 客戶端),那麼現在應該已經收到了郵件提醒,打開後即可看到小明發過來的郵件
請原諒我用小明小紅,咱就是個起名廢
自建郵件伺服器#
一般來講,現在自建郵件服如果不需要圖形化界面和其他附加功能,只需要最基本的發送、接受、阻止垃圾郵件,沒必要去使用那些過於冗雜的郵件伺服器完全解決方案,例如 iRedmail
本部分使用docker-mailserver作為例子來搭建郵件服,它包含了postfix
, dovecot
, SpamAssian
, OpenDKIM
, OpenDMARC
, Fail2Ban
等服務,並簡單配置為了開箱即用,基本上半小時就可以打造一個功能完備的郵件伺服器
docker-mailserver#
準備伺服器#
需要滿足幾個條件
- ip 和伺服器域名未被列入黑名單 (可通過MX Super Tool檢查黑名單)
- 開放 25 端口
- 可配置 rDNS/PTR 記錄 (可選,盡量)
- 純淨系統,安裝時將 hostname 主機名填為郵件伺服器域名,如
mail.example.org
如果只是用 docker-mailserver 配置簡單的郵件服,使用人數少,那配置無需太高,1c 512m 即可
當然,要安裝 docker 及 docker-compose
配置#
如果你的郵件伺服器準備於二級域名下,如mail.example.org
指向它,那將hostname
配置為子域名,domainname
配置為 apex 域2即可。
此處並非填寫發信域
此處僅配置郵件伺服器的所在域,例如配置為mail.example.org
,那後面小節中的發信域 mx 記錄就指向它
如果直接用 apex 域,那就將hostname
填為example.org
,刪掉domainname
version: '3.3'
services:
mailserver:
image: docker.io/mailserver/docker-mailserver:latest
container_name: mailserver
hostname: mail
domainname: example.org
env_file: mailserver.env
environment:
- SSL_TYPE=letsencrypt
ports:
- "25:25" # SMTP (explicit TLS => STARTTLS)
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
- "465:465" # ESMTP (implicit TLS)
- "587:587" # ESMTP (explicit TLS => STARTTLS)
- "993:993" # IMAP4 (implicit TLS)
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
- /etc/letsencrypt:/etc/letsencrypt
restart: always
stop_grace_period: 1m
cap_add:
- NET_ADMIN
healthcheck:
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
timeout: 3s
retries: 0
環境變量#
以下是我自用的環境變量,使用前請修改所有example.org
佔位符為你自己準備配置的主發信域名
還有其它一些選項可配置,請看文檔了解相關環境變量含義
強烈建議使用 certbot 申請證書!上一節的docker-compose.yml
已經配置為兼容 certbot 與 letsencrypt,只需在容器外用 certbot 申請一個郵件伺服器域名的 SSL 證書即可使用
更多 SSL 配置方式請看文檔
OVERRIDE_HOSTNAME=
DMS_DEBUG=0
LOG_LEVEL=info
SUPERVISOR_LOGLEVEL=info
ONE_DIR=1
ACCOUNT_PROVISIONER=
[email protected]
ENABLE_UPDATE_CHECK=1
UPDATE_CHECK_INTERVAL=1d
PERMIT_DOCKER=none
TZ=Asia/Shanghai
NETWORK_INTERFACE=
TLS_LEVEL=
SPOOF_PROTECTION=1
ENABLE_SRS=0
ENABLE_OPENDKIM=1
ENABLE_OPENDMARC=1
ENABLE_POP3=
ENABLE_CLAMAV=0
ENABLE_RSPAMD=1
ENABLE_RSPAMD_REDIS=
ENABLE_AMAVIS=1
AMAVIS_LOGLEVEL=1
ENABLE_DNSBL=1
ENABLE_FAIL2BAN=1
FAIL2BAN_BLOCKTYPE=drop
ENABLE_MANAGESIEVE=
POSTSCREEN_ACTION=enforce
SMTP_ONLY=
SSL_TYPE=
SSL_CERT_PATH=
SSL_KEY_PATH=
SSL_ALT_CERT_PATH=
SSL_ALT_KEY_PATH=
VIRUSMAILS_DELETE_DELAY=
POSTFIX_DAGENT=
POSTFIX_MAILBOX_SIZE_LIMIT=
ENABLE_QUOTAS=1
POSTFIX_MESSAGE_SIZE_LIMIT=
CLAMAV_MESSAGE_SIZE_LIMIT=
PFLOGSUMM_TRIGGER=
PFLOGSUMM_RECIPIENT=
PFLOGSUMM_SENDER=
LOGWATCH_INTERVAL=weekly
LOGWATCH_RECIPIENT=
LOGWATCH_SENDER=
[email protected]
REPORT_SENDER=
LOGROTATE_INTERVAL=weekly
POSTFIX_INET_PROTOCOLS=all
DOVECOT_INET_PROTOCOLS=all
ENABLE_SPAMASSASSIN=1
SPAMASSASSASSIN_SPAM_TO_INBOX=1
ENABLE_SPAMASSASSASSIN_KAM=1
MOVE_SPAM_TO_JUNK=1
SA_TAG=-100000.0
SA_TAG2=5.0
SA_KILL=15.0
SA_SPAM_SUBJECT=***SPAM*****
ENABLE_FETCHMAIL=0
FETCHMAIL_POLL=300
ENABLE_LDAP=
LDAP_START_TLS=
LDAP_SERVER_HOST=
LDAP_SEARCH_BASE=
LDAP_BIND_DN=
LDAP_BIND_PW=
LDAP_QUERY_FILTER_USER=
LDAP_QUERY_FILTER_GROUP=
LDAP_QUERY_FILTER_ALIAS=
LDAP_QUERY_FILTER_DOMAIN=
DOVECOT_TLS=
DOVECOT_USER_FILTER=
DOVECOT_PASS_FILTER=
DOVECOT_MAILBOX_FORMAT=maildir
DOVECOT_AUTH_BIND=
ENABLE_POSTGREY=0
POSTGREY_DELAY=300
POSTGREY_MAX_AGE=35
POSTGREY_TEXT="Delayed by Postgrey"
POSTGREY_AUTO_WHITELIST_CLIENTS=5
ENABLE_SASLAUTHD=0
SASLAUTHD_MECHANISMS=
SASLAUTHD_MECH_OPTIONS=
SASLAUTHD_LDAP_SERVER=
SASLAUTHD_LDAP_BIND_DN=
SASLAUTHD_LDAP_PASSWORD=
SASLAUTHD_LDAP_SEARCH_BASE=
SASLAUTHD_LDAP_FILTER=
SASLAUTHD_LDAP_START_TLS=
SASLAUTHD_LDAP_TLS_CHECK_PEER=
SASLAUTHD_LDAP_TLS_CACERT_FILE=
SASLAUTHD_LDAP_TLS_CACERT_DIR=
SASLAUTHD_LDAP_PASSWORD_ATTR=
SASLAUTHD_LDAP_AUTH_METHOD=
SASLAUTHD_LDAP_MECH=
SRS_SENDER_CLASSES=envelope_sender
SRS_EXCLUDE_DOMAINS=
SRS_SECRET=
DEFAULT_RELAY_HOST=
RELAY_HOST=
RELAY_PORT=25
RELAY_USER=
RELAY_PASSWORD=
啟動 & 查看日誌#
啟動
docker-compose up -d
查看實時日誌
docker-compose logs -f
管理#
您最好的朋友!Setup.sh!
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh
chmod a+x ./setup.sh
下載setup.sh
後,可以直接運行以獲得幫助信息
由於docker-mailserver
使用虛擬域,所以任何域名的配置都會在添加相關帳號後才會生成
例如使用./setup.sh email add [email protected] "<password>"
後,example.org
的配置和存儲位置便會生成
登錄#
選擇任何一個 MUA:
IMAP:
host: mail.example.org
port: 993
encryption: tls
username: [email protected]
password: <password>
SMTP:
host: mail.example.org
port: 465
encryption: tls
username: [email protected]
password: <password>
按照這樣填寫參數登錄即可
配置 dns 記錄#
接下來,請轉到你的域名服務提供商。這裡假設已經配置好郵件伺服器域名為mail.example.org
,且已將 A 記錄指向伺服器。發信域名為example.org
請確保域名已配置好 DNSSEC
MX#
該記錄用於聲明為域處理郵件的伺服器
類型 | 名稱 | 內容 | 優先級 | TTL |
---|---|---|---|---|
MX | @ | mail.example.org | 10 | 1 小時 |
SPF#
SPF(Sender Policy Framework)用於防止郵件偽造,也就是防止垃圾郵件。可以指定哪些 IP 地址有權代表該域名發送電子郵件。
SPF 記錄語法如下:
v=spf1 a mx ip4:192.0.2.0/24 ip6:2001:0db8::/32 include:example.org ~all
- a:該域名的 A 記錄所指向的 IP 地址允許代表該域名發信
- mx:該域名的 MX 記錄所指向的 IP 地址允許代表該域名發信
- ip4/ip6:允許該網段 ip 發信
- include.com:example.com 域名的 SPF 記錄中包含的 IP 地址也可以代表該域名發送電子郵件
- ~all:表示除了上述機制之外的所有 IP 地址都可以嘗試代表該域名發送電子郵件,但是會被標記為垃圾郵件。如果將此處改為
-all
,那除了前面被允許的地址外,任何地址都不可以代表該域名發信。建議使用-all
建議配置為:
類型 | 名稱 | 內容 | TTL |
---|---|---|---|
TXT | @ | v=spf1 mx -all | 1 小時 |
DMARC#
DMARC(Domain-based Message Authentication, Reporting & Conformance)基於 SPF 和 DKIM,可以讓郵件接收方驗證發件人的身份,並向域名所有者提供有關發件人身份驗證結果的報告
當郵件接收方收到一封電子郵件時,它會通過 SPF 和 DKIM 技術驗證發件人的身份。如果發件人的電子郵件地址與 SPF 記錄和 DKIM 簽名匹配,則認為該郵件是合法的;否則就認為該郵件可能是垃圾郵件或欺詐郵件。如果域名所有者啟用了 DMARC,郵件接收方還會將驗證結果報告給域名所有者,以便其進一步分析和處理
DMARC 記錄語法如下:
v=DMARC1; p=quarantine; sp=none; rua=mailto:[email protected]; ruf=mailto:[email protected];
- p: 用於指定如何處理郵件,有三種取值:none 表示只報告,不採取任何動作,quarantine 表示將郵件放入收件人的垃圾郵件文件夾中,reject 表示直接拒絕郵件。
- sp: 用於指定子域名的處理方式,取值同 p
- rua: 指定接收郵件驗證報告的地址,可以是多個郵箱地址,用逗號分隔。
- ruf: 指定接收郵件處理報告地址,其它同上
建議配置為:
類型 | 名稱 | 內容 | TTL |
---|---|---|---|
TXT | _dmarc | v=DMARC1; p=quarantine; sp=quarantine; rua=mailto:[email protected]; ruf=mailto:[email protected]; | 1 小時 |
DKIM#
首先,通過./setup.sh config dkim domain "<domain>"
生成發信域名的 DKIM 公 / 私鑰
接著,查看docker-data/dms/config/opendkim/keys/<domain>/<selector>.txt
即可找到需要發布到 DNS 的 DKIM 公鑰,形如<selector>._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; " "p=xxxxxxx" "xxxxxxx" "xxxxxxx" ) ; ----- DKIM key mail for example.org
使用時需去掉引號與空格,將引號內內容發布至 dns,示例如下:
類型 | 名稱 | 內容 | TTL |
---|---|---|---|
TXT | <selector>._domainkey | v=DKIM1; h=sha256; k=rsa; p=xxxxxxxxxxxxxxxxxxxxx | 1 小時 |
高級:多域名使用#
由於使用虛擬域,docker-mailserver
不存在主域名的說法,任何添加了帳號的域名都可被使用來發信,因此請謹慎添加帳號,以防發送假冒郵件被退信導致域名 /ip 進黑名單
按照上文,已經配置好example.org
,該如何添加第二個test.com
?
只需要按照從 "使用 setup.sh 添加帳號" 這個步驟起,將發信域名全換為準備添加的第二個域名,重新走一遍流程,就可以正常發信 / 收信了
多個域名同理
優化#
PTR/rDNS#
PTR 記錄提供 IP 地址和域名之間的映射關係
在郵件傳輸過程中,郵件伺服器會根據 IP 地址來確定郵件發送方的域名。如果郵件伺服器收到了一封來自未知 IP 地址的郵件,它會嘗試使用 PTR 記錄查找該 IP 地址對應的域名。如果查找成功,並且該域名與郵件中的發件人域名匹配,那麼該郵件就被認為是合法的
例如,iCloud Mail 某一台 mx 伺服器的 ip 地址為17.42.251.62
,那麼dig -x 17.42.251.62
即可反查到該 ip 指向的域名
附加:解除黑名單 / 白名單#
DNSWL是一個不錯的 ip 地址庫,可以將域名與其匹配的 ip 認證為白名單
SpamHaus也可用於檢查是否為黑名單,並提供自助申訴
檢查#
Mail Tester可以非常方便地看出得分,10 分基本上就不可能被放進垃圾郵箱裡了
Footnotes#
-
該記錄對於大型郵件服務提供商而言,一般會有多個 MTA,這只是優先級最高的那個。例如通過
dig MX gmail.com
可以看到 gmail 具有五個 MX 記錄alt4.gmail-smtp-in.l.google.com
,gmail-smtp-in.l.google.com
,alt3.gmail-smtp-in.l.google.com
,alt2.gmail-smtp-in.l.google.com
,alt1.gmail-smtp-in.l.google.com
↩ -
Apex 域是指域名中的最高層級,不包括任何子域名。例如,在
www.example.org
域中,example.org
是 apex 域 ↩