用户终端 ←──TLS 1.3──→ Server A (Caddy) ←──VLESS+Reality──→ Server B ←──TLS──→ AI API
全程加密 防火墙+fail2ban 全程加密 HTTPS
所有数据传输均使用加密通道,无明文暴露。
| 措施 | 说明 |
|---|---|
| VLESS+Reality 隧道 | 流量伪装为正常 HTTPS,DPI 不可区分 |
| TLS 1.2/1.3 | Caddy 配置 TLS 1.2 及 TLS 1.3,兼顾兼容性与安全性 |
| 防火墙 | 默认拒绝所有入站,仅开放必要端口 |
| 域名白名单 | Xray 路由规则限制出站域名 |
| 流媒体封锁 | 明确拒绝 Netflix/YouTube 等域名 |
| 措施 | 说明 |
|---|---|
| SSH 加固 | 禁密码登录、禁 root 直连、自定义端口 |
| fail2ban | 暴力破解自动封禁(SSH: 3次/24h) |
| 内核加固 | SYN cookies、ASLR、禁 ICMP 重定向 |
| BBR | 拥塞控制优化 + SYN flood 防护 |
| 自动更新 | 安全补丁自动安装 |
| rkhunter | 每周 rootkit 扫描 |
| Lynis | 综合安全审计,目标 hardening index ≥ 65 |
| 措施 | 说明 |
|---|---|
| API Key 隔离 | 用户使用内部 Key,不接触上游 API Key |
| 配额管理 | 每用户独立配额,防止滥用 |
| 审计日志 | New API 记录所有 API 调用 |
| 容器隔离 | New API 运行在 Docker 中 |
本工具涉及跨境网络服务,使用前请了解以下法律法规:
- 《计算机信息网络国际联网管理暂行规定》第六条:使用国际联网须经国际出入口信道提供单位。
- 《网络安全法》第二十七条:不得提供专门用于从事侵入网络等违法犯罪活动的工具。
- 工信部 2017 年通知:未经批准不得自行建立或租用国际专线。
- 企业内部使用:仅限公司员工,不对外提供服务
- 白名单限制:仅允许 AI API 域名,拒绝一般浏览
- 合理流量模式:避免 24/7 高带宽,保持正常业务流量模式
- 伪装合规:Server A 展示正常企业网站
- 审计记录:保留操作日志,证明合法使用场景
- 不收费:不以此服务收取费用(避免非法经营风险)
如果处理敏感数据(如律所客户文件),请注意:
- API 调用内容会通过隧道传输到海外 AI 服务商
- 这可能触发《数据出境安全评估办法》的相关要求
- 建议对传输内容进行脱敏处理
- 咨询法律顾问了解具体合规要求
Bifrost 支持三种暴露面 profile,并默认采用 vpn-first:
| Profile | 适用场景 | 安全要求 |
|---|---|---|
vpn-first |
生产默认 | 管理面只允许 VPN、私网或来源白名单访问;公网仅保留业务 API 与伪装站 |
public-managed |
兼容需要公网管理入口的部署 | 必须配合强认证、WAF/来源白名单、限速、审计日志和云防火墙 |
lab |
临时测试 | 可放宽限制,但不得用于生产 |
生产部署应验证以下暴露面:
/v1/*可以作为 OpenAI-compatible API 公网入口。/dashboard、/login、New API 前端静态资源(如/static/*、/logo.png)以及/manage/*在vpn-first下必须从公网返回拒绝访问;白名单/VPN 来源访问管理 UI 时,这些静态资源必须同样反代到 New API,避免入口页面可达但 JS/CSS 不可用。- Server B 的 3x-ui 直接端口默认不得开放;
/xui-panel/在vpn-first下必须受 VPN/私网/来源白名单保护。 New API镜像应使用固定版本或 digest;latest只能在lab或显式BIFROST_ALLOW_UNPINNED=1时临时使用。
分发栈启用后,Server B 同时承载 Verdaccio、NewAPI、PostgreSQL、Redis、git mirror、静态 files 和 restic 备份任务。它的安全边界必须按“公网最小入口 + wg0 私网服务 + 专用低权只读通道”执行。
| 面 | 允许 | 禁止 |
|---|---|---|
| Server B 公网入站 | 22/tcp 双通道维护入口、配置的 WireGuard UDP 端口、既有 Xray Reality 端口 |
3000/4873/8081/8082 从公网直接访问 |
| Server B wg0 入站 | A 和白名单 peer 访问 3000/4873/8081/8082 |
团队成员绕过 A 直连 B 的服务端口 |
| Docker 端口 | 绑定 10.8.0.2:<port>,并用 DOCKER-USER drop 公网 eth0 |
0.0.0.0:<port> 或仅依赖 nftables 主链 |
| bifrost-api 日志读取 | bifrost-readonly 专用用户 + forced-command 白名单 |
root SSH 私钥、任意命令 SSH、交互 shell |
| Verdaccio bootstrap | 密码只 stdout 一次,并保存 /root/.verdaccio-bootstrap-pwd.txt 0400 |
写入 deploy state、日志、bifrost-api 响应 |
| Git mirror | Caddy 拒绝 git-receive-pack,只允许 clone/fetch |
允许 push 到镜像源 |
生产变更后必须运行:
bash scripts/diagnostics.sh --check distribution
iptables -S DOCKER-USER | grep -E '3000|4873|8081|8082'
nft list table inet filterinternal Claude Code plugin marketplace(prompts/0519-1/marketplace-bootstrap/)的安全边界由 ADR-4(spec.md §5.2,LOCKED via WebFetch 实测)+ panel.uuhfn.cloud @panel_private + 独立 SSH 双通道支撑。整体定位 internal-only,不镜像 anthropic/claude-code 或任何 proprietary upstream。
| 面 | 允许 | 禁止 |
|---|---|---|
panel.uuhfn.cloud(公网) |
仅 vpn-first allowlist(@panel_private matcher);非 allowlist 来源 403 |
公网无差别访问 admin endpoint;公网爆破 X-Admin-Key |
files.uuhfn.cloud/git/bifrost-internal-plugins.git |
公网只读 clone(fcgiwrap,HTTP smart protocol) | git-receive-pack(Caddy 已硬阻 403) |
Server B marketplace-render.service 写入 /var/lib/git-mirrors/... |
仅 root;marketplace-render 通过 systemd unit 执行 | 任何用户态写入 bare 仓库 |
bifrost-admin SSH 写通道 |
forced-command 仅 5 verbs:upload tag-create approve curate rerender |
任意 SSH 命令;交互 shell;root 私钥复用 |
bifrost-readonly SSH 读通道(PR-2 既有) |
forced-command 白名单(marketplace:status, list-json, disk-report, logs:marketplace-render, logs:upstream-schema-check) | 写命令、其它服务的日志、状态文件 |
panel.uuhfn.cloud 走 vpn-first allowlist 而不是公网。Caddy 在 Caddyfile-a 顶层定义 @panel_private matcher(remote_ip + VPN/私网段),匹配命中后才允许 reverse_proxy 到 bifrost-api;其它来源直接 403。这把"admin 上传 panel"的暴露面收回到管理网/VPN,与 hardening-v2 PR-3 的 vpn-first profile 协同。RK-4 因此被显式闭合(公网 token 爆破窗口归零)。
DNS 上
panel.uuhfn.cloud仍解析到 Server A 公网 IP(普通员工浏览器 → Server A 入口走 wg0 隧道),但 Caddy 在 vpn-first allowlist 失配时直接 403,不把请求转发到 bifrost-api,也不读 X-Admin-Key。
bifrost-api 通过 BIFROST_ADMIN_KEY 环境变量配置,由 require_admin dependency 在所有 /marketplace/* 路由强制要求 X-Admin-Key header。轮换流程:
# 1. 生成新 key
NEW_KEY="$(openssl rand -hex 32)"
# 2. 写入 systemd drop-in 或 /etc/bifrost-api/env
sudo tee /etc/bifrost-api/env.d/admin-key.conf > /dev/null <<ENV
BIFROST_ADMIN_KEY=${NEW_KEY}
ENV
sudo chmod 0600 /etc/bifrost-api/env.d/admin-key.conf
# 3. 重启 bifrost-api(让新 key 生效,旧 key 立即失效)
sudo systemctl restart bifrost-api
# 4. 通知需要上传/curate 的管理员(带外 / 加密通道);NewAPI Token 单独管理频率建议每季度 + 人员变动 + 异常调用后立即。严禁把 X-Admin-Key 写入 git 仓库、CI 配置或 settings template。
ADR-4 决策(spec.md §5.2,LOCKED via WebFetch 实测):DENY 镜像 anthropic/claude-code。因此:
prompts/0519-1/marketplace-bootstrap/bifrost-internal-plugins/LICENSE显式声明ALL-RIGHTS-RESERVEDbifrost-internal-plugins/NOTICE明示不镜像 upstreammarketplace.json.metadata.upstream_url = nullmarketplace.json.metadata.license_id = "ALL-RIGHTS-RESERVED"
每个 plugin 子目录可以带自己的 LICENSE 文件;默认 policy 是 ALL-RIGHTS-RESERVED,除非明确覆盖。任何镜像 upstream 的尝试(PR-1b 路径)当前 deferred,触发条件由 check-upstream-schema.sh 监控。
prompts/0519-1/team-config/.claude/settings.json.template 默认下发的 permissions.deny 12 条硬阻:
Bash(curl http://github.com/*)/Bash(curl https://github.com/*)Bash(curl http://raw.githubusercontent.com/*)/Bash(curl https://raw.githubusercontent.com/*)Bash(wget http://github.com/*)/Bash(wget https://github.com/*)Bash(npm install *)Bash(pip install --index-url *)/Bash(pip install --extra-index-url *)WebFetch(domain:github.com)/WebFetch(domain:raw.githubusercontent.com)WebFetch(domain:registry.npmjs.org)
这是 RK-2(恶意 plugin hook 提权)的最后一道防线:即便恶意 plugin 通过 admin upload 审核失误进入仓库,它的任何"从公网 GitHub 拉 payload"或"npm install"动作会被 Claude Code 在执行前 deny。任何缩减 deny 都是 security review 范围的变更,必须走 PR review + 多人 sign-off。
PR-5a 的设计:
- 独立 Linux user
bifrost-admin(与bifrost-readonly完全分开) ~bifrost-admin/.ssh/authorized_keys的每一条 key 都走command="/usr/local/bin/bifrost-admin-router.sh"+no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,restrictbifrost-admin-router.sh仅接受 5 verbs(upload tag-create approve curate rerender),其它输入 exit 2- 所有动作
printf '%s %s %s\n' "$(date -Iseconds)" "${VERB}" "${audit_id}" >> /var/log/marketplace/admin-audit.log - bifrost-api 经
subprocess.run(['ssh', '-i', settings.admin_ssh_key, ...])调用,不复用 readonly key(M11 闭合)
读通道(PR-2 既有 bifrost-readonly)和写通道(PR-5a bifrost-admin)即使一方泄露,另一方权限不会被牵连——这是双通道隔离的安全前提。
Claude Code plugin 通过 .claude-plugin/plugin.json + manifest.yaml 声明 declared_hooks / declared_mcp_servers / declared_skills。RK-2(恶意 plugin hook 提权)通过三层缓解:
- panel curate:admin upload 时人工审核 manifest.yaml + plugin 内容(panel.uuhfn.cloud admin SPA + audit log)
permissions.deny:即使 hook 通过审核,运行时 Claude Code 仍会 deny 公网 GitHub / npm 等高危动作- audit log:
/var/log/marketplace/admin-audit.log记录 upload / approve / curate / rerender 全链路,事后可追溯
scripts/check-upstream-schema.sh 由 upstream-schema-check.timer 每日运行:
- 抓
https://github.com/anthropics/claude-code/raw/main/LICENSE.md的 sha256 - 与 baseline(
/etc/bifrost-api/marketplace/upstream-license-baseline.sha256)比对 - 不变:
LICENSE-OK <sha256> <ts>,state.jsonupstream_alert = false - 漂移:
UPSTREAM-CHANGED <old> -> <new> <ts>,state.jsonupstream_alert = true,Vue admin panel 红色 badge
AC-12 验收命令(spec.md §11 + e2e rehearsal 自动执行):
bash scripts/check-upstream-schema.sh 2>&1 | head -1 | \
grep -E '^(LICENSE-OK|LICENSE-BASELINE-INIT|UPSTREAM-CHANGED) [0-9a-f]{64}'
jq -e '.upstream_alert == false' /var/lib/dist/plugins/state.json当 alert 触发,agent manager 介入 SOP:
- 人工对照新版 LICENSE / Commercial Terms,确认是否变为 OSS license
- 如果是 OSS license(如 MIT/Apache-2.0),评估是否启动 PR-1b(mirror upstream)路径
- 如果仍 proprietary 但条款收紧(如禁止内部 fork),通知法务 + 团队
- 不要自动更新 baseline;baseline 更新必须人工 commit + PR review
# 读通道(PR-2 既有 — render / schema-check 日志)
curl -H "X-Admin-Key: ${ADMIN_KEY}" \
"https://<server-a-domain>/manage/marketplace/logs?service=render&tail=200"
curl -H "X-Admin-Key: ${ADMIN_KEY}" \
"https://<server-a-domain>/manage/marketplace/logs?service=schema-check&tail=200"
# 写通道(PR-5a — 仅 admin-audit.log;当前 PR-7 docs marker,
# bifrost-readonly-router.sh 的 logs:admin-audit verb 是 PR-5a 范围
# 但未在 readonly-router 中开放,PR-7 不补;当 admin-audit 可读时
# 通过 /marketplace/logs?service=admin-audit 出口)bifrost-api 当前对 service=admin-audit 返回 422(spec.md §7.2 占位),等 PR-5a 在 bifrost-readonly-router.sh 增加 logs:admin-audit verb(独立后续 PR)后,会自动转为 200。
参见 spec.md §12 风险登记簿,本小节涉及:
- RK-1(Anthropic 升级 marketplace 协议)— 由
check-upstream-schema.sh+ docs min Claude Code 版本缓解 - RK-2(恶意 plugin hook 提权)— 由
permissions.deny+ panel curate + audit log 三层缓解 - RK-4(panel 公网 token 爆破,已闭合)—
@panel_privatevpn-first - RK-5(LICENSE 合规误判)— ADR-4 LOCKED + check-upstream-schema.sh + 强制 LICENSE/NOTICE
- RK-8(admin/readonly 通道混淆)— 两 user + 两 forced-command + audit log
- RK-12(C6 自指 upstream 死锁,已闭合)— bifrost-internal-plugins 不进 git-mirror-sync 矩阵
- RK-14(state.json 并发写)—
mktemp+mv原子替换
| 对象 | 频率 | 操作 |
|---|---|---|
| WireGuard A/B key | 90 天或泄露后立即 | 先把新公钥加入对端 peer,再替换本机私钥,最后 wg-quick down wg0 && wg-quick up wg0 验证 handshake |
| bifrost-readonly SSH key | 90 天或人员变更 | ssh-keygen -t ed25519 -f /etc/bifrost-api/ssh/bifrost-readonly.ed25519 -N "",把新公钥写入 B 的 forced-command authorized_keys,删除旧公钥 |
| Verdaccio bootstrap 密码 | 首次分发后、人员变更、泄露后 | bash scripts/server-b.sh --rotate-bootstrap-pwd;新密码只发给需要发布私有包的管理员 |
| NewAPI 管理员密码/token | 每季度、成员离职、异常调用后 | 在新 NewAPI 管理面轮换;成员 token 必须逐人独立,不共享 |
| restic password | 年度或备份仓库泄露后 | 新建仓库或按 restic 官方流程迁移;旧仓库保留到恢复验证完成 |
WG 轮换时不要同时替换 A/B 两侧私钥。先利用现有隧道把新 peer 配置写入对端,确认新 handshake 小于 10 秒后再移除旧 peer。
Server B 数据源包括:
/var/lib/verdaccio/var/lib/new-api-pg/var/lib/new-api-redis/var/lib/git-mirrors/var/lib/dist
restic-to-a.timer 负责把这些目录备份到 Server A。真实生产验收不能只看 timer active,必须执行:
systemctl status restic-to-a.timer
systemctl start restic-to-a.service
restic snapshots恢复演练先恢复到临时目录,比对文件和 PostgreSQL 数据目录完整性后,再进入维护窗口替换生产目录。不要在未验证快照可用前退订旧 Windows VPS 或删除 legacy 数据。
BIFROST_SERVER_A_NEWAPI_MODE=distribution 下,Server A 不再默认安装本地 NewAPI;NewAPI 在 B 上绿启。安全后果是旧 token 不迁移,团队成员必须重建:
- 管理员初始化 B 上 NewAPI,生成新管理员密码。
- 每个成员创建独立账号/token/配额。
- 旧 Windows VPS 或 Server A legacy NewAPI 设置只读并保留最终快照 30 天。
- 旧 token 在切流完成后集中作废,避免双写和权限漂移。
退订旧 Windows VPS 前必须满足:
npm.uuhfn.cloud、files.uuhfn.cloud、api.uuhfn.cloud已连续 7 天走 Server A → wg0 → Server B。restic snapshots至少有 7 条 Server B 快照。- 已导出 Windows VPS final snapshot,并能在离线环境列出 Verdaccio storage、Caddy 配置、git mirror tarball。
- 团队成员 NewAPI 账号重建完成,旧 token 作废。
- 已记录退役日期、快照位置、30 天清理日期。
| 频率 | 操作 | 命令 |
|---|---|---|
| 每日 | 查看健康检查报告 | cat /var/log/bifrost/health.json |
| 每周 | 查看 fail2ban 封禁日志 | fail2ban-client status sshd |
| 每周 | 检查 rkhunter 扫描报告 | cat /var/log/rkhunter.log |
| 每月 | 运行 Lynis 安全审计 | lynis audit system --quick |
| 每月 | 更新所有组件 | 见 USAGE.md 更新组件部分 |
| 每月 | 轮换管理面板密码 | New API/3x-ui 面板设置 |
| 每季度 | 审计 API Key 使用情况 | New API 面板 → 日志 |
| 每季度 | 检查域名 SSL 证书有效期 | caddy list-certificates |
| 每日 | IP HTTPS 模式检查短生命周期证书续期 | systemctl is-active bifrost-certbot-renew.timer && certbot certificates --cert-name <SERVER_A_PUBLIC_IPV4> |
| 每日 | Server B 分发栈健康检查 | bash scripts/diagnostics.sh --check distribution |
| 每日 | Server B restic 快照检查 | restic snapshots |
| 每季度 | bifrost-readonly SSH key 轮换 | 见上方“密钥与密码轮换” |
- 为每个用户创建独立的 Key
- 设置合理的配额和到期时间
- 离职员工立即禁用其 Key
- 不要将上游 API Key 分发给用户
- 定期审计 Key 使用情况
- 所有面板密码至少 16 字符
- 使用随机生成的密码
- 不同服务使用不同密码
- 使用密码管理器存储
- 立即检查日志:
tail -100 /var/log/xray/access.log - 检查是否有非白名单流量
- 如有必要,暂停 Xray 服务:
systemctl stop xray - 检查 fail2ban 封禁列表:
fail2ban-client status - 联系管理员处理
- 立即暂停服务
- 排查异常流量来源
- 强化白名单规则
- 确认伪装网站正常
- 回复云厂商说明使用场景
- Server B:更换 IP 或使用 CDN
- 更新 Server A 的连接配置
- 考虑使用多节点备份