Bladeren bron

docs: update multi-user deployment guide

Codex 1 maand geleden
bovenliggende
commit
23f51d7396
2 gewijzigde bestanden met toevoegingen van 69 en 90 verwijderingen
  1. 5 1
      .env.example
  2. 64 89
      README.md

+ 5 - 1
.env.example

@@ -1,8 +1,12 @@
 APP_PORT=3025
 APP_PORT=3025
 APP_BASE_URL=http://mail-send.ss5.xyz
 APP_BASE_URL=http://mail-send.ss5.xyz
 ADMIN_USER=admin
 ADMIN_USER=admin
+ADMIN_EMAIL=admin@mailhub.local
 ADMIN_PASSWORD=change-this-admin-password
 ADMIN_PASSWORD=change-this-admin-password
-API_TOKEN=change-this-api-token
+SESSION_SECRET=change-this-long-random-session-secret
+
+# Legacy admin send token. New users should generate their own token in the web UI.
+API_TOKEN=change-this-legacy-api-token
 SUBMISSION_ENABLED=true
 SUBMISSION_ENABLED=true
 SUBMISSION_HOST=in.ss5.xyz
 SUBMISSION_HOST=in.ss5.xyz
 SUBMISSION_BIND=0.0.0.0
 SUBMISSION_BIND=0.0.0.0

+ 64 - 89
README.md

@@ -1,12 +1,12 @@
 # MailHub
 # MailHub
 
 
-MailHub 是一个 Docker 化的发信域名管理面板和发送 API。它负责:
+MailHub 是一个 Docker 化的多用户发信域名管理面板、SMTP Submission 服务和发送 API。它负责:
 
 
-- 添加发信域名并生成验证 TXT
-- 为每个域名生成 DKIM key 和 DNS 记录
-- 读取当前公网 DNS,合并已有 SPF 和第三方发信 include
-- 给出 SPF、DKIM、DMARC、PTR、发信主机 A 记录的状态和建议
-- 通过内部 Postfix 出站队列发送邮件,API 会先给邮件加 DKIM 签名。
+- 开放注册用户账号,并隔离每个用户的域名、SMTP 凭据、DNS API 凭据和发送记录
+- 为每个发信域名生成验证 TXT、DKIM key、SPF、DMARC、发信主机 A 记录建议
+- 支持 Cloudflare、阿里云 DNS、腾讯云 DNSPod API,一键写入基础发信 DNS
+- 读取公网 DNS,检查 SPF、DKIM、DMARC、PTR、发信主机 A 记录状态
+- 通过内部 Postfix 出站队列发送邮件,并按发件域名加 DKIM 签名。
 
 
 ## 运行
 ## 运行
 
 
@@ -17,127 +17,102 @@ docker compose ps
 docker compose logs -f app postfix
 docker compose logs -f app postfix
 ```
 ```
 
 
-管理面板默认监听宿主机 `127.0.0.1:3025`,nginx 已反代到 `mail-send.ss5.xyz`
+管理面板默认监听宿主机 `127.0.0.1:3025`,nginx 可反代到公网域名
 
 
-面板使用 HTML 登录页和 Cookie 会话。登录账号和 API Token 在 `.env`:
+## 账号和权限
 
 
-- `ADMIN_USER`
-- `ADMIN_PASSWORD`
-- `API_TOKEN`
-- `SESSION_SECRET` 可选;不设置时会从管理密码和 API Token 派生
+- 首次启动会根据 `.env` 中的 `ADMIN_USER`、`ADMIN_EMAIL`、`ADMIN_PASSWORD` 创建或修复一个 admin 用户。
+- 普通用户可在 `/register` 开放注册。
+- 用户只能看到自己的域名、SMTP 凭据、DNS 凭据、API Token 和发送记录。
+- admin 用户可在网页里修改系统业务设置、查看用户列表、禁用或启用用户
 
 
-## SMTP 公网发信
+## SMTP 发信
 
 
-MailHub 现在提供公网 SMTP Submission。所有发信端口都需要 SMTP AUTH,未认证不会转发邮件,避免开放中继。
+MailHub 提供公网 SMTP Submission。所有发信端口都需要 SMTP AUTH,未认证不会转发邮件,避免开放中继。
 
 
 连接信息:
 连接信息:
 
 
 ```txt
 ```txt
-Host: in.ss5.xyz
+Host: 由 SUBMISSION_HOST 配置,例如 in.example.com
 Port 25:   SMTP + STARTTLS + AUTH
 Port 25:   SMTP + STARTTLS + AUTH
 Port 587:  SMTP Submission + STARTTLS + AUTH
 Port 587:  SMTP Submission + STARTTLS + AUTH
 Port 465:  SMTPS implicit TLS + AUTH
 Port 465:  SMTPS implicit TLS + AUTH
 Port 2525: SMTP + STARTTLS + AUTH
 Port 2525: SMTP + STARTTLS + AUTH
