Bind Feishu identity to OpenVPN sessions and refine management UI
This commit is contained in:
1 parent
347799cb1e
commit
8fd8c0cd73
27 files changed
+1556
-1011
No files matched your search
+3
-2
@@ -29,7 +29,8 @@ curl --fail http://127.0.0.1:18080/api/health
|
||||
cd /home/ubuntu/opsx-vpn
|
||||
git status --short
|
||||
curl --fail http://127.0.0.1:18080/api/health
|
||||
sudo cp -p opsx-vpn "opsx-vpn.bak-$(date +%Y%m%d-%H%M%S)"
|
||||
sudo install -d -m 700 /tmp/opsx-vpn-backups
|
||||
sudo cp -p opsx-vpn "/tmp/opsx-vpn-backups/opsx-vpn.bak-$(date +%Y%m%d-%H%M%S)"
|
||||
git pull --ff-only origin master
|
||||
cd frontend && npm run build && cd ..
|
||||
go build -ldflags="-s -w" -o opsx-vpn.new ./backend
|
||||
@@ -47,7 +48,7 @@ curl --fail http://127.0.0.1:18080/api/health
|
||||
sudo ss -tlnp | grep ':18080'
|
||||
```
|
||||
|
||||
再检查页面静态资源、`/tmp/opsx-vpn.log` 和原有 VPN 实例状态。若新版本无法启动,停掉新管理进程,复制最近的 `opsx-vpn.bak-*` 到 `opsx-vpn` 并按上面的方式启动;如涉及数据模型变更,先依据对应变更的迁移说明处理数据库,不能直接覆盖 `data/`。
|
||||
再检查页面静态资源、`/tmp/opsx-vpn.log` 和原有 VPN 实例状态。备份只放在 `/tmp/opsx-vpn-backups`,验证成功后仅保留最新两份。若新版本无法启动,停掉新管理进程,复制该目录中最近的 `opsx-vpn.bak-*` 到 `opsx-vpn` 并按上面的方式启动;如涉及数据模型变更,先依据对应变更的迁移说明处理数据库,不能直接覆盖 `data/`。
|
||||
|
||||
## 飞书 / OIDC 登录入口
|
||||
|
||||
|
||||
@@ -93,3 +93,31 @@
|
||||
真实飞书授权及 OpenVPN 建链现已在实例日志中验证。配置页输入 App ID/Secret 后延迟调用已有 HTTPS 管理员查询接口:成功读取企业信息才显示 `tenant:tenant:readonly` 已验证,并自动填入 tenant_key;失败仅展示脱敏错误并允许重试。`contact:user.base:readonly` 是用户授权范围,不能仅靠应用凭据证明;首次成功换取用户资料并校验租户后记录无用户标识的验证时间,换应用凭据或租户时清除,设置页只展示是否曾通过。服务端启动或页面刷新不重复发起 OAuth,也不把手填 tenant_key 当作验证通过。密钥只经 HTTPS 管理接口传输,页面异步请求需忽略被新输入覆盖的旧响应。
|
||||
|
||||
认证组件通过原生 `http.template` 加载每实例的 0600 HTML 页面;页面只提示返回客户端查看连接状态,不在可能复用的终态模板上错误宣称 VPN 已连接。不额外放宽 Nginx 路径。验收:凭据变更后状态清零,查询正确/错误与旧响应均不误报;首次真实登录后状态可见;插件能正常加载模板并保持浏览器认证、OpenVPN 建链。发布时先构建测试,再替换二进制并重启受影响的 OIDC 实例以应用新模板;若插件不兼容模板,回退之前的配置与二进制、重启实例,旧认证仍可使用。数据库新列由 AutoMigrate 添加,回退版本忽略该列。
|
||||
|
||||
## 12. 在线会话的未知身份(S1)
|
||||
|
||||
OpenVPN `status-version 3` 的第 1 列 Common Name 与第 9 列 Username,在无客户端证书的 WebAuth 首次连接时都可能为 `UNDEF`。目前会话同步把占位字符串当作拨号用户名,误写入在线会话及审计。优先使用有效 Username,其次使用有效证书 CN;两列均为 `UNDEF` 时留空,并在在线会话及连接审计页面明确显示“身份未识别”。保持原流量快照键以避免部署后的首次轮询重复计数;不根据公网 IP、姓名或认证时间猜测身份,更不把“LG”这样的显示名自动绑定 VPNUser。已有历史日志保留原值,仅页面标记其身份未识别。
|
||||
|
||||
验收:密码与证书连接仍展示其用户名/CN;无身份的 WebAuth 状态行不会记录 `UNDEF` 为 VPNUser,也不会误关联飞书人员;在线会话保留连接地址与流量。若同一连接随后获得可信 Username,沿用此前 `UNDEF` 流量快照计算增量,不把旧流量重复累计;单元测试覆盖未知身份、重复轮询和后续获得稳定 subject 的过程。真正按人展示还需验证 `openvpn-auth-oauth2` 是否能向 OpenVPN 提供带稳定 subject 的 Username;如插件不传递,须在认证阶段增加可信映射,不能用会话 IP 推断。此修复不改变认证权限或 Nginx;发布前运行 OpenVPN 单元测试及前端构建,发布后检查真实状态文件第 1/9 列与在线会话,失败则恢复旧管理二进制,保留数据库和专线进程。
|
||||
|
||||
## 13. 授权身份传递与回调结果页(S2)
|
||||
|
||||
现有 `fetchFeishuUser` 从飞书 `/authen/v1/user_info` 读取 `open_id`、`tenant_key`、`name`,`/id/userinfo` 输出稳定 `sub` 与 `name`;但 ID Token 仅包含 `sub`。浏览器 `/oauth2/callback` 是认证组件消费授权码的端点,地址栏的 code 不能代表用户资料。将已获取的非空姓名作为标准 OIDC `name` claim 加入 ID Token;`sub` 继续由 `tenant_key:open_id` 组成,不能以可变姓名替代。结果模板改为简洁蓝白灰页面,继续保持结果中性,不能输出令牌、姓名或根据地址栏宣称 VPN 已连通。
|
||||
|
||||
FR-13-01:模拟飞书换码与用户信息返回时,服务端传递稳定标识与姓名,签发的 ID Token 在非空时包含 `name`,`/id/userinfo` 保留原字段;空姓名不写入空 claim。FR-13-02:同一 0600 模板通过 `http.template` 用于回调终态,无论认证结果如何都只提示切回客户端检查状态。
|
||||
|
||||
AC-13-01:模拟上游含 `open_id`、`tenant_key`、`name` 的响应,验签并检查 ID Token 的 `sub`/`name`,验证名称为空时不增加 claim;运行身份模块测试。AC-13-02:读取生成的模板,核对页面标题、移动端视口及不出现回调参数/用户资料;运行 OpenVPN 模块测试。真实账号授权和 OpenVPN 状态第 1/9 列另需在线联调,单元模拟不等同于线上已显示真人姓名。
|
||||
|
||||
风险:姓名属于个人信息,只放入面向已认证机密客户端的短时 ID Token 与现有 UserInfo,不写浏览器结果页或日志;上游未提供姓名时维持稳定 `sub`。兼容性:已有依赖 `sub` 的客户端行为不变;不增公网路由或数据库列。发布先构建并验证,再部署管理二进制、重启受影响的 OIDC 专线以生成新模板,检查 HTTPS 认证流程;失败回退二进制并重启该专线,不删除身份数据或签名密钥。
|
||||
|
||||
## 14. WebAuth 身份进入 OpenVPN 会话(S3,待组件核实)
|
||||
|
||||
现状证据:飞书回调读取 `tenant_key`、`open_id`、`name`,受限 OIDC Token 返回稳定 `sub`;实例配置 `openvpn.auth-token-user: true`,但服务端 `status-version 3` 的 Username 和 CN 仍可能都是 `UNDEF`。`auth-token-user` 是推送给客户端以后重新认证使用的用户名,不能证明服务端首次连接状态已绑定 OIDC subject。会话同步只信任 OpenVPN 状态文件,不应按公网 IP、浏览器回调时间或显示名匹配人。旧版代码把 `UNDEF` 直接写入审计,本地 S1 修正正在避免新增这类伪用户;历史审计保持原样。
|
||||
|
||||
目标链路:飞书应用确认用户和租户 → OIDC 使用 `tenant_key:open_id` 作为稳定 subject、姓名只用于显示 → 认证组件验证令牌与 pending CID/KID 的绑定 → 在同一条管理接口 `client-auth` 的客户端配置中设置 `override-username <稳定 subject>` → OpenVPN 状态文件 Username 出现该 subject → 平台在线会话与审计使用这个 subject。OpenVPN 本地源码文档 `doc/man-sections/server-options.rst` 明确 `override-username` 会被状态输出采用;但须先核实当前 `openvpn-auth-oauth2` 版本是否支持下发此配置,若不支持,应修改/封装认证组件并在集成测试验证,不能在 `server.conf` 中静态设置一个共用用户名。将来展示飞书姓名需按 subject 保存受控身份资料或提供受限查询,不得将姓名当成授权键或自动关联同名 VPNUser。
|
||||
|
||||
FR-14-01:每个成功认证的 OpenVPN CID/KID 必须绑定经验证的非空稳定 subject;缺失身份则拒绝授权,不得以 `UNDEF`、IP 或可变姓名补全。FR-14-02:状态文件的 Username 必须与该 subject 一致,平台在线列表和连接/断开审计使用该值;密码和证书专线保留原行为。FR-14-03:部署不得扩大 Nginx、管理 socket 或飞书资料接口的权限;失败时能回退认证组件与配置。
|
||||
|
||||
AC-14-01:模拟两个并发 WebAuth 请求及其不同 CID/KID,证明不能交叉赋值,令牌验证失败、租户不符和空 subject 均拒绝;检查管理命令仅含安全编码的 subject。AC-14-02:测试专线真实授权后读取 `openvpn-status.log` 的 CN/Username、在线会话和连接/断开审计,三处能追溯同一稳定 subject;重复认证和重连无误绑定。AC-14-03:测试旧密码/证书连接、认证失败、组件崩溃恢复,并检查失败时无匿名会话被错误归到已认证用户。
|
||||
|
||||
待决:当前运行的 `openvpn-auth-oauth2` 对 `client-auth` 配置和令牌 `sub` 的实际处理,以及线上状态文件原始列值;在没有组件源码/运行输出验证前,不声明此节已实现。发布顺序为先验证并编译组件、测试专线联调,再更新管理服务及组件,最后观察线上状态与审计;回退为恢复先前组件及配置并重启测试专线,保留密钥、身份数据和历史审计。
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
风险:菜单 meta 遗漏导致入口消失;颜色对比、窄屏表格或图表实例切换出错;登录错误处理误跳转;大文件 CSV 进入浏览器内存。验收:所有原有入口均可从对应角色看到,子页可返回专线;中文/英文页面可构建;桌面与小屏下菜单和操作按钮可点;登录 401 留在登录表单,业务 401 只触发一次跳转;接口错误时有重试且不伪造数据;日志导出请求 URL 不含 token,HTTP Authorization 携带凭据并成功下载 CSV。旧版后端接口保持兼容。提交前执行前端构建与 `git diff --check`,生产发布需另行确认可访问性与当前在线 VPN 连接;回退只需切回上一版前端嵌入二进制。
|
||||
|
||||
## 后续拆分点
|
||||
## 第二轮:页面拆分与发布
|
||||
|
||||
`Settings.vue`、`Tunnels.vue`、`VPNUsers.vue` 目前超过指南的 400 行视图上限。下一轮按业务边界分别将身份/网关设置、专线详情/日志、拨号用户的 MFA 弹层移至页面私有组件和 composable。避免本次视觉统一同时改变这些敏感业务表单的状态及 API 调用顺序。
|
||||
`Settings.vue` 的飞书身份与公网入口各自负责原有请求、加载状态及校验;`Tunnels.vue` 的专线详情(流量策略、WireGuard Peer、二维码)与实时日志各自管理原有弹层;`VPNUsers.vue` 的 MFA 管理弹层负责原有绑定、PIN、解锁与备用码操作,操作完成通知父页刷新列表。路由、接口 URL、鉴权头、表单字段和权限入口不变,页面私有组件放在各自的 `views/<域>/components/`。实时日志在关闭、切换和组件销毁时结束请求,以免后台持续读取。
|
||||
|
||||
风险:拆分状态后子组件遗漏初始化、刷新或清理;身份凭据与 MFA 的错误处理不能退化;二维码仍需在列表与详情两处可用。验收:前端构建、差异检查;管理端登录后验证设置页四个标签、专线详情/日志及拨号用户 MFA 操作;服务器确认 HTTPS 健康、OIDC 发现文档、Nginx 配置与原有 VPN 进程。生产发布只替换管理服务的内嵌前端二进制,不重启专线进程;先在 `/tmp/opsx-vpn-backups` 保留旧二进制,最多留两份。若健康检查或静态资源异常,恢复旧二进制并重启管理进程;保留现有数据库、证书和专线配置。
|
||||
@@ -0,0 +1,55 @@
|
||||
# SDD-009:飞书认证会话身份
|
||||
|
||||
| 字段 | 内容 |
|
||||
|---|---|
|
||||
| 版本 / 变更级别 | 0.1 / S3 |
|
||||
| 设计状态 | 已形成 |
|
||||
| 实现状态 | 未开始 |
|
||||
| 负责人 / 日期 | Codex / 2026-10-22 |
|
||||
| 关联需求 | 飞书登录后在线会话与连接审计显示具体用户 |
|
||||
| 批准状态 | 用户授权排查修复;尚未发布 |
|
||||
|
||||
## 1. 背景、目标与范围
|
||||
|
||||
线上 2.3.0 认证组件日志显示授权成功且 sub 非空,但 user_username 为空;OpenVPN 状态中 CN、Username 均为 UNDEF。`identityprovider/handlers.go` 已从飞书 user_info 获取 tenant_key、open_id、name,现有短期 grant 未形成持久姓名关联。`openvpn/oidc.go` 没有配置用户名表达式。
|
||||
|
||||
目标:内置飞书专线以已验证的 sub 传递用户身份,并展示飞书姓名。保留外部 OIDC、密码、证书模式和现有用户权限;不按 IP 或时间推断历史身份,不自动创建或授权 VPNUser,不查询整个企业通讯录。
|
||||
|
||||
## 2. 需求与约束
|
||||
|
||||
- FR-01:内置飞书认证组件使用 token.claims.sub 作为用户名来源,外部配置保持原行为。
|
||||
- FR-02:仅成功获取飞书资料并通过企业校验后,保存应用、issuer、subject、姓名和更新时间;不保存上游访问令牌。
|
||||
- FR-03:在线会话与连接审计以稳定 subject 为身份,额外展示同应用同 issuer 的飞书姓名。未知身份保持未知;同名用户不合并。
|
||||
- NFR-01:姓名仅用于展示,不参与权限判断;资料写入失败时不发放本次授权码;已有记录不回填猜测身份。
|
||||
|
||||
## 3. 架构与方案
|
||||
|
||||
回调校验企业后 upsert 独立身份资料表;内置专线生成明确 CEL 用户名表达式。会话采集按当前内置配置及 subject 查资料,保存展示姓名快照;连接审计同步保存姓名快照。前端显示姓名与稳定标识,旧 UNDEF 显示未知。
|
||||
|
||||
## 4. 接口、数据与配置契约
|
||||
|
||||
新增身份资料表,issuer、app_id、subject 为联合唯一键;VPNSession 与 ConnectionLog 增加可选 display_name。现有 username 字段语义不变。自动迁移仅新增表与列。内置组件配置增加 oauth2.openvpn-username,要求支持该选项(线上 2.3.0 已通过 --help 核对);外部 OIDC 不增加此配置。
|
||||
|
||||
## 5. 风险与验收
|
||||
|
||||
| 风险 | 防护 / 恢复 | 可观察信号 |
|
||||
|---|---|---|
|
||||
| 姓名或应用混淆 | 同 issuer、应用、subject 精确查询;姓名非身份键 | 同名、跨应用测试 |
|
||||
| 新组件配置不兼容 | 发布前核对组件参数,恢复原配置 | 启动日志与监听 |
|
||||
| 旧连接仍无身份 | 用户重新发起 WebAuth;不关联旧 UNDEF | 状态文件用户名 |
|
||||
| 数据写入失败 | 本次回调 503,不发码 | 负向测试 |
|
||||
|
||||
| AC | 对应需求 | 操作与预期 | 验证方式 | 结果 |
|
||||
|---|---|---|---|---|
|
||||
| AC-01 | FR-01 | 内置生成 sub 表达式,外部不改变 | Go 测试 | 未执行 |
|
||||
| AC-02 | FR-02、NFR-01 | 保存及更新同一身份,应用隔离;不保存令牌 | Go 测试 | 未执行 |
|
||||
| AC-03 | FR-03 | 会话与审计保留 subject 并显示姓名,未知无误归属 | Go 测试、前端构建 | 未执行 |
|
||||
| AC-04 | FR-01、FR-03 | 真实用户重连后状态用户名非 UNDEF,后台显示姓名 | 远程联调 | 未执行 |
|
||||
|
||||
## 6. 发布与恢复
|
||||
|
||||
先完成测试及前端构建,备份旧二进制及认证配置,再更新管理二进制;不覆盖数据库或密钥。认证配置在专线下次启动时生成;现有连接不自动中断,重启测试专线须明确告知影响。验证健康、认证监听、实际重连和会话展示。失败恢复旧二进制、旧认证配置及原启动方式;新增表列可保留,旧版忽略,不做删除迁移。没有不可逆删除。
|
||||
|
||||
## 7. 待决问题
|
||||
|
||||
真实用户重连需用户本人完成飞书认证;不阻塞本地实现,阻塞端到端验收。历史 UNDEF 无可信身份关联,保持原始记录。
|
||||
Reference in new issue
Block a user