# Roundcube 一键登录 MailHub 的“一键登录 Webmail”使用两层短期凭据,不会向浏览器、URL 或 Roundcube 日志暴露邮箱原密码: 1. MailHub 在已登录的同源页面为指定邮箱签发 60 秒、仅可使用一次的启动票据。 2. 浏览器通过隐藏表单把 `mailhub_ticket` **POST** 到 `WEBMAIL_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` 中设置: ```dotenv 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`;也可以手工生成: ```bash 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 建议使用 `0440`、`1000: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`](../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`](../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`。 生产示例: ```php $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` 为准: ```yaml 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 ``` 同时在对外反向代理中拒绝公网访问 `/internal/webmail-sso/`;Bearer Secret 是内网接口的第二层校验,不应代替网络隔离。 内部接口只接受 `Authorization: Bearer ` 和 JSON: ```text 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 命令的强制踢线。