-Username: 在面板左侧“SMTP 凭据”中配置
-Password: 在面板左侧“SMTP 凭据”中配置,保存后不会回显
+Username: 用户在网页“SMTP 凭据”中配置,可复制
+Password: 用户在网页“SMTP 凭据”中配置,可复制
 ```
 ```
 
 
-默认 `SUBMISSION_ALLOW_INSECURE_AUTH=false`,也就是 `25/587/2525` 必须先 STARTTLS 才允许 AUTH;`465` 连接建立时就是 TLS
+SMTP 密码会同时保存哈希和服务端加密密文:哈希用于认证,加密密文用于用户本人在网页复制。旧数据如果只有哈希,无法反解,需要用户重新设置一次密码后才能复制
 
 
-`.env` 中的 `SUBMISSION_USERNAME` 和 `SUBMISSION_PASSWORD` 只用于首次初始化数据库。初始化后以面板保存的 SMTP 凭据为准,修改后无需重启。
+`.env` 中的 `SUBMISSION_USERNAME` 和 `SUBMISSION_PASSWORD` 只用于首次给 admin 初始化 SMTP 凭据。初始化后以每个用户在面板保存的 SMTP 凭据为准,修改后无需重启。
 
 
-当前 TLS 证书位于:
+## DNS API 一键配置
 
 
-```txt
-certs/mail-send.ss5.xyz.crt
-certs/mail-send.ss5.xyz.key
-```
-
-这是一张自签名证书,只用于先把协议跑通。正式公网使用建议替换为包含 `in.ss5.xyz` 的可信证书,然后重启:
-
-```bash
-docker compose up -d app
-```
-
-## 当前默认配置
-
-```env
-MAIL_HOSTNAME=in.ss5.xyz
-SENDING_IP=8.231.54.11
-DEFAULT_SPF_MECHANISMS=include:spf.mailjet.com
-SMTP_HOST=postfix
-SMTP_PORT=25
-```
-
-如果实际出站 IP 不是 `8.231.54.11`,需要同步修改 `.env` 和面板里对应域名的“发信 IP”。
-
-## DNS 重点
-
-SPF 只能保留一条 `v=spf1` TXT。不要为了 Mailjet、飞书、本机发信分别添加多条 SPF;要合并为一条。
-
-## in.ss5.xyz 测试记录
-
-请为 `in.ss5.xyz` 添加或确认以下记录:
-
-```txt
-in.ss5.xyz.  A  8.231.54.11
-```
-
-```txt
-_mailhub.in.ss5.xyz.  TXT  mailhub-verification=9e45358e44624ce991c655e9c19b847f8c98
-```
+用户可在网页“DNS API”中保存以下服务商凭据:
 
 
-```txt
-mh202607._domainkey.in.ss5.xyz.  TXT  v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuaQrn0IrsNDVsbn160yOR7DMBaOkXegtlhUCvHYdKCy5Aa/rUcGvUI3gbLoo6m9QEiLs3EAJo29Y9RBbv3lQn/PvAKtfrTuT6Zo1BfcIhe9wJvipgAkH+hFmB4aNZ7MMTKE7vOztOHJooyyiJSHhgBqCVSG4fP7hr+vz5X8kAPXfasQnChHAmA1zM/m41/7qyo78C4pbqBiMAkkGV0zwNhaR1Fy1hdKx4IGpmrMrm5/sye4MK8dMD3jXooJ1Efv+Sh0HjpvKrN8zcxqvNDPusmIgZBxCN49m4R0dYsldK/v/KOeQJVfd+B6U0/2O8p2JHpd5Y1eaJjbREvFsKI30aQIDAQAB
-```
-
-```txt
-in.ss5.xyz.  TXT  v=spf1 ip4:8.231.54.11 a:in.ss5.xyz include:spf.mailjet.com ~all
-```
+- Cloudflare:API Token,建议只授予目标 Zone 的 DNS Edit 权限;可选 Zone ID。
+- 阿里云 DNS:AccessKeyId 和 AccessKeySecret。
+- 腾讯云 DNSPod:SecretId 和 SecretKey。
 
 
-```txt
-_dmarc.in.ss5.xyz.  TXT  v=DMARC1; p=none; rua=mailto:dmarc@in.ss5.xyz; adkim=s; aspf=s; pct=100
-```
-
-还需要在 IP 服务商控制台设置反向解析:
-
-```txt
-8.231.54.11  PTR  in.ss5.xyz
-```
+域名绑定 DNS 凭据后,点击“一键配置 DNS”会写入或更新:
 
 
-当前检测到 `8.231.54.11` 的 PTR 仍是 `11.54.231.8.bc.googleusercontent.com`。
+- `_mailhub.<domain>` TXT 验证记录。
+- `<selector>._domainkey.<domain>` DKIM TXT。
+- 根域 SPF TXT,并合并为一条 `v=spf1`。
+- `_dmarc.<domain>` DMARC TXT。
+- 发信主机 A 记录。
 
 
-当前 `ss5.xyz` 已检测到两条 SPF,建议合并为:
+PTR 反向解析只检测和提示,通常必须在云服务器或 IP 服务商控制台配置。
 
 
-```txt
-v=spf1 include:spf.mailjet.com include:_netblocks.m.feishu.cn include:spf.ss5.xyz.com include:spf.97admin.com ip4:8.231.54.11 a:ali.ss5.xyz -all
-```
+## 网页化系统设置
 
 
-当前测试发信主机使用 `in.ss5.xyz`,它应当解析到 `8.231.54.11`。二者需要保持对齐
+admin 可在面板中修改以下业务设置:
 
 
-- 如果由当前 Docker 服务器发信,把 `in.ss5.xyz` A 记录和 IP 的 PTR 都指向 `8.231.54.11`。
-- 如果实际由 `39.108.92.239` 发信,把 `.env` 和域名设置里的 `SENDING_IP` 改为 `39.108.92.239`。
+- `APP_BASE_URL`
+- `MAIL_HOSTNAME`
+- `SENDING_IP`
+- `DEFAULT_SPF_MECHANISMS`
+- `DMARC_POLICY`
+- `DMARC_RUA`
+- `SEND_REQUIRES_VERIFIED`
 
 
-PTR 反向解析需要在云厂商或 IP 服务商控制台设置,普通域名 DNS 控制台不能设置。
+基础设施设置仍通过 `.env` 配置并需要重启,例如监听端口、数据目录、SMTP 中继、TLS 证书路径和 `SESSION_SECRET`。
 
 
 ## 发送 API
 ## 发送 API
 
 
