邮件传输原理
sequenceDiagram participant User1; participant MUA1 as Mail User Agent 1; participant MTA1 as Mail Transfer Agent 1; participant DNS; participant MTA2 as Mail Transfer Agent 2; participant MDA as Mail Delivery Agent; participant MRA as Mail Retrieval Agent; participant MUA2 as Mail User Agent 2; participant User2;
User1->>MUA1: 写一封邮件; MUA1->>+MTA1: 通过SMTP(s)发送邮件; MTA1->>+MTA1: 检查发信地址是否合法; MTA1->>+DNS: 查询收信人域名MX记录; DNS-->>-MTA1: 响应; MTA1->>+MTA2: 邮件转递; MTA2->>+DNS: 查询发信人域名MX/TXT记录; DNS-->>-MTA2: 响应; Note over MTA1,MTA2: 验证发信人MX/DKIM/SPF/DMARC/PTR; rect RGB(200,200,200); MTA2->>+MDA: 在收信人邮箱中存储邮件; MDA-->-MTA2: 确认接收邮件; end; MTA2-->>-MTA1: 确认邮件已接收; MTA1-->>MUA1: 邮件已发送; MUA1->>+MUA1: 存到已发信箱; MUA1-->>User1: 发送成功; rect RGB(200,200,200); MUA2->>+MRA: 访问邮箱下载邮件; MRA->>+MDA: 访问邮箱以检索邮件内容; MDA-->>-MRA: 响应邮件内容; MRA-->>-MUA2: 返回邮件; User2-)MUA2: 查看邮件; end;注:
- 灰色部分表示对方服务器
- 仅供参考,有任何错误欢迎评论区讨论!
基本定义
- 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
传输过程
邮件传输的过程可以分为以下几个步骤:
本过程将模拟 “小明” <ming@qq.com>发送至 “小红” <hong@gmail.com>
1. 发送
小明通过MUA编写邮件,并指定hong@gmail.com,点击发送,MUA使用SMTP(s)将邮件发送到smtp.qq.com的25/465/587端口
2. 传递
smtp.qq.com检查发信地址是否属于该账号,如果属于,则继续投递。smtp.qq.com使用DNS查询hong@gmail.com对应域的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进行处理,例如扫描/自动回复等。然后将其存储在hong@gmail.com的邮箱中
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=0LOG_LEVEL=infoSUPERVISOR_LOGLEVEL=infoONE_DIR=1ACCOUNT_PROVISIONER=POSTMASTER_ADDRESS=postmaster@example.orgENABLE_UPDATE_CHECK=1UPDATE_CHECK_INTERVAL=1dPERMIT_DOCKER=noneTZ=Asia/ShanghaiNETWORK_INTERFACE=TLS_LEVEL=SPOOF_PROTECTION=1ENABLE_SRS=0ENABLE_OPENDKIM=1ENABLE_OPENDMARC=1ENABLE_POP3=ENABLE_CLAMAV=0ENABLE_RSPAMD=1ENABLE_RSPAMD_REDIS=ENABLE_AMAVIS=1AMAVIS_LOGLEVEL=1ENABLE_DNSBL=1ENABLE_FAIL2BAN=1FAIL2BAN_BLOCKTYPE=dropENABLE_MANAGESIEVE=POSTSCREEN_ACTION=enforceSMTP_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=1POSTFIX_MESSAGE_SIZE_LIMIT=CLAMAV_MESSAGE_SIZE_LIMIT=PFLOGSUMM_TRIGGER=PFLOGSUMM_RECIPIENT=PFLOGSUMM_SENDER=LOGWATCH_INTERVAL=weeklyLOGWATCH_RECIPIENT=LOGWATCH_SENDER=REPORT_RECIPIENT=postmaster@example.orgREPORT_SENDER=LOGROTATE_INTERVAL=weeklyPOSTFIX_INET_PROTOCOLS=allDOVECOT_INET_PROTOCOLS=allENABLE_SPAMASSASSIN=1SPAMASSASSIN_SPAM_TO_INBOX=1ENABLE_SPAMASSASSIN_KAM=1MOVE_SPAM_TO_JUNK=1SA_TAG=-100000.0SA_TAG2=5.0SA_KILL=15.0SA_SPAM_SUBJECT=***SPAM*****ENABLE_FETCHMAIL=0FETCHMAIL_POLL=300ENABLE_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=maildirDOVECOT_AUTH_BIND=ENABLE_POSTGREY=0POSTGREY_DELAY=300POSTGREY_MAX_AGE=35POSTGREY_TEXT="Delayed by Postgrey"POSTGREY_AUTO_WHITELIST_CLIENTS=5ENABLE_SASLAUTHD=0SASLAUTHD_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_senderSRS_EXCLUDE_DOMAINS=SRS_SECRET=DEFAULT_RELAY_HOST=RELAY_HOST=RELAY_PORT=25RELAY_USER=RELAY_PASSWORD=启动&查看日志
启动
docker-compose up -d
查看实时日志
docker-compose logs -f
管理
您最好的朋友!Setup.sh!
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.shchmod a+x ./setup.sh下载setup.sh后,可以直接运行以获得帮助信息
由于docker-mailserver使用虚拟域,所以任何域名的配置都会在添加相关账号后才会生成
例如使用./setup.sh email add admin@example.org "<password>"后,example.org的配置和存储位置便会生成
登录
选择任何一个MUA:
IMAP: host: mail.example.org port: 993 encryption: tls username: admin@example.org password: <password>SMTP: host: mail.example.org port: 465 encryption: tls username: admin@example.org 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:postmaster@example.org; ruf=mailto:postmaster@example.org;- p: 用于指定如何处理邮件,有三种取值:none 表示只报告,不采取任何动作,quarantine 表示将邮件放入收件人的垃圾邮件文件夹中,reject 表示直接拒绝邮件。
- sp: 用于指定子域名的处理方式,取值同 p
- rua: 指定接收邮件验证报告的地址,可以是多个邮箱地址,用逗号分隔。
- ruf: 指定接收邮件处理报告地址,其它同上
建议配置为:
| 类型 | 名称 | 内容 | TTL |
|---|---|---|---|
| TXT | _dmarc | v=DMARC1; p=quarantine; sp=quarantine; rua=mailto:postmaster@example.org; ruf=mailto:postmaster@example.org; | 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分基本上就不可能被放进垃圾邮箱里了
![]()