ryantylerross/sentinel

GitHub: ryantylerross/sentinel

Sentinel 是一个零信任飞地的带外边界验证器,通过三态出口探针定期验证网络隔离控制措施是否有效,采用失败即关闭策略确保仅在明确确认阻挡时才判定通过。

Stars: 0 | Forks: 0

# Sentinel Sentinel 是 **Faraday** 飞地的一个带外边界探针 —— 这是一个由 Terraform 定义、采用零信任的环境(一个被严格锁定的 VPC),旨在 容纳不受信任的 AI 工作负载。该工作负载是用户提供的输入,不属于 设计的一部分;无论它是什么,飞地都假设其带有恶意。 **Sentinel 是一个验证器,而不是执行器。** 它从飞地*外部*定期运行, 并检查安全控制措施是否有效。它永远不会出现在实时数据路径中,也 无法实时检查真实流量。一次通过的运行是控制措施有效运行的*证据* —— 其本身并不提供保护。 因为它是一个验证器,其核心规则是:**当仅仅是未能执行检查时,绝不报告该控制措施有效。** 出口探针是三态的 —— 只有当目标被明确阻挡时才是*通过 (pass)*;可到达的 目标是一个**泄漏 (leak)**,而未能确定(DNS 失败、无路由、探针主机故障)的 目标状态是**不确定 (inconclusive)**,这两种情况都会导致运行失败(失败即关闭)。 当前构建完全针对 mock 运行,零运行时依赖( 仅使用标准库)。Live 的实现代码已编写但并未指向真实 目标(目前尚未部署)。请参阅 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) 了解设计原理。 ## 目录 - [Sentinel 为什么存在](#why-sentinel-exists) - [两道边界](#the-two-boundaries) - [安装](#install) - [用法](#usage) - [退出码](#exit-codes) - [配置](#configuration) - [测试](#testing) - [投入生产](#going-live) - [许可证](#license) ## Sentinel 为什么存在 飞地将其工作负载视为带有恶意,无论它具体是什么,因此 安全问题在于**控制可能离开飞地的数据**。存在两个 独立的风险: 1. **网络渗出** —— 内部的某个进程试图向 外部发送数据。通过*出口限制*解决:如果没有通向外的路由,无论意图如何,都无法离开。 2. **合法输出中的违规内容** —— 即使通过被允许的通道, 模型的响应也可能包含机密或违禁内容。通过 *输出防护栏*(output guardrail)解决,该防护栏目前尚不存在且不在本次讨论范围内。 Sentinel 不会实时防御任何一种风险。它只探测防御 措施是否存在并发挥作用。 ## 两道边界 这两项控制措施截然不同,并且会独立失效。在任何地方将它们混为一谈都是 设计错误。 | | 出口限制 | 输出防护栏 | | -------------- | -------------------------------------- | -------------------------------------- | | OSI 层 | L3/L4(地址,端口) | L7(内容本身) | | 控制措施 | 数据*去向* | 允许离开的*内容* | | 执行者 | GCP VPC firewall egress 规则 / Private Google Access / default-deny DNS (Terraform) | 虚拟机外部的路径内执行器 | | 目前是否存在? | 是 —— Sentinel 对其进行探测 | 否 —— 不在本次构建范围内 | | Sentinel 的职责 | 探测出口是否已被封锁 | 待其存在后进行验证 | 封锁网络对于允许通道上的不良内容毫无作用; 清洗内容对于意外的网络路由也无济于事。 Sentinel 的出口检查只有在从特定视点运行时才有意义, 该视点必须受限于真实逃脱进程将面临的相同网络限制 —— 在 GCP 上,即飞地 VPC 内部的一个 GCE/GKE 工作负载,没有外部 IP,并 受相同的 VPC firewall egress 规则和 Cloud NAT 状态的约束。如果从具有 更宽松外出路由的地方(特权主机、不同的子网)运行,“已阻挡”的结果将 毫无意义。 ## 安装 ``` python3 -m venv .venv && source .venv/bin/activate pip install -e ".[dev]" ``` 运行时没有任何第三方依赖。`pytest` 仅通过 可选的 `[dev]` 扩展安装。 ## 用法 ``` sentinel --config config.mock.json # text report sentinel --config config.mock.json --json # JSON report python -m sentinel --config config.mock.json # equivalent module form sentinel --version ``` `--config` 默认为 `config.mock.json`,并基于工作目录进行解析。 报告示例: ``` Sentinel probe report [mode=mock] => PASS (verifier, not enforcer — evidence the controls hold, not protection) (MOCK — results are SIMULATED in-process; this exercises the logic, not a live GCP network or a real model surface) [PASS] egress_containment — L3/L4 network egress all 2 target(s) blocked; egress containment holds - malicious.example.com:443: blocked (mock: simulated block) - exfil.example.net:80: blocked (mock: simulated block) [PASS] output_guardrail — L7 output guardrail (mock-fixture verification) 2 case(s) behaved as expected (mock-fixture self-check; not a live enforcer) - probe:benign-greeting: expected_blocked=False observed=False [ok] findings=[] - probe:adversarial-exfil: expected_blocked=True observed=True [ok] findings=['aws_access_key_id'] ``` 在 `live` 模式下,如果 Sentinel 既无法到达也无法排除某个目标,则会被报告为 `INCONCLUSIVE` 并导致运行失败 —— 该探针绝不会将“无法判断”升级为 “已限制”。 ## 退出码 CLI 返回对 CI 友好的退出码。当出现任何 非零结果时使 pipeline 失败以进行拦截。 | 代码 | 含义 | | ---- | -------------------------------------------------- | | `0` | 所有检查均通过 | | `1` | 探针检查失败 —— 边界发生泄漏或状态不确定 | | `2` | 配置或使用错误 | ## 配置 `config.py` 即为授权边界:它决定了允许 Sentinel 探测的内容, 并且它是失败即关闭的 —— 任何格式错误、范围过大或无法识别的 内容都会被拒绝,而不是进行猜测。 ``` { "version": 1, "mode": "mock", "egress_targets": ["host:port", "..."], "model_endpoint": "https://..." } ``` - `mode` —— `mock` 或 `live`。选择要使用的实现。 - `egress_targets` —— 显式的 `host:port` 允许列表。通配符和 全量 CIDR 形式(`*`,`0.0.0.0/0`)将被拒绝;“探测所有内容”绝不会被 授权。 - `model_endpoint` —— 在 live 模式下是必需的,在 mock 模式下会被忽略(但仍会进行验证)。它是一个探针目标,因此它会经过 相同的边界:它必须是一个不包含凭据的 `http(s)` URL,并且 它**不得**指向 GCP 元数据服务器(`169.254.169.254` / `metadata.google.internal`)、 环回地址或链路本地地址 —— 因为元数据服务器会分发 工作负载的 service-account token,是经典的 SSRF 渗出跳板。 合法的内部 GCP 主机(RFC1918、Private Service Connect、内部 load balancer、`*.run.app`)是被允许的。 - 以 `_` 开头的键(如 `_comment`)是允许的并被忽略。任何其他 未知键都会引发错误。 `config.mock.json` 针对 mock 实现进行离线运行。 `config.live.example.json` 是真实目标的模板 —— 将其复制到 `config.local.json`(已被 gitignored)并填入相应的值。 ## 测试 ``` pytest -q ``` 测试套件**不会发起真实的网络调用**:live 实现的判定 映射和输入处理是通过注入的标准库 模拟对象(通过 monkeypatch 替换的 `socket` / `urllib`)离线执行的,绝不会指向已部署的目标。它 也独立于工作目录。 ## 投入生产 当部署了真实目标后,从 mock 切换到 live 是配置上的 更改,而不是重写: 1. 将 `mode` 设置为 `live`,并在被 gitignored 的 配置文件中提供真实的 `model_endpoint`(受上述端点防护限制 —— 不得包含元数据/环回主机)。 2. 从邻近飞地的视点运行 Sentinel —— 即飞地 VPC 内部的一个 GCE/GKE 工作负载, 无外部 IP —— 使其出口尝试面临真实的 VPC firewall / Cloud NAT 限制。发生泄漏会导致运行失败;探针 既无法到达也无法排除的目标会被报告为 `INCONCLUSIVE` 并同样导致运行失败 (失败即关闭)。 3. 一旦路径内的输出防护栏存在,它就会作为验证对象替换内置的 `OutputGuardrail` fixture,并且防护栏探针 用例将变为真实的对抗性 prompt。 Live 的代码路径已经存在,因此这只是线路连接问题,而不是新的 架构。请参阅 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。 ## 许可证 在 MIT 许可证下发布。请参阅 [LICENSE](LICENSE)。
标签:ECS, Go语言, Terraform, 出站流量控制, 程序破解, 网络安全, 边界验证, 逆向工具, 隐私保护, 零信任架构