自建docker-mailserver邮件服务器
大约两年前我就想过整一个自己域名的邮件服务,这样就可以随意注册邮箱账号了,而且看上去很帅。然而这件事却一拖再拖,到今天总算是整上了,于是就在这里记录一下整的过程。
我所使用的服务器是腾讯云的轻量应用服务器,地域在新加坡,配置是最low的2核2G入门型,由于配置比较低还跑了一些其他的服务,我只能放弃一些诸如mailcow这样的选项,最后选择的是比较轻量的docker-mailserver,不开启反病毒功能的前提下内存占用比较少。
一些约定
为了方便描述,本文中出现的下面内容均为示例,需要替换为实际值。
- 一级域名:example.com
- 邮件服务器域名:mail.example.com
- 主机IP地址:1.2.3.4
- 邮件用户名:admin
- 邮件用户密码:password
涉及到的所有DNS解析,均只写主机名,而省略一级域名example.com。
开启端口
邮件服务器需要用到的端口非常多,首先需要确保没有其他进程占用这些端口,并将它们打开:
- 25:SMTP(显式TLS端口,不可用于身份认证)
- 143:IMAP4(显式TLS端口)
- 465:ESMTP(隐式TLS端口)
- 587:ESMTP(显式TLS端口)
- 993:IMAP4 (隐式TLS端口)
- 110:POP3
- 995:POP3(TLS端口)
其中25端口被一些国内主机商默认封锁,需要手动申请解封。我的服务器好像没有封,于是直接就用起来了。
对于这些乱七八糟的端口的理解可以参考这个链接。
解析DNS
在开始之前,需要做几条基本的DNS解析。
- A记录:
mail -> 1.2.3.4
- MX记录:
mail -> mail.example.com
正式开始安装
下载、修改配置文件
mkdir mailserver && cd mailserver
DMS_GITHUB_URL='https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master'
wget "${DMS_GITHUB_URL}/compose.yaml"
wget "${DMS_GITHUB_URL}/mailserver.env"
wget "${DMS_GITHUB_URL}/setup.sh"
chmod +x setup.sh
修改compose.yaml:
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:latest
container_name: mailserver
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
hostname: mail.example.com
env_file: mailserver.env
# More information about the mail-server ports:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
# To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
ports:
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
- "465:465" # ESMTP (implicit TLS)
- "587:587" # ESMTP (explicit TLS => STARTTLS)
- "993:993" # IMAP4 (implicit TLS)
- "110:110" # POP3
- "995:995" # POP3 (with TLS)
volumes:
- /root/.certificates/:/etc/letsencrypt/ # 挂载证书
- ./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
restart: always
stop_grace_period: 1m
# Uncomment if using `ENABLE_FAIL2BAN=1`:
cap_add:
- NET_ADMIN
healthcheck:
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
timeout: 3s
retries: 0
这里我将主机上已经有的域名证书直接挂载到了容器内,以便容器内的邮件服务对其进行读取,如果还没有证书,则也可以在后面设置用Let’s Encrypt获取。
另外添加了POP3的两个端口映射。
修改mailserver.env:
...
ENABLE_POP3=1 # 开启POP3协议
...
SSL_TYPE=manual # 指定SSL证书类型,manual表示手动指定路径,这里可以改成letsencrypt(自动获取)
# 如果是manual,则手动填写下面的路径
SSL_CERT_PATH=/path/to/fullchain.pem
SSL_KEY_PATH=/path/to/privkey.pem
设置邮件账户
这个邮件服务在启动之前得预先设置一个邮件账户。
./setup.sh email add admin@mail.example.com password
./setup.sh alias add postmaster@mail.example.com admin@mail.example.com
创建了一个admin用户(不一定得是admin),然后添加了一条alias将postmaster用户指向它。
添加额外的DNS解析
接下来需要添几条与邮件安全相关的DNS。
- SPF记录
发件人策略框架(英语:Sender Policy Framework;简称SPF; RFC 4408)是一套电子邮件认证机制,可以确认电子邮件确实是由网域授权的邮件服务器寄出,防止有人伪冒身份网络钓鱼或寄出垃圾电邮。SPF允许管理员设定一个DNS TXT记录或SPF记录设定发送邮件服务器的IP范围,如有任何邮件并非从上述指明授权的IP地址寄出,则很可能该邮件并非确实由真正的寄件者寄出(邮件上声称的“寄件者”为假冒)。
TXT mail -> "v=spf1 a mx ip4:1.2.3.4 ~all"
SPF语法详见此链接。
- DMARC记录
基于域的消息认证,报告和一致性(英语:Domain-based Message Authentication, Reporting and Conformance,简称DMARC)是一套以SPF及DKIM为基础的电子邮件认证机制,可以检测及防止伪冒身份、对付网络钓鱼或垃圾电邮。
TXT _dmarc.mail -> "v=DMARC1; p=quarantine; sp=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:postmaster@mail.example.com; ruf=mailto:postmaster@mail.example.com"
DMARC的详细配置见此链接。
- DKIM记录
域名密钥识别邮件(英语:DomainKeys Identified Mail,简称DKIM)是一套电子邮件认证机制,使用公开密钥加密的基础提供了数字签名与身份验证的功能,以检测寄件者、主旨、内文、附件等部分有否被伪冒或窜改。
这个相当于将公钥通过DNS解析的方式分发到客户端,发件服务器用私钥签名,从而收件方可验证来源。
./setup.sh config dkim
cat docker-data/dms/config/opendkim/keys/mail.example.com/mail.txt
将cat
得到的( "
与" )
之间的内容(记为X)解析到DNS:
TXT mail._domainkey.mail -> X
如果可以的话,再设置一条PTR记录(DNS反向解析记录),用以降低被识别为垃圾邮件的概率。(然而腾讯云轻量应用服务器不给解析,就此作罢。)
启动服务
在启动服务之前,由于一个也许是feature的bug,我们需要给容器打一个patch:
#! /bin/bash
##
## to match what should be escaped whitespaces are escapted with \s and the other character with \character
## the final string does not need to be escaped
sed -i 's/mydestination\s=\s\$myhostname,\slocalhost\.\$mydomain,\slocalhost/mydestination=localhost.$mydomain,localhost/g' /etc/postfix/main.cf
echo "user-patches.sh successfully executed with custom main.cf hotfix"
将上述脚本放置在docker-data/dms/config/user-patches.sh
,然后:
docker compose up -d
可以前往这个网站测试一下tls是否可用。
实测该服务占用内存在100M左右,可以说是非常轻量了:
部署起来了,然而怎么使用?
这个邮件服务器并没有自带的web服务,因此我们需要使用第三方客户端来登录账号,进行邮件收发。
使用Python脚本通过587端口发邮件:
import smtplib
from email.mime.text import MIMEText
from email.utils import formatdate
mail_host = 'mail.example.com'
mail_user = 'admin@mail.example.com'
mail_pass = 'password'
sender = 'FBI'
receivers = ['admin@mail.114514.com']
content = """Open the door!!!"""
message = MIMEText(content, 'plain', 'utf-8')
message['Subject'] = 'Your email has been hacked!'
message['From'] = sender
message['To'] = receivers[0]
message['Date'] = formatdate()
server = smtplib.SMTP(mail_host, 587) # 连接587端口
server.ehlo()
server.starttls() # 开启tls
server.login(mail_user, mail_pass)
server.sendmail(sender, receivers, message.as_string())
server.quit()
iOS邮箱app:
后续设置中,将发件服务器端口设置为587或者465,勾选SSL。
最后,可以在这个网站对上面设置的DKIM、SPF等进行测试:
以及这个网站,可以进一步测试邮件的得分:
很遗憾,我的.xyz
域名被识别为了SUSPICIOUS_NTLD
,再加上用不上rDNS,估计会被以很高的置信度丢进垃圾箱了 🙁 以后再改进!