# zgate **Repository Path**: cpsoft13/zgate ## Basic Information - **Project Name**: zgate - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-15 - **Last Updated**: 2026-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # zgate — 四应用单端口边缘网关 zauth / zworker / zagent / zEtl 的统一 `/api/` 入口:一个后端端口,前缀路由 + 剥段反代 + 边缘鉴权。 ## 拓扑(2026-10-15 拍板) ``` 浏览器 └─ nginx :443(唯一前端端口,静态子目录 try_files,nginxWebUI 托管) ├─ 静态:/ /agent/ /etl/ /zauth/console(SPA 路由切换) └─ /api/ → zgate :8888(唯一后端端口,仅回环监听;2026-10-15 收敛为单条 location) ├─ /api/v1/* → zworker-api:8881(直通) ├─ /api/worker/* → zworker-api:8881(剥 /worker/ → /v1/,前端默认 base 别名) ├─ /api/agent/* → zagent:8310(剥 /agent/;白名单模式,仅 7 条 sso 通道) ├─ /api/etl/* → zEtl:8082(剥 /etl/;service 数据面 404 护栏) └─ /api/auth/* → zauth:8300(剥 /auth/ → /v1/,门户 API 归并;service 护栏) ``` ## 职责边界 - **纯传输层反代**:不终止上游各自的业务中间件;Authorization 原样透传(上游保留自身验签作纵深)。 - **边缘鉴权**(`auth.enabled`):zauth JWKS 验签(缓存 + 瞬断续命)→ 应用授权门 (`prefix → app → grant`,zauth service access API + 30s 缓存,fail-closed)→ `X-Zauth-Uid/App/Roles/Dept` 身份头注入。入站先剥离所有 `X-Zauth-*` 防外部直连伪造。 - **公开端点豁免**:登录/换票/JWKS 类端点逐条列在路由 `public` 下(精确匹配,无前缀豁免)。 - **路由级安全门**(先于鉴权,无条件生效):`blocked` 剥段后前缀黑名单(service 数据面 不发布,zauth 拦 `/api/v1/service/`、zEtl 拦 `/api/service/`);`allow` 剥段后精确白名单 (非空=白名单模式,zagent 仅放行 sso 通道 7 条,复刻收敛前 nginx 最小暴露面)。 - **fail-closed 启动校验**:路由表为空、前缀重叠、上游非回环、鉴权开启但缺 JWKS/授权 API、 allow 与 public 互斥、allow 被 blocked 覆盖——任一出现拒绝启动。 - **上游宕 = 该路由 502 JSON**,不影响其它路由;网关自身无状态,重启秒级。 - **observe 灰度**(`auth.observe: true`):验签/授权照常执行并记录决策,但不拦截——用于在切 enforce 前穷尽豁免清单。 ## 路由表(etc/zgate.yaml) | 前缀 | 上游 | 剥段 | 鉴权 | 安全门 | |---|---|---|---|---| | `/api/v1/` | zworker-api:8881 | — | required(5 个公开端点) | — | | `/api/worker/` | zworker-api:8881 | `/worker/`→`/v1/` | required(同上 5 个) | — | | `/api/agent/` | zagent:8310 | `/agent/`→`/` | passthrough(cookie 会话,P3 收敛) | **白名单 7 条** | | `/api/etl/` | zEtl:8082 | `/etl/`→`/` | passthrough(同上) | blocked `/api/service/` | | `/api/auth/` | zauth:8300 | `/auth/`→`/v1/` | required(7 个公开端点) | blocked `/api/v1/service/` | 注意:zauth 的 `/authorize`、`/portal`、`/console` 在内部根路径,不在 `/api/auth/` 覆盖面, 仍走既有 nginx `/zauth/` 位置。 ## 运维 ```bash # 部署(不碰 nginx;复用既有 /api/ 反代规则) PREFIX=/opt/zgate deploy/install.sh # 健康检查与路由内省(本机回环) curl -s 127.0.0.1:8888/healthz curl -s 127.0.0.1:8888/routes # 日志(结构化访问日志 + 上游错误) journalctl -u zgate -f ``` 密钥:`config.env`(chmod 600)注入 `ZAUTH_SERVICE_TOKEN`,须与 zauth 的 `ServiceToken` 一致。 ## 割接与回滚(P1) 1. zworker-api `8888 → 8881`(yaml + systemd,内部端口,nginx 仍打 8888 不变)。 2. zgate 起 `8888`,验证 `/api/v1`、`/api/agent`、`/api/etl` 三链路。 3. 回滚:zworker-api 回 `8888`、停 zgate,分钟级。zworker 内 appgw 包(`AppGw.*Upstream` 置空即停用)为锚点。 ## 生产部署记录(2026-10-15 21:37,woker.xsq-inc.net / 121.199.169.134) P1 已割接完成,验证全绿: - zworker-api 移居 `*:8881`(服务器 yaml 已改,备份 `etc/zworker-api.yaml.bak-pre-zgate`; 端口由 zworker `deploy/config.env` 的 `APP_PORT` 模板渲染驱动,已同步改 8881)。 - zgate 起 `127.0.0.1:8888`(`/opt/zgate`,systemd `zgate.service` 已 enable), observe 模式(验签/授权记录不拦截)。 - 公网核验:`/api/v1/settings/dingtalk/public` 200、`/api/worker/...` 200、 `/api/v1/auth/password/login` 410(正确引导 zauth 统一登录)、四端前端页全 200。 - 回环核验:`/api/agent/x/portal/apps`→8310(401)、`/api/etl/...`→8082、 `/api/auth/auth/dingtalk/config`→8300(200)——五条路由与生产 nginx 逐Prefix映射同义。 **生产实况与设计假设的差异(P2 输入)**:四端跳转发布已将 nginx 改为逐Prefix显式路由 (`/api/v1//api/worker`→8888、`/api/agent/*`→8310、`/api/auth/`→8300 `/api/v1/`、 `/api/etl/`→8082 `/api/`、未知 `/api/` 404),并带 `proxy_cookie_path` 重写 (`/`→`/api/agent/` 等)。故当前经 zgate 的只有 zworker API 两条;其余三应用的 API 仍由 nginx 直连各自后端(不经过 8888,不受本次割接影响)。 **P2 收敛完成(2026-10-15 23:0x)**——三项前置缺口全部闭合,nginx `/api/` 全量收口: 1. **Set-Cookie 路径重写已上线**:zagent/zetl/zauth 三路由配 `cookiePath`, `ModifyResponse` 把上游 `Path=/` 精确改写为公网前缀(仅根路径、保留引号/大小写/空白)。 2. **nginx 收敛为单条 location**:`woker-https.conf` 原 88–201 行共 14 条 `/api/` location (含 7 条 zagent 精确匹配 + 2 条 service 护栏 + 兜底 404)替换为单条 `location /api/ → 127.0.0.1:8888`(proxy_pass 不带 URI,原样透传)。 diff 证实 88 行前/201 行后零改动;备份 `/opt/fas-backup-20261015/nginx/conf.d/woker-https.conf.pre-zgate-collapse`。 注意:**服务器 `/usr/sbin/nginx` 二进制已不存在**(master 靠已删除 inode 运行), `nginx -t`/`-s reload` 均不可用——本次用 `kill -HUP ` 重载(信号机制不依赖二进制; 语法错误时 master 保持旧配置继续服务,为优雅降级)。后续恢复 nginxWebUI 管控需先找回二进制。 3. **zgate 安全门内建收敛语义**:allow 白名单(7 条,1:1 复刻 nginx zagent 最小暴露面)+ blocked 护栏(2 条,service 数据面无条件 404,先于鉴权)。判定空间=剥段后上游路径, 与 nginx location 前缀/精确语义逐位同义(含无尾 slash 不拦边界,已实测 zgate 与 直连上游响应一致)。 收敛后公网核验全绿:`/api/v1`、`/api/worker` 200;`/api/agent/x/portal/apps` 经白名单 透传上游 401(与直连 8310 一致);白名单外/`service/`/未知 `/api/` 均 404 `unknown api`; 前端六页(`/ /worker/ /agent/ /etl/ /auth/ /zauth/`)零回归;zgate access 日志可见 全部公网 API 流量(route/decision 字段完整)。 **下一步(P3/P2-enforce)**: 1. observe 日志穷尽豁免清单后,zworker 两条路由转 `auth.observe: false`(enforce)。 2. 上游双模过渡:见 `X-Zauth-Uid` 信任边缘身份 / 不见走原逻辑(zworker 已具备, zagent/zEtl/zauth 待补)。 3. 各应用自有 session cookie 与 exchange 流程收敛退役(zagent_session/zetl_session 消亡)。 **回滚操作**(分钟级): - zgate 侧:`systemctl stop zgate` + `cp etc/zworker-api.yaml.bak-pre-zgate etc/zworker-api.yaml && systemctl restart zworker-api`(zworker-api 回 8888)。 - nginx 侧:`cp /opt/fas-backup-20261015/nginx/conf.d/woker-https.conf.pre-zgate-collapse /home/nginxWebUI/conf.d/woker-https.conf && kill -HUP `。 ## 开发 ```bash go test ./... # 68 例:配置校验/路由剥段/JWKS 缓存/RS256 验签/授权缓存/安全门/端到端 ``` 仅依赖 `gopkg.in/yaml.v3`;RS256 验签与 JWKS 解析为 stdlib 手写(无 JWT 库,算法白名单只放 RS256)。