+每个用户在面板生成自己的发送 API Token。Token 只在创建时完整显示并自动复制,之后列表只显示前缀。
+
 ```bash
 ```bash
-curl -X POST http://mail-send.ss5.xyz/api/send \
-  -H "Authorization: Bearer <API_TOKEN>" \
+curl -X POST http://mail-send.example.com/api/send \
+  -H "Authorization: Bearer <USER_API_TOKEN>" \
   -H "Content-Type: application/json" \
   -H "Content-Type: application/json" \
   -d '{
   -d '{
-    "from": "noreply@ss5.xyz",
+    "from": "noreply@example.com",
     "to": "user@example.com",
     "to": "user@example.com",
     "subject": "Hello from MailHub",
     "subject": "Hello from MailHub",
-    "text": "Signed with DKIM and queued by Postfix."
+    "text": "Signed with DKIM and queued by MailHub."
   }'
   }'
 ```
 ```
 
 
-SMTP 发信时,`Host` 使用 `in.ss5.xyz`。任意已添加并完成 DNS 配置的发信域名,都可以使用该域名下任意邮箱地址作为 `From`,例如 `notice@example.com`、`billing@example.com`、`noreply@example.com`。本服务会按 `From` 所属域名查找 DKIM 私钥并签名。
+发送时会校验 `From` 所属域名必须归当前 Token 所属用户所有。`.env` 中的 `API_TOKEN` 仅作为旧版 admin 兼容发送 token,新用户应使用网页生成的用户级 Token。
+
+## 测试
+
+```bash
+npm test
+```
+
+测试覆盖:
+
+- 旧 SQLite 数据迁移到 admin 用户。
+- 多用户域名、发送记录、SMTP 凭据、API Token 隔离。
+- SMTP 密码可复制明文与哈希认证。
+- Cloudflare、阿里云 DNS、腾讯云 DNSPod 的 mocked API 写入流程。
 
 
-生产发信前确认:
+## 生产检查
 
 
+- 修改默认 admin 密码并设置足够长的 `SESSION_SECRET`。
+- 不要提交 `.env`、SQLite 数据库、证书私钥或 DNS/API 密钥。
 - 服务器出站 25 端口没有被云厂商拦截。
 - 服务器出站 25 端口没有被云厂商拦截。
 - 服务器入站 `25/465/587/2525` 已在云防火墙和系统防火墙放行。
 - 服务器入站 `25/465/587/2525` 已在云防火墙和系统防火墙放行。
 - SPF、DKIM、DMARC 均通过。
 - SPF、DKIM、DMARC 均通过。