berkotako/embargo

GitHub: berkotako/embargo

自托管的 npm 依赖防火墙,在解析阶段拦截未通过年龄、来源证明和行为信号检查的包版本。

Stars: 3 | Forks: 0

# Embargo

Embargo — a self-hosted dependency firewall for npm

**一个自托管的 npm 依赖防火墙。** Embargo 位于 npm registry 之前,并且 在 package 版本通过年龄、来源证明和行为风险检查之前,*拒绝提供* 这些版本。 它是一个用于拦截的防火墙——而不是一个事后发出警告的扫描器。 ## 为什么需要它 npm 生态系统遭受了一波供应链攻击的冲击——token 被劫持后发布的 超流行 package(如 Axios, debug/chalk),自我传播的蠕虫(Shai-Hulud/Miasma), 通过孤儿 commit 绕过审查,以及在安装/导入时植入的后门。原生 package 管理器的 冷却机制(`min-release-age` 等)有助于应对“打砸抢”的攻击窗口,但它们是全局性的, 没有例外机制,并且无法根据版本 *为什么* 可疑来采取行动。 Embargo 提供了 package 管理器在结构上无法做到的功能: - **Per-scope 策略** —— 拦截公共 package,立即放行你自己的 `@org/*`。 - **快速通道例外** —— 针对紧急的 CVE 修复绕过拦截。 - **信号门控** —— 利用冷却窗口实际评估版本(新增的 install scripts、缺失来源证明、重新发布异常、typosquatting、advisory 匹配)并拦截 不良版本。 - **威胁情报源与跟踪** —— 针对精心策划的已知恶意情报源自动 DENY(可选择开启),以及一个 watchlist worker,持续重新评估受跟踪的 package。 - **Pipeline 准入控制** —— 使引入违反策略版本的 CI 构建失败。 - **安装隔离** —— 在沙箱中运行安装,并控制 egress。 开放、自托管,并作为一个门禁强制执行。 ## 工作原理 使用一行命令将你的客户端指向 Embargo: ``` # .npmrc registry=https://embargo.your-org.internal/ ``` Embargo 会在解析之前拦截 npm 获取的 package 元数据,并过滤掉违反策略的版本—— 因此你的 resolver 根本不会选择被 HOLD 或 DENY 的版本。支持 npm、 pnpm、Yarn 和 Bun。

Point .npmrc at Embargo, install, the gate sorts allow/hold/block, only safe versions land

每个版本都会得到以下三种裁定之一:

The three Embargo verdicts: ALLOW, HOLD, DENY

- 🟢 **ALLOW** —— 正常提供 - 🟡 **HOLD** —— 在冷却期间/等待审查时暂扣 - 🔴 **DENY** —— 被拦截(被信号或 advisory 标记) ## 组件 | 组件 | 层级 | 功能描述 | |---|---|---| | [`engine`](engine/) | core | 策略与信号引擎:解析、冷却、来源证明、行为信号、OSV advisories、mTLS gRPC + JSON admin API | | [`gateway`](gateway/) | L1 | Verdaccio 插件 —— 重写 packument,移除 HOLD/DENY 的版本 | | [`admission`](admission/) | L2 | CI 门禁 (CLI + GitHub Action) —— 如果 lockfile diff 引入了被 HOLD/DENY 的版本,则使构建失败 | | [`sandbox`](sandbox/) | L3 | 带有 namespace 和 egress 控制的安装运行器;拦截并捕获 phone-home 和运行时的 secret→egress 链 | | [`console`](console/) | UI | Web 管理端 —— 隔离审查、策略、批准、审计、仪表盘 (OIDC + 服务器端 RBAC) | | [`policy`](policy/) | — | 版本化策略 schema (JSON Schema) + YAML DSL + 示例 | ## 架构 一个原则驱动着整个设计:**engine 是大脑;L1/L2/L3 是向其请求裁定的执行点。** 客户端从不直接与 engine 通信——它将 `registry=` 指向 gateway,后者会在 resolver 看到不允许的版本之前重写 package 的元数据。

Embargo architecture overview

