Stalwart Mail & Collaboration Server. Secure, scalable and fluent in every protocol (IMAP, JMAP, SMTP, CalDAV, CardDAV, WebDAV).
最近给邮件服务从 Zoho 改为 Self-hosting,搜索过程中,Stalwart 这个使用 Rust 的软件越看越符合需求。R门!
主要看重两点,资源占用以及支持 JMAP,在本人的邮件服务器上,内存占用只有不到 200MB,并且支持 JMAP 意味着出站只需开放 25/80/443,传统的 465/587/993 在支持 JMAP 的客户端上都可以不需要。
提前解析部分域名,方便初次配置
首先是 Mail eXchanger,我这里以 mx.domain.org A 记录指向 1.2.4.8 ,且 1.2.4.8 的 rDNS 同样指向 mx.domain.org 为例 其次是 MX 解析,@domain.org MX 记录指向 mx.domain.org,优先级 10 最后是 Web Admin,这里使用 mail.domain.org A 记录指向 1.2.4.8,使用 CDN
这里我给出 Zonefile 作为该步参考(Stalwart也会有该文件)
;; A Records
mail.domain.org. 1 IN A 1.2.4.8 ; cf_tags=cf-proxied:true
mx.domain.org. 1 IN A 1.2.4.8 ; cf_tags=cf-proxied:false
;; CNAME Records
autoconfig.domain.org. 1 IN CNAME mail.domain.org. ; cf_tags=cf-proxied:true
autodiscover.domain.org. 1 IN CNAME mail.domain.org. ; cf_tags=cf-proxied:true
mta-sts.domain.org. 1 IN CNAME mail.domain.org. ; cf_tags=cf-proxied:true
;; MX Records
domain.org. 1 IN MX 10 mx.domain.org.
;; SRV Records
_caldavs._tcp.domain.org. 1 IN SRV 0 1 443 mail.domain.org.
_carddavs._tcp.domain.org. 1 IN SRV 0 1 443 mail.domain.org.
_imaps._tcp.domain.org. 1 IN SRV 0 1 993 mx.domain.org.
_submissions._tcp.domain.org. 1 IN SRV 0 1 465 mx.domain.org.
_submission._tcp.domain.org. 1 IN SRV 0 1 587 mx.domain.org.
为了方便起见我选择直接使用 Docker 来进行安装,并使用 Caddy 来代理 webadmin, 使用 acme.sh 来统一管理证书
1services:
2 stalwart:
3 image: stalwartlabs/stalwart:latest-alpine
4 container_name: stalwart
5 restart: unless-stopped
6 ports:
7 - "25:25" # SMTP
8 - "465:465" # SMTPS
9 - "993:993" # IMAPS
10 volumes:
11 - /data/stalwart/data:/opt/stalwart
12 - /data/acme.sh/data:/certs:ro
13
14 networks:
15 - mailserver
16
17networks:
18 mailserver:
19 external: true
注意,我这里只对外映射了 25/465/993 三个端口,对于常规使用来说是完全足够的
1services:
2 caddy:
3 image: caddy:2-alpine
4 container_name: caddy
5 restart: unless-stopped
6
7 ports:
8 - "80:80"
9 - "443:443"
10
11 volumes:
12 - /data/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
13 - /data/caddy/data:/data
14 - /data/caddy/config:/config
15 - /data/acme.sh/data:/certs:ro
16
17 networks:
18 - mailserver
19
20networks:
21 mailserver:
22 external: true
注意,Caddy 与 Stalwart 需要在同一个网络中
mail.domain.org {
reverse_proxy stalwart:8080 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Host {host}
header_up Host {host}
}
tls /certs/domain.org_ecc/fullchain.cer /certs/domain.org_ecc/domain.org.key
log {
output file /data/log/mail.access.log
}
}
mta-sts.domain.org, autoconfig.domain.org, autodiscover.domain.org {
handle /.well-known/* {
reverse_proxy stalwart:8080 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Host {host}
header_up Host {host}
}
}
handle {
respond "Forbidden" 403
}
tls /certs/domain.org_ecc/fullchain.cer /certs/domain.org_ecc/domain.org.key
log {
output file /data/log/mail.access.log
}
}
与官方示例不同,这里我只代理 http 流量,其余流量直接映射到 Stalwart 容器内
准备完上述文件后就可以启动 Stalwart 和 Caddy 了
使用: docker logs stalwart 来获取管理员账户及密码
访问: https://mail.domain.org 来进行配置
Setting->Server->
Network->Hostname:
mx.domain.org
TLS->Certificates:
Create certificate:
Certificate Id: domain.org
Certificate : %{file:/certs/domain.org/fullchain.cer}%
Private Key: %{file:/certs/domain.org/domain.org.key}%
启用 Default
Setting->HTTP->Setting:
Base URL: 'https://mail.domain.org'
启用 Obtain remote IP from Forwarded header
Setting->SMTP->
Inbound->MTA-STS->MX Patterns:
mx.domain.org
Reports->Outbound->Default Domain:
domain.org
Setting->Security-Allowed IPs:
Create address:
你的内网地址块
点击 Reload configuration 或重启容器加载配置
Management->Directory->
Domains:
Create domain:
Domian name: domain.org
现在就可以去添加对应的TXT记录:
;; TXT Records
dkime._domainkey.domain.org. 1 IN TXT "v=DKIM1; k=ed25519; h=sha256; p=xxx"
dkimr._domainkey.domain.org. 1 IN TXT "v=DKIM1; k=rsa; h=sha256; p=xxx"
_dmarc.domain.org. 1 IN TXT "v=DMARC1; p=reject; rua=mailto:[email protected]; ruf=mailto:[email protected]"
_mta-sts.domain.org. 1 IN TXT "v=STSv1; id=16426630999589944260"
mx.domain.org. 1 IN TXT "v=spf1 a ra=postmaster -all"
_smtp._tls.domain.org. 1 IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"
domain.org. 1 IN TXT "v=spf1 mx ra=postmaster -all"
至于 TLSA 看个人需要添加
转到 Management->Directory->Accounts
点击 Create Account 创建账户,需要注意的是,Stalwart 的 Login name 并不强制要求 @domain 结尾,但是如果你需要客户端自动发现功能,还是添加 @domain 为上。
对于 Catch-all ,Stalwart 的默认策略是发给 Aliases 中为 Domain-part 的用户(在本例中为:@domain.org),可自行前往 Setting->SMTP->Inbound->RCPT stage->Catch-all 修改策略。
账户基本信息设定完成之后,点击 Authentication 设置主密码、应用密码。
点击 Permissions 可以为用户添加不同的细分权限或角色,可以添加一个 [email protected] 用户并给予 admin 角色来代替默认的 admin 账户。
到此,便可以正常的使用支持 IMAP/SMTP 的客户端来收发邮件了,但是,在开头我就说过,使用 Stalwart 的一个原因是支持 JMAP,下面便是针对此方面的配置
Stalwart 的 JMAP 支持基本是开箱即用的,只是要做一些细节调整
Setting->Authentication->OpenID Connect
默认的 Signature algorithm 为 HS256,改为 ES256或 RS256 皆可,这里使用 ES256
Signature Key 我们使用 openssl 来生成 prime256v1 ecdsa key:
1openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out ec_private_key_pkcs8.pem
Setting->Authentication->OAuth
在 Dynamic Client Registration 中启用 Require client registration 选项
虽然在上面部分中,已经使用 CNAME/SRV 配置了部分服务的自动发现服务,但是实际情况中还是一定程度依赖 .well-known
根据 RFC 8615 中的定义,domain.org/.well-known/* 下存放着特定用途的配置文件用于自动发现服务
而我们需要用到 openid-configuration/webfinger/jmap
如果你的主域名能直接 A 解析到 MX 主机上那是最好不过的,但是实际情况中,主域名基本都有别的业务
所以就需要根据实际情况来配置主域的.well-known了
我这里的选择是将所有对 domain.org/.well-known/* 的请求转发到 mail.domain.org/.well-known/*
当然,其实不做这一步也可以,手动填写服务 URL 也行
截至本文完结当天,也没有一个功能完备的 GUI 客户端可供选择
Webmail这里推荐:https://github.com/thunderbird/stormbox
Android: https://github.com/linagora/tmail-flutter https://codeberg.org/iNPUTmice/lttrs-android
又是折腾的一天,笑