安全自审计
这是一份 maintainer 自审计,面向正在判断是否把 Codex Quota Monitor 接到敏感
CLIProxyAPI 和 Codex OAuth 状态上的用户。它不是第三方审计,也不是“部署后
没有风险”的保证;它说明 monitor 会读什么、数据可能到哪里、项目承诺不持久化
或暴露什么,以及用户如何本地复现检查。
如果需要更结构化的资产、边界和 abuse path 视图,见 威胁模型。
威胁模型
monitor 面向可信 operator,在已有 CPA 部署附近运行自托管 dashboard。这是运维型安全 模型,不是公网 SaaS 安全模型:项目依赖本地监听、最小权限运行时、secret 脱敏和 共享 dashboard 时的外部鉴权访问层。它不实现内置用户登录、RBAC、多租户隔离或用户 访问审计日志。核心资产是:
- CPA management key。
- 本地 Codex OAuth auth 文件和其中的
access_token。 - 账号标签、plan type、quota window、请求 / model 活动和 gateway health 元数据。
- 可选 SQLite 历史和 benchmark summary。
纳入范围:
- 浏览器 UI、只读 HTTP API、Prometheus metrics、日志、SQLite history 和 benchmark 输出造成的意外暴露。
- Docker、NixOS 或 reverse proxy 部署时权限给得过宽。
- 回归改动意外序列化 key 或 token 值。
不纳入范围:
- 恶意 root 用户或已被攻陷的主机。
- 已被攻陷的 Python runtime、container runtime、CPA 进程、浏览器或 reverse proxy。
- 没有认证访问层就把 dashboard 直接暴露到公网或 LAN。
- 上游 CPA 或 provider API 的 bug / policy 行为。
数据流
| 输入 | 用途 | 目的地 | 持久化 |
|---|---|---|---|
managementBaseUrl |
读取 CPA management endpoint 中的 auth inventory、usage、routing、logs 和 health。 | operator 配置的本地 CPA endpoint。 | 派生出的状态和计数可能进入 API/UI/history。 |
| CPA management key | 给 CPA management 请求加 Authorization: Bearer <key>;旧的 manual_quota_refresh_auth = "management-key" 部署也用它鉴权受保护的手动 direct quota refresh。 |
operator 配置的 CPA management URL,以及旧模式下的入站 /api/v1/quota/refresh 校验。 |
key 值不应进入 API/UI/history/metrics/logs。浏览器保存默认关闭,只在旧的 management-key refresh 模式且启用 allow_browser_key_storage 后可用。 |
| 手动刷新 UI token | 鉴权默认浏览器 POST /api/v1/quota/refresh 动作,避免暴露 CPA management key。 |
页面 bootstrap 和入站 /api/v1/quota/refresh 校验。 |
进程启动时在内存中生成,只嵌入返回的 HTML 页面;不会由 print-config、API JSON、history、metrics 或日志打印。 |
| Codex auth 文件 | 可选 direct quota sampling,从 auth 文件读取 access_token 和 account id。 |
https://chatgpt.com/backend-api/wham/usage。 |
token 值不应进入 API/UI/history/metrics/logs。 |
| SQLite state DB | 保存 dashboard snapshot 派生出的 Trends、ETA、Audit 和 Stats 时间序列输入。 | --state-db 或 NixOS module 配置的本地路径。 |
账号标签、状态、quota 和 usage 元数据会被持久化。 |
| Benchmark summary | 在 Trends 中展示校准结果。 | 本地 summary.json 路径。 |
summary 字段会显示在 dashboard 中。 |
HTTP API 基本是只读的,但它不是鉴权边界。Host 和 Origin 检查能减少误连或跨源暴露,
但不会识别用户,也不能替代登录或 access layer。默认 POST /api/v1/quota/refresh
会校验页面内 refresh token 后才排队触发 provider-facing refresh,因此浏览器用户不需要
CPA management key。设置 manual_quota_refresh_auth = "none" 会移除这层动作校验,只适合可信本地部署。即使 secret value 已脱敏,也应把 /api/v1/status、/api/v1/stats、
/api/v1/recommendations、/api/v1/diagnostics、/api/v1/alerts 和 /metrics
当作敏感运维元数据。LAN 或 reverse-proxy 部署必须通过 --allowed-host 或 NixOS
module 的 allowedHosts 选项明确列出浏览器访问用的域名/IP。
安全声明
这些声明应当能用仓库里的测试和验证命令复现:
- 默认 service exposure 是 loopback-first。Python 和 Nix 默认使用
127.0.0.1:4515;NixOS module 保持openFirewall = false;HTTP request 带非预期Host或Origin时会被拒绝,但这不是用户鉴权。 - Docker 示例发布
127.0.0.1:4515:4515,drop Linux capabilities,使用--read-only、--security-opt=no-new-privileges,并且只挂载 management key 文件、auth-files 目录和/data。 - CPA management key 配置后从文件读取,只作为 CPA 请求 header 使用;默认手动 quota refresh
endpoint 改用页面内 UI token,旧的
management-key模式才会复用 CPA key。 - Direct Codex quota sampling 在本地读取 auth JSON,只把 provider usage 请求所需 bearer token 发给 provider;token 不是公开 API schema 的一部分。
print-config打印解析后的非敏感配置;doctor --json报告访问和风险检查, 不打印 secret 内容。- SQLite history 保存运维元数据,而不是 management key 或 auth token 值。
- Release image 从 tag source 构建,带 SBOM 和 provenance attestation,并通过
GitHub OIDC 使用 keyless
cosign签名。
本地验证
信任某个改动前,先跑常规测试:
nix develop -c python3 -m unittest discover -s tests -v
nix develop -c python3 -m build
确认 resolved config 和 doctor 输出不会打印 secret 值:
codex-quota-monitor print-config \
--management-base-url http://127.0.0.1:8317 \
--management-key-file /path/to/management-key \
--gateway-health-url http://127.0.0.1:8317/healthz \
--state-db off
codex-quota-monitor doctor --json \
--management-base-url http://127.0.0.1:8317 \
--management-key-file /path/to/management-key \
--gateway-health-url http://127.0.0.1:8317/healthz \
--auth-dir /path/to/auth-files \
--state-db off
启动服务后,用只存在于测试 key 或 auth 文件里的精确 secret 字符串检查只读输出:
secret='replace-with-a-test-only-secret-string'
curl -fsS http://127.0.0.1:4515/api/v1/status > /tmp/cqm-status.json
curl -fsS http://127.0.0.1:4515/api/v1/recommendations > /tmp/cqm-recommendations.json
curl -fsS http://127.0.0.1:4515/api/v1/diagnostics > /tmp/cqm-diagnostics.json
curl -fsS http://127.0.0.1:4515/api/v1/alerts > /tmp/cqm-alerts.json
curl -fsS http://127.0.0.1:4515/metrics > /tmp/cqm-metrics.txt
grep -R --fixed-strings "$secret" /tmp/cqm-status.json /tmp/cqm-recommendations.json /tmp/cqm-diagnostics.json /tmp/cqm-alerts.json /tmp/cqm-metrics.txt
grep 应该没有任何匹配。这个检查要用一次性测试 secret;不要把真实 auth token
或 management key 粘到 issue 里。
共享 support artifact 前,生成脱敏副本,不要直接改原始文件:
codex-quota-monitor redact /tmp/cqm-status.json --secret "$secret" >/tmp/cqm-status-redacted.json
codex-quota-monitor redact /path/to/history.sqlite3 --output /tmp/history-redacted.sqlite3 --secret "$secret"
codex-quota-monitor redact /path/to/benchmark/summary.json --output /tmp/summary-redacted.json --secret "$secret"
如果已经把 support artifact 复制到一个 staging 目录,先跑 dry-run summary。目录和多路径
脱敏只有在传入 --apply 时才会改写文件:
codex-quota-monitor redact /tmp/cqm-support --secret "$secret"
codex-quota-monitor redact /tmp/cqm-support --secret "$secret" --apply
dry-run JSON 会报告有多少受支持的 JSON、JSONL、CSV、text/Markdown 和 SQLite
文件会变化。只在复制出来的 support artifact 上使用 --apply,不要对正在使用的 auth
目录、live history database 或仍在写入的 benchmark directory 原地执行。
新用户部署规则
- 评估阶段保持 monitor 监听
127.0.0.1。 - 绑定到 loopback 之外时,只用
--allowed-host或services.codexQuotaMonitor.allowedHosts放行浏览器实际访问的域名/IP。 - 使用
--management-key-file;避免把 inline key 留在 shell history 或 process listing 里。 - 只挂载 monitor 需要的具体 key 文件和 auth-files 目录。除非明确启用 auth live plan sync,否则 auth files 保持只读;启用时也只让同步 runner 可写。
- 容器部署时,只让
/data或配置的 SQLite 路径成为可写持久路径。 - 不要挂载整个 home、
.config、password store 或无关 secret 目录。 - 如果需要 LAN 或公网访问,把 monitor 放在有鉴权的 reverse proxy 或 access layer 后面;不要直接暴露 monitor API。
- 把 dashboard 的
Audit标签页理解为已存账号池快照的运维 diff,不要当成用户登录 或访问审计日志。 - support bundle、截图、日志、
summary.json和 SQLite history 都应视为敏感,除非 已经脱敏账号标签和运维元数据。
残余风险
monitor 按设计会接触敏感材料。能读取 monitor 进程内存、auth 目录、management key 文件,或能控制配置的 CPA / proxy endpoint 的本地攻击者,可以窃取 secret。即使没有 原始 token,暴露的 dashboard endpoint 仍可能泄露账号标签、plan 档位、quota 时间、 最近 model 活动和服务健康。保持 runtime 本地化和最小权限,并把 API、metrics、 history、benchmark 或日志里的每一个新增字段都当作 privacy change 复查。