webmail-sso.md 6.1 KB

Roundcube 一键登录

MailHub 的“一键登录 Webmail”使用两层短期凭据,不会向浏览器、URL 或 Roundcube 日志暴露邮箱原密码:

  1. MailHub 在已登录的同源页面为指定邮箱签发 60 秒、仅可使用一次的启动票据。
  2. 浏览器通过隐藏表单把 mailhub_ticket POSTWEBMAIL_SSO_URL,票据不会进入查询字符串或 Referer。
  3. mailhub_sso 插件在服务器端用共享密钥调用 MailHub 的 /internal/webmail-sso/exchange,取得邮箱地址和 mhw_ 临时凭据。
  4. Roundcube 使用该凭据连接固定的 IMAP/SMTP 服务;成功后固定进入 INBOX。退出或销毁 Roundcube 会话时,插件会尽力调用 /internal/webmail-sso/revoke,凭据自身到期仍是最终边界。

同一用户每分钟最多签发 20 个启动票据,同一邮箱最多保留 5 个有效 Webmail 临时会话;超过上限时会淘汰最久未使用的会话。过期或撤销记录会机会式清理,避免 SQLite 表无界增长。

MailHub 配置

.env 中设置:

WEBMAIL_SSO_URL=https://mail.us.ss5.xyz/
WEBMAIL_SSO_SECRET_FILE=./data/secrets/webmail_sso_secret
WEBMAIL_SSO_TICKET_TTL_SECONDS=60
WEBMAIL_SSO_CREDENTIAL_TTL_SECONDS=43200

WEBMAIL_SSO_URL 留空时功能关闭,便于未配置 Roundcube 的部署继续运行;一旦设置 URL,Secret 文件必须存在且有效,否则应用会快速失败。Compose 固定从 /data/secrets/webmail_sso_secret 读取(宿主机对应 ./data/secrets/webmail_sso_secret),功能关闭时不会因为旧部署缺少该文件而阻止容器升级。WEBMAIL_SSO_URL 必须是固定 HTTPS 起始地址。MailHub 会把它规范化成绝对 origin(例如 https://mail.us.ss5.xyz)并写入票据;Roundcube 的 mailhub_sso_audience 必须完全一致。

Secret 只接受 64–512 个十六进制字符。首次部署前可直接运行 npm run prepare:dovecot;也可以手工生成:

install -d -m 0700 data/secrets
openssl rand -hex 32 > data/secrets/webmail_sso_secret
chown 1000:1000 data/secrets/webmail_sso_secret
chmod 0440 data/secrets/webmail_sso_secret

同机 Roundcube 建议使用 04401000:1000,并仅把 Roundcube PHP 进程加入补充组 1000;不要改成全局可读。MailHub 的准备脚本会自动维持该权限。若 Roundcube 在另一台机器,复制相同内容到仅由其 PHP 运行用户可读的独立文件即可。

该 Secret 不能复用 Session、Dovecot、Token 或其他业务密钥,也不能提交到 Git。

Roundcube 1.6.x 安装

Roundcube PHP 运行环境需要启用 cURL 扩展。

  1. docker/roundcube/plugins/mailhub_sso 复制到 Roundcube 的 plugins/mailhub_sso
  2. plugins/mailhub_sso/config.inc.php.dist 复制为 plugins/mailhub_sso/config.inc.php,按实际网络修改内部地址;不要把 Secret 内容写进 PHP 配置。
  3. mailhub_sso 加入 Roundcube $config['plugins']。可参考 docker/roundcube/config.inc.php.example
  4. 将与 MailHub 完全相同的 Secret 只读挂载到 Roundcube 的 /run/secrets/webmail_sso_secret;同机容器为 PHP 用户增加补充组 1000,确保它能读取 0440 文件。
  5. 保持外部 Webmail 全程 HTTPS,并把 mailhub_sso_audience 设置为精确 origin,不带路径和结尾斜杠。

若 HTTPS 终止在 Roundcube 前方的反向代理,将 Roundcube 的 use_https 设为 true;不要同时启用与它互斥的 force_https

生产示例:

$config['plugins'][] = 'mailhub_sso';
$config['imap_host'] = 'ssl://in.ss5.xyz:993';
$config['smtp_host'] = 'ssl://in.ss5.xyz:465';
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';

$config['mailhub_sso_internal_base_url'] = 'http://app:3000';
$config['mailhub_sso_audience'] = 'https://mail.us.ss5.xyz';
$config['mailhub_sso_secret_file'] = '/run/secrets/webmail_sso_secret';
$config['mailhub_sso_imap_host'] = 'ssl://in.ss5.xyz:993';

网络拓扑

本仓库的 Compose 不运行 Roundcube。可按现有部署选择以下方式:

  • Roundcube 容器与 MailHub 同机:让 Roundcube 加入 mailhub Compose 网络,使用 http://app:3000,并把宿主机同一个 Secret 文件以只读方式挂入两个容器。
  • Roundcube 运行在宿主机:使用仅回环监听的 http://127.0.0.1:3025
  • Roundcube 在另一台机器:通过 WireGuard、SSH 隧道或等价私网访问。不得把 /internal/webmail-sso/* 无保护地公开到互联网。

Roundcube 使用独立 Compose 时,可将现有 MailHub 网络声明为 external;实际网络名以 docker network ls 为准:

services:
  roundcube:
    group_add:
      - "1000"
    volumes:
      - /absolute/path/to/mailhub/data/secrets/webmail_sso_secret:/run/secrets/webmail_sso_secret:ro
    networks:
      - mailhub

networks:
  mailhub:
    external: true
    name: <mailhub-compose-project>_mailhub

同时在对外反向代理中拒绝公网访问 /internal/webmail-sso/;Bearer Secret 是内网接口的第二层校验,不应代替网络隔离。

内部接口只接受 Authorization: Bearer <shared-secret> 和 JSON:

POST /internal/webmail-sso/exchange  { ticket, audience }
POST /internal/webmail-sso/revoke    { credential, audience }

插件不跟随 HTTP 重定向,不接受 GET 票据,不允许浏览器指定 IMAP 主机,也不记录内部响应、票据、临时凭据或共享密钥。仅有收信权限的临时凭据可以进入 Roundcube,但 SMTP 认证应由 MailHub 拒绝;有发信权限时 Roundcube 才能发信。

被分配用户通过 Webmail 登录时,Dovecot 使用固定 ACL 组将 IMAP 权限限制为列出、读取和更新 \Seen;不能删除/移动邮件、EXPUNGE,也不能创建、删除或重命名文件夹。邮箱所有者保持完整 IMAP 权限。撤销收信权限、停用用户或临时凭据到期后,新的 IMAP 认证会立即失败;已经建立的 IMAP TCP 连接会在连接关闭后生效,Roundcube 的典型按请求连接使该窗口通常很短,但它不是逐条 IMAP 命令的强制踢线。