nolabs-ai/runseal

GitHub: nolabs-ai/runseal

Runseal 通过内核级沙箱、虚拟凭证替换和 L7 网络过滤,为 GitHub Actions 提供密封执行环境,防御 CI/CD 环境中的软件供应链攻击。

Stars: 57 | Forks: 6

Runseal logo Join Discord We're hiring

Runseal 的构建旨在解决通常由 GitHub Actions 漏洞 exploit 引发的软件供应链攻击问题。 为了应对针对 GitHub Actions 的供应链攻击日益增加的趋势,我们开发了 runseal。在这些攻击中,攻击者通常会获取对 repository secrets 的访问权限,并利用它们来窃取数据或部署恶意代码。通过使用 [nono 的](https://github.com/always-further/nono)内核级强制沙箱机制,runseal 可以保护敏感文件、secrets/tokens,并过滤来自不受信任或恶意代码的网络访问,同时仍然通过灵活的策略系统允许必要的软件工程操作。 由带给您 [sigstore](https://sigstore.dev) 和 [nono](https://nono.sh) 的同一团队打造。 ## Runseal 的功能 - 使用会话作用域的虚拟凭证替换工作流中的真实 secrets,这些凭证即使泄露也毫无用处 - 通过 nono 使用 Landlock 的内核级强制沙箱机制,保护敏感文件和 secrets 免受 CI 中不受信任代码的窃取 - L7 网络过滤,通过 HTTP 方法和路径锁定网络访问 - 默认拦截网络,除非策略明确允许特定的主机或凭证路由 - 在沙箱外部捕获加密审计记录,涵盖所有网络请求、凭证注入和文件系统访问 ## 快速开始 ``` name: Publish on: workflow_dispatch: jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: always-further/runseal@v0.3.3 with: run: npm publish policy: | fs: read: ["."] write: [] network: mode: filtered access: npm: secret: NPM_TOKEN url: https://registry.npmjs.org allow: - PUT /** env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} ``` 在此示例中,`npm publish` 可以读取 repository,但不能写入未在 `fs.write` 中列出的路径,网络访问仅限于 'registry.npmjs.org' 而无法访问其他主机。同时,'NPM_TOKEN' 会从 GitHub Secrets 中获取,并在原位注入一个虚拟凭证。如果该工作流被攻破,攻击者试图窃取 token 或发布恶意 package,此攻击将被沙箱拦截,并且真实的 token 将保持安全,位于 'npm publish '工作流之外。 ## 策略格式 Runseal 策略是通过 `policy` 输入传递的 YAML。 ``` fs: read: - "." - "$HOME/.cache/my-tool" write: - "./dist" network: mode: filtered allow: - api.github.com access: deploy: secret: DEPLOY_TOKEN url: https://api.example.com allow: - POST /v1/deployments - GET /v1/deployments/* ``` ### 文件系统访问 `fs.read` 列出了命令可以读取的路径。`fs.write` 列出了命令可以写入的路径。 请尽量缩小这些路径的范围。例如,部署步骤通常只需读取 `./dist` 和一个配置文件,并且可能完全不需要写入权限。 ``` fs: read: ["./dist", "./fly.toml"] write: [] ``` ### 网络访问 Runseal 要求设置 `network.mode: blocked` 或 `network.mode: filtered`。 仅在命令必须访问的未经身份验证的主机上添加 `network.allow`。访问授权中使用的主机会自动添加到生成的 `nono` 配置文件中。 ``` network: mode: filtered allow: - api.github.com ``` ### 访问授权 `access` 下的每个 key 都是一个命名授权。`secret` 是包含真实 secret 的环境变量,`url` 是服务的基础 URL,而 `allow` 列出了可以注入该 secret 的 HTTP 路由。Runseal 会在日志中屏蔽该 secret,将其写入一个私有文件,从子进程环境中将其移除,并配置 `nono` 通过本地代理将其注入。 ``` access: fly: secret: FLY_API_TOKEN url: https://api.machines.dev allow: - POST /v1/apps/*/machines ``` 沙箱化的命令会接收到一个用于兼容 SDK 的虚拟凭证。真实的 secret 保留在沙箱之外,仅当主机和 endpoint 策略匹配时才由代理插入。 ### HTTPS Endpoint 过滤 `allow` 通过 HTTP 方法和路径限制访问。匹配是基于允许列表(allow-list)的。 ``` allow: - POST /v1/apps/*/releases - GET /v1/apps/*/status ``` Runseal 依赖 `nono` 的 TLS 拦截来实现此功能。`nono` 代理会创建一个临时的信任包,并将标准的 CA 环境变量注入到沙箱化的进程中,因此常见的 HTTPS 客户端可以通过代理进行连接,同时仍然允许执行 L7 策略。 ## 常用方案 ### 在无网络环境下运行测试 ``` - uses: always-further/runseal@v0.3.2 with: run: npm test policy: | fs: read: [".", "./node_modules"] write: ["./coverage"] network: mode: blocked ``` ### 在拥有 Package Registry 访问权限的情况下构建 ``` - uses: always-further/runseal@v0.3.2 with: run: npm ci policy: | fs: read: ["."] write: ["./node_modules"] network: mode: filtered allow: - registry.npmjs.org ``` ### 使用受保护的 Token 进行部署 ``` - uses: always-further/runseal@v0.3.2 with: run: ./scripts/deploy.sh policy: | fs: read: ["./dist", "./deploy.yaml"] write: [] network: mode: filtered access: deploy: secret: DEPLOY_TOKEN url: https://deploy.example.com allow: - POST /v1/releases - GET /v1/releases/* env: DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} ``` ## 输入 | Input | Required | Default | Description | | --- | --- | --- | --- | | `run` | 是 | 无 | 在沙箱内执行的命令。 | | `policy` | 否 | 空 | Runseal 策略 YAML。建议在新工作流中使用此项。 | | `fs-read` | 否 | 空 | 未设置 `policy` 时,以逗号分隔的读取路径。 | | `fs-write` | 否 | 空 | 未设置 `policy` 时,以逗号分隔的写入路径。 | | `network` | 否 | `blocked` | 未设置 `policy` 时的网络策略:`blocked` 或以逗号分隔的域名。 | | `runseal-version` | 否 | `0.3.1` | 要安装的 Runseal 发布版本。接受 `v0.1.0` 或 `0.1.0`。 | | `nono-version` | 否 | `0.62.0` | 要安装的 nono 发布版本。接受 `v0.1.0` 或 `0.1.0`。 | | `verify-attestations` | 否 | `true` | 验证已下载发布资产的 GitHub artifact attestations。 | | `audit` | 否 | `false` | 设置为 `artifact` 或 `true` 以将 nono 审计证据作为 GitHub Actions artifact 上传。 | ## 审计证据 Runseal 可以为沙箱化的命令导出 nono 审计会话: ``` - uses: always-further/runseal@v0.3.2 with: run: npm rebuild audit: artifact policy: | fs: read: [".", "./node_modules"] write: ["./node_modules"] network: mode: blocked ``` 启用后,Runseal 会在命令完成后捕获新的 nono 审计会话,并上传一个包含以下内容的 `runseal-audit` artifact: - `summary.md` - 每个检测到的 nono 审计会话对应的一个 JSON 文件 审计导出会在 Runseal 返回沙箱化命令的退出状态之前运行,因此即使是失败或被拒绝的命令也可以生成审计证据。 ## 要求 - Linux x86_64 GitHub-hosted runner - runner 上必须提供 `gh` CLI 以进行 attestation 验证 - Runseal 和 `nono` 均需发布发布资产 发布资产应使用以下命名方案: - `runseal-v-x86_64-unknown-linux-gnu.tar.gz` - `nono-v-x86_64-unknown-linux-gnu.tar.gz` - `SHA256SUMS` ## 开发 ``` make ci ``` `make ci` 运行 `make lint` 和 `make test` —— 与 [CI 工作流](.github/workflows/ci.yml)中的 Rust 检查相同。 ``` make lint # clippy + fmt check make test # unit tests only make fmt # format code make audit # cargo audit (run make audit-install first) ```
标签:DevSecOps, GitHub Actions, StruQ, 上游代理, 可视化界面, 文档安全, 沙箱, 自动笔记, 通知系统