Skip to content

Latest commit

 

History

History
326 lines (229 loc) · 15.9 KB

File metadata and controls

326 lines (229 loc) · 15.9 KB

api_show —— 局域网内部 OpenAPI 聚合门户

English · 中文

Release Go License

注重安全、隐私的快速、轻量化 API 文档展示服务。

Caution

适用性 —— 务必先读。 api_show 面向 高保密性、权限粒度较粗 的场景:

  • 适合: 仅在内网 / 局域网暴露的 API 文档;权限按 项目 + tag 前缀 粒度控制(例:「Alice 可见 payments 项目的 auth* / users* 标签」)。
  • 不适合: SaaS 多租户文档门户、按端点 / 按字段的细粒度 ACL、按操作限流、合规审计日志,或任何需要直接暴露在公网的部署。

如果需要操作级细粒度权限、审计追踪、SSO/OAuth 联邦登录、公网托管 —— 请选别的工具。api_show 用功能广度换来更小、更可审计的攻击面,目标只有一个:内网。

api_show 把多个生产者仓库的 OpenAPI 文档聚合成单一门户,由内嵌的 Redoc 渲染(本地静态资源,无 CDN 依赖)。各生产者仓库在 tag 发版时把自己的 docs/openapi.yaml POST 到门户;用户打开同一个 URL,从下拉菜单里选项目 + 版本,就能看到渲染后的接口文档。文档更新不需要重新编译门户。

截图

登录 绑定 TOTP(首次)
登录 绑定 TOTP
门户 — demo_a 门户 — demo_b
demo_a demo_b

绑定页中的二维码与 base32 secret 已主动模糊 / 遮罩 —— 每个用户首次登录都会生成全新的 secret。

暗色主题色板与 Redoc 左侧栏 + 右侧代码面板对齐。Schema 标签 / 表格行通过 webroot/style.css#api-reference 作用域内提升对比度。

威胁模型 —— 仅限局域网。 不暴露公网 IP,永远不上互联网。 only_local = true(默认)下,任何非私网 / 非环回 IP 命中 /upload 都会在应用层直接拒绝,不管 Bearer 是否正确。

认证 —— 三种模式,由 TOML 中 auth_mode 字段切换:

模式 行为
pwd 仅用户名 + 密码。无 TOTP。适合 IDE 断点调试。
pwd_totp 默认。先校验密码;若用户已绑定 TOTP,要求 6 位 2FA 码;否则直接发会话。
totp 无密码。TOTP 即凭证;首次登录通过 /bind-totp 自动绑定。

TOML 中永不存密码。 Argon2id 哈希存放在 <data_dir>/pwd_blobs/ 下经 AES-GCM 加密的 sidecar blob。管理 CLI 可以创建用户而不需要知道明文密码 —— 见下文 添加用户


同类对比

api_show 的定位很窄:多份 OpenAPI 规范聚合到一个内网门户、按用户重写规范、不依赖 SaaS、单二进制部署。 同类工具大多缺其中一项,或者是完整的 SaaS 平台。

图例:✅ 支持 · ⚠️ 部分支持 / 需插件 · ❌ 不支持 · — 不适用。

能力 api_show Swagger UI / Redoc 原生 Redocly CLI Stoplight Elements Stoplight Platform / Bump.sh ReadMe.com Mintlify Backstage TechDocs
单门户聚合 多份 OpenAPI 规范 ⚠️ ⚠️
自托管单二进制,无运行时依赖 ⚠️(静态文件) ⚠️ ❌(SaaS) ❌(SaaS) ❌(SaaS) ❌(重型平台)
应用层强制 LAN-only(拒非 private/loopback IP) ⚠️
内置 多用户认证(密码 / TOTP / 2FA) ⚠️(依托 Backstage)
按用户 ACL:项目 + tag 前缀粒度 ⚠️(组织级) ⚠️ ⚠️
服务端 重写规范,剔除未授权 op 后再下发
生产者 上传端点(CI 在打 tag 时 POST 规范) ⚠️
运行时 无 CDN / 无外网字体抓取 ⚠️ ⚠️ ⚠️ ⚠️
密码 永不入配置,argon2id PHC 存于 AES-GCM blob ⚠️
管理员 CLI 永不见明文密码(走 /set-password 流) ⚠️ ⚠️ ⚠️ ⚠️
规范更新 无需重编 / 重部署 门户 ⚠️ ❌(需重编) ⚠️ ❌(需重编) ❌(需重编)
审计日志 / SSO / 单接口级 ACL ⚠️
支持 公网托管 部署 ❌(刻意)