``` flowchart TD client["Client — npm · pnpm · yarn · bun
(.npmrc → registry=embargo)"] gw["L1 — Ingress Gateway (Verdaccio plugin)
rewrites packument, applies verdicts"] engine["Policy & Signal Engine (Rust) — the core
cooldown · provenance · signal scoring"] store[("State store
Postgres + Redis")] feeds["External feeds
OSV · advisories · provenance"] l2["L2 — Admission gate
CI: GitHub Action / CLI"] l3["L3 — Sandbox runner
namespaced, egress-controlled"] console["Web Admin Console
quarantine · policy · approvals · audit"] client -->|"GET /{package}"| gw gw -->|"mTLS gRPC: verdict?"| engine engine --> store engine --> feeds l2 -->|"lockfile diff"| engine l3 -->|"containment events"| engine console -->|"admin API · OIDC + RBAC"| engine console --> store ``` ### 请求生命周期 1. 客户端解析依赖项,并从 gateway 获取 **packument**(`GET /{package}` —— 即 `versions` 和 `time` 映射)。 2. Gateway 通过 mutual-TLS gRPC 向 engine 请求每个版本的裁定。裁定会被缓存在 Redis 中——在这个**热路径(解析延迟面向用户)中没有未缓存的网络调用**。 3. Engine 解析**以最具体匹配为准的 per-scope 策略**,应用**冷却期**,在需要时强制执行 **来源证明**,并对**行为信号 + OSV advisories** 进行评分。 4. Gateway 从映射中移除所有 **HOLD** / **DENY** 的版本,并返回过滤后的 packument —— resolver 根本不会选择不允许的版本。在 lockfile 中被锁定但 现在处于 HOLD 状态的版本将降级为明确的 Embargo 错误(包含原因 + 批准链接),绝不会出现晦涩难懂的 `ETARGET`。 5. 在冷却 HOLD *期间* 被信号标记的版本将升级为 **DENY 永久** —— 当计时器到期时,它绝不会在不被察觉的情况下被提供。这种升级才是核心目的。 ### 纵深防御 每一层都是由同一个 engine 提供支持的独立执行点,因此在一层的漏网之鱼会被下一层捕获: | 层级 | 拦截内容 | 机制 | |---|---|---| | **L1** Gateway | “打砸抢”式的 token 劫持发布、缺失来源证明、重新发布异常 | 解析时的 packument 重写 | | **L2** Admission | 到达 CI/CD 的违反策略版本(大多数攻击的着陆点) | 导致构建失败的 lockfile-diff 门禁 | | **L3** Sandbox | 安装时的 phone-home、lifecycle-script 后门、secret→egress 链 | 带有 namespace 的、egress 加入白名单的安装运行器 | 请参阅 [`ARCHITECTURE.md`](ARCHITECTURE.md) 了解权威的设计、数据模型、技术栈以及 完整的威胁模型(哪种攻击对应哪种防御)。 ## 快速开始 一条命令即可启动整个技术栈(Postgres + Redis + engine + console + gateway), 等待 engine 健康运行,并打印如何将客户端指向它的说明: ``` make up # builds + starts everything, self-seeds the default policy ``` ``` Console http://localhost:4000 (sign in, pick a role) Gateway http://localhost:4873 (point clients here) Admin API http://localhost:8080/api ``` 然后使用一行命令为任何项目配置防火墙 —— `make onboard` 会写入其 `.npmrc`: ``` cd my-project && make -C /path/to/embargo onboard # or: echo 'registry=http://localhost:4873/' >> .npmrc npm install # held/denied versions are stripped before resolve ``` `make` 列出了所有其他操作(`down`、`logs`、`health`、`test` 等)。更喜欢直接使用 Docker?执行 `docker compose up --build`。不使用 Docker 运行组件(Rust engine,Vite console 等)在 [`DEVELOPMENT.md`](DEVELOPMENT.md) 中有介绍; 生产环境部署在 [`DEPLOYMENT.md`](DEPLOYMENT.md) 中。 ## 文档 - [状态](docs/STATUS.md) —— 已构建并验证的内容,以及测试数量 - [架构](ARCHITECTURE.md) —— 权威设计 - [信号](SIGNALS.md) —— 检测目录和评分契约 - [常见问题](docs/FAQ.md) —— 它是什么,如何使用,裁定的行为表现 - [开发](DEVELOPMENT.md) —— 本地设置、运行命令、配置、admin API - [部署](DEPLOYMENT.md) —— 生产拓扑、强化、客户端接入 - [发布](docs/RELEASE.md) —— 从已发布的镜像部署 release build;如何进行 release - [更新日志](CHANGELOG.md) —— release 说明 - [项目计划](docs/PROJECT_PLAN.md) 和 [各组件计划](docs/plans/) - [贡献指南](CONTRIBUTING.md) · [行为准则](CODE_OF_CONDUCT.md) · [安全](SECURITY.md) ## License [MIT](LICENSE) © 2026 berkotako. 整个技术栈 —— engine、gateway、signals、 console 以及 CI gate —— 都是开源的:使用它,fork 它,发布它。 ## 不是什么 不是 SCA/CVE 扫描器(请将其与 Grype/Trivy 搭配使用),也不是运行时的 EDR。Embargo 是一个解析时的门禁,加上安装时的隔离。 ## 支持 Embargo 是免费且自托管的。如果它帮你避免了一次糟糕的安装,并且你想表达感谢: Buy me a coffee Donate via PayPal
标签:DevSecOps, npm, Python工具, Streamlit, 上游代理, 依赖防火墙, 可视化界面, 搜索引擎查询, 私有仓库, 访问控制, 请求拦截, 通知系统