自建轻量邮件服务器?先了解传输原理!
邮件传输原理
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记录,以确保邮件的发送者是真实的、可信的,并且该邮件未被篡改。
具体来说,收信方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=0
LOG_LEVEL=info
SUPERVISOR_LOGLEVEL=info
ONE_DIR=1
ACCOUNT_PROVISIONER=
POSTMASTER_ADDRESS=postmaster@example.org
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=
REPORT_RECIPIENT=postmaster@example.org
REPORT_SENDER=
LOGROTATE_INTERVAL=weekly
POSTFIX_INET_PROTOCOLS=all
DOVECOT_INET_PROTOCOLS=all
ENABLE_SPAMASSASSIN=1
SPAMASSASSIN_SPAM_TO_INBOX=1
ENABLE_SPAMASSASSIN_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 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:example.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分基本上就不可能被放进垃圾邮箱里了
-
该记录对于大型邮件服务提供商而言,一般会有多个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域 ↩︎