api_show 当且仅当 同时满足:

  • 多个内部团队各自维护 OpenAPI 规范,需要单门户列出全部。
  • 访问范围在 LAN / VPN 内;愿依赖 SaaS、愿把文档暴露到公网。
  • 粗粒度访问控制(每用户 项目 + tag 前缀)够用;需要单接口 / 单字段 ACL 或审计追溯。
  • 想要一个单 Go 二进制 + 内嵌 Redoc:无 Node 工具链、无构建流水线、运行时不打 CDN。

选别的工具当 你需要其中任一:单接口 ACL、审计日志、SSO / OIDC 联合登录、公网托管、细粒度配额 / 限流,或是带营销页的精致文档 CMS。


安装(局域网部署)

# 1. 下载 release 包
wget https://github.com/0xYeah/api_show/releases/download/<VERSION>/api_show_linux_release_<VERSION>.zip
unzip api_show_linux_release_<VERSION>.zip && cd api_show

# 2. 一键安装(创建 /api_show/{api_show, conf/, logs/},安装 systemd unit)
sudo ./install_api_show.sh

# 3. 启动 → 编辑配置 → 重启
sudo systemctl start api_show
sudo $EDITOR /api_show/conf/api_show_config.toml   # 填 secret + upload_token
sudo systemctl restart api_show
sudo systemctl status api_show

install_api_show.sh 行为:

动作 说明
install(默认) 首次安装。已存在的二进制 + 配置会加 _YYYY-MM-DD_HH-MM-SS 备份后缀。
update 停服务 → 替换二进制(保留 data/ + conf/)→ 重启。
uninstall 停服务 + 删 unit 文件 + 删 /api_show/ 目录(不可恢复)。

配置

路径:/api_show/conf/api_show_config.toml(首次启动时自动用占位值生成)。

# HMAC cookie 密钥 + 单 blob 密钥派生 salt。≥ 16 字节,ASCII。
secret = "REPLACE_ME_at_least_16_chars"

# /upload 接受的 Bearer token。
upload_token = "REPLACE_ME_bearer_token"

# 上传 spec + totp_blobs/ + pwd_blobs/ 的根目录。
data_dir = "./data"

# HTTP 监听端口。仅绑 IPv4 0.0.0.0。
port = 12110

# 内网模式。true = /upload 在应用层拒绝任何非私网 / 非环回 IP,
# 与 Bearer 是否正确无关。
only_local = true

# 认证策略:pwd | pwd_totp(默认)| totp
auth_mode = "pwd_totp"

# 用户名册 + 单用户 ACL。改完重启即生效。
# TOML 永不存密码 —— 密码以加密 blob 存放在 <data_dir>/pwd_blobs/。
# 详见「添加用户」。
[[users]]
username = "alice"
acl = { demo_a = ["*"], demo_b = ["auth", "users"] }

[[users]]
username = "bob"
acl = { demo_a = ["*"] }

约束:

  • secret 为空或不足 16 字节 → 进程拒绝启动。
  • upload_token 为空 → /upload 返回 503 uploads disabled
  • auth_mode 不在 {pwd, pwd_totp, totp} 范围 → 进程拒绝启动。
  • 不读取任何环境变量。配置只来自 TOML;改部署 = 覆盖文件。
  • 首次启动若文件不存在,会写入占位配置 —— 启动永不会因「缺配置文件」而失败。

登录流程

pwd 模式

  1. GET /login → 输入 username + 密码 → 提交。
  2. Argon2id 校验通过 → 发 7 天会话 cookie → 302/

pwd_totp 模式(默认)

  1. GET /login → 输入 username + 密码 → 提交。
  2. 密码通过 → 门户检查用户是否已绑 TOTP。
    • 未绑 → 直接发会话 → 302/
    • 已绑 → 302/login-totp
  3. /login-totp → 输入 6 位码 → 发会话 → 302/

totp 模式

  1. GET /login → 输入 username,TOTP 留空 → 提交。
  2. 账号未绑 → 302/bind-totp,扫二维码 + 输入首个码 → secret 加密落盘 → 发会话 → 302/
  3. 后续登录:username + 6 位码 → 校验 → 会话 → /

首次登录但还没设密码

如果用户是用 add_user 创建但没带 -p,或被 reset_pwd 重置过,下次登录时门户会重定向到 /set-password。用户自己挑密码(≥ 8 位);管理员永远看不到明文。

浏览文档

登录后,根页面用两个下拉菜单列出可见的项目 + 版本。当前选择会反映在 URL 上,方便分享给同一 ACL 的用户。


添加用户

管理 CLI 永远不会输出明文密码。两种添加模式:

模式 1 —— 管理员设初始密码,用户后续自改:

sudo /api_show/api_show add_user alice -p "InitChangeMe!8" -acl "demo_a=*;demo_b=auth"
sudo systemctl restart api_show
# 把初始密码以带外渠道告诉 Alice(如签名邮件)。
# 任何时候管理员跑 `reset_pwd alice`,下次登录就走 /set-password 让 Alice 自己改。

模式 2 —— 用户首次登录时自己挑密码:

sudo /api_show/api_show add_user alice -acl "demo_a=*"
sudo systemctl restart api_show
# 告诉 Alice 用任意密码访问 /login。
# 她会被重定向到 /set-password,自己挑一个。

-acl 语法:proj1=tag1,tag2;proj2=** 表示该项目的所有 tag。不带 -acl 会写入空 ACL 块(管理员之后可以直接编辑 TOML 中的 [[users]])。

CLI 会向当前生效的配置文件追加一个 [[users]] 块(保留你的注释);带 -p 时还会写加密密码 blob。


维护命令

./api_show version                              # 构建身份(commit / 时间 / OS / Go 版本)
./api_show add_user <user> [-p <plaintext>]     # 追加 [[users]] 块 + 可选 pwd blob
                  [-acl <spec>]
./api_show reset_pwd  <user>                    # 删 pwd blob;用户下次登录走 /set-password
./api_show reset_totp <user>                    # 删 TOTP blob;用户下次登录重新绑定(totp / pwd_totp 模式)

推送 spec(生产者侧)

生产者发版脚本只在 git push --tags 成功后才把 docs/openapi.yaml POST 到门户。endpoint + token 来自生产者仓库内一份 git-ignored 文件(不读环境变量):

# 在生产者仓库根目录
cat > .gen_docs.local <<'EOF'
DEPLOY_ADDR=http://<portal-host>:12110
DEPLOY_TOKEN=<必须与 api_show 的 upload_token 一致>
# ALLOW_LOOPBACK=1   # 仅本机自测时打开
EOF

生产者各自维护自己的 gen_docs.sh(或同等脚本)。参考实现默认拒绝环回地址(127.0.0.1localhost[::1]0.0.0.0),除非显式设置 ALLOW_LOOPBACK=1 走本地自测。

手动上传(本地自测):

curl -X POST \
  -H "Authorization: Bearer <upload_token>" \
  --data-binary @./docs/openapi.yaml \
  "http://127.0.0.1:12110/upload?project=<proj_id>&version=v0.0.1&latest=true"
参数 含义
project 项目 id(同时用作数据目录名 + ACL 键)。
version vX.Y.Z 形式。
latest true 把该版本标记为默认(在下拉菜单中默认选中)。

权限(ACL)

名册 + 权限存在 TOML 的 [[users]] 中:

  • 每个 acl 映射:项目 id → 允许的 tag 前缀。
  • * = 该项目下所有 tag。
  • 用户 acl 中没有的项目 = 该用户完全不可见。
  • GET /api/sources.json 会过滤掉调用者无权访问的项目。
  • GET /data/<proj>/<ver>/openapi.yaml 会重写 spec,移除不允许的 operation + tag 声明。

改名册 = 改配置 + 重启。 编辑 /api_show/conf/api_show_config.toml(或用 add_user / reset_pwd),然后 systemctl restart api_show。无须打 tag / 发 release。


健康检查

curl http://<portal-host>:12110/healthz   # 200 OK = 存活

升级

sudo ./install_api_show.sh update

update 保留 /api_show/data/(spec + TOTP/pwd blob)和 /api_show/conf/;只替换二进制。


卸载

sudo ./install_api_show.sh uninstall

停服务,删 unit 文件,递归删除 /api_show/(含数据 —— 不可恢复)。


安全边界

  • 设计上仅限内网部署。only_local = true 下,/upload 拒绝外网 IP,与 token 无关。
  • 监听绑定 0.0.0.0(IPv4)—— 没有 IPv6 双栈监听。
  • secret(≥ 16 字节)用于 HMAC 签名 cookie 以及 pwd / TOTP blob 的 AES-GCM 单用户密钥派生。
  • 密码:argon2id(m=64 MiB, t=3, p=2),PHC 格式输出,永不明文落盘 / 入 TOML。
  • Cookie:HttpOnly + SameSite=Lax。前面套 nginx TLS 后把 Secure=true 打开。
  • /upload 外没有任何远程写 API;改用户名册必须有主机文件系统访问权限。

从源码构建

go build ./...           # debug 二进制
./build.sh               # 带版本戳的发版包

go.mod 要求 Go 1.24+。依赖纯 Go。


许可证

LICENSE