news 2026/9/25 3:50:10

IronClaw 沙箱出口(Sandbox Egress)拓扑 Spike 全记录:双宿主机代理、默认拒绝策略与逐运行时 TLS 信任验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IronClaw 沙箱出口(Sandbox Egress)拓扑 Spike 全记录:双宿主机代理、默认拒绝策略与逐运行时 TLS 信任验证
  • 人工智能
  • AI 应用
  • 交互助手
  • AI Agent

【免费下载链接】ironclaw

IronClaw is an Agent OS focused on privacy, security and extensibility

项目地址:https://gitcode.com/gh_mirrors/iro/ironclaw
点击查看免费下载

本文基于 IronClaw 仓库中 2026-08-19 sandbox egress spike 调研文档 及同目录下的全部结果文件整理而成,并结合ironclaw_sandbox领域源码对设计落地做了对照印证。读者将掌握:如何用--internal网络 + 双宿主机iron-proxy侧车为每个线程容器构建“唯一出口”,如何验证 DNS 拦截、MITM 允许列表、默认拒绝与凭证脱敏,以及 curl/git/Node/Python/pip 各运行时信任自签 CA 的差异与docker exec进程管理的坑。

IronClaw 的沙箱化执行以“每线程一个命令容器”为核心拓扑。为了让这些容器既不能直连公网、又不能访问云元数据地址,同时仍能按策略访问允许列表内的站点,IronClaw 在 2026-08-19 进行了一次代号#7732的 Throwaway spike(一次性验证实验):使用纯 Docker 原生命令与库存镜像(无任何 Rust 改动),验证“internal 网络 + 双宿主机代理侧车”的出口拓扑是否成立。该 spike 共验证 9 个关键假设,全部获得证据支撑,并产出了 8 条对后续 Step 1 实现具有直接指导意义的工程结论。

一、Spike 目标与总体判定

本次 spike 的拓扑设想是:命令容器挂在一个--internalDocker 网络上(该网络没有外部路由),唯一的出口是一个同时挂载 internal 与 egress 两个网络的iron-proxy侧车容器。客户端的所有 DNS 查询与 HTTP/HTTPS 流量都必须经过该代理,由代理基于允许列表决定放行或拒绝。

Spike 环境为macOS + OrbStack,Docker server 29.4.0,代理镜像固定为:

ironsh/iron-proxy@sha256:c4628019c24f4cc8d77564a26b7c9cedb00accee6f93d06270e85fb8f9c6a7da

9 项验证的最终判定如下(完整证据见 results-topology-dns.md、results-policy-credentials.md、results-tls-trust.md、results-exec-mechanics.md):

#验证项判定证据文件
1internal 网络 + 双宿主机代理拓扑PASSresults-topology-dns.md
2--dns <proxy>经 Docker 内嵌 DNS 生效PASSresults-topology-dns.md
3经 MITM(spike CA)的允许列表 TLS 出口PASSresults-policy-credentials.md
4默认拒绝 403 + 结构化 JSON 审计PASSresults-policy-credentials.md + audit-denial-example.jsonl
5直连出口死亡(公网、元数据 IP、无默认路由)PASSresults-topology-dns.md
6占位符→真实凭证替换、require: true、主机绑定、日志无真实字面量PASS(4/4 子检查)results-policy-credentials.md
7逐运行时 TLS 信任矩阵(curl/git/node/python/pip)PASSresults-tls-trust.md
8docker exec流/杀进程/僵尸/延迟机制PASS with findingsresults-exec-mechanics.md
9internal 网络无 IPv6 路由PASSresults-topology-dns.md

此外,spike 明确记录了参与镜像的不可变身份,并诚实标注了证据边界:alpine:3.20与alpine/openssl:latest两个历史 tag 没有记录运行时摘要(RepoDigest),因此这两个输入的精确复现是证据缺口,后续 spike 必须在执行前记录RepoDigests。文中的real-secret-value-42是确定性本地替换 canary(哨兵值),并非真实运营凭证。

二、标准复现配方与 CA 生成

Spike 文档给出了可复用的标准配方。要点是:并发实例必须使用唯一子网(用 N 区分),以免网络冲突。

docker network create --internal --subnet 172.28.N.0/24 <pfx>-int docker network create --subnet 172.29.N.0/24 <pfx>-egress docker run -d --name <pfx>-proxy --network <pfx>-int --ip 172.28.N.2 \ -v $DIR/proxy.yaml:/etc/iron-proxy/proxy.yaml:ro \ -v $DIR/ca.crt:/etc/iron-proxy/ca.crt:ro -v $DIR/ca.key:/etc/iron-proxy/ca.key:ro \ ironsh/iron-proxy@sha256:c4628019c24f4cc8d77564a26b7c9cedb00accee6f93d06270e85fb8f9c6a7da \ -config /etc/iron-proxy/proxy.yaml docker network connect <pfx>-egress <pfx>-proxy docker run -d --name <pfx>-client --network <pfx>-int --dns 172.28.N.2 alpine:3.20 sleep 900

其中<pfx>-int(internal)与<pfx>-egress两个网络分别承担“命令容器面”与“公网出口面”,代理固定 IP 如172.28.N.2,同时作为客户端 DNS 指向。CA 材料每次运行临时生成、刻意不提交仓库。

CA 生成有一个重要经验:macOS 宿主上的 LibreSSL 行为异常,必须改用容器内的 OpenSSL 实现:

docker run --rm -v $DIR:/work -w /work alpine/openssl genrsa -out ca.key 4096 docker run --rm -v $DIR:/work -w /work alpine/openssl req -x509 -new -nodes \ -key ca.key -sha256 -days 3650 -subj /CN=iron-proxy-spike-CA \ -addext basicConstraints=critical,CA:TRUE -addext keyUsage=critical,keyCertSign \ -out ca.crt

生成的 CA 经容器化openssl x509校验:subject=CN=iron-proxy-spike-CA、issuer=CN=iron-proxy-spike-CA、4096 位 RSA、criticalCA:TRUE、criticalCertificate SignKeyUsage。代理启动日志确认了三个监听面与变换管线:

running 0 {"level":"INFO","msg":"upstream deny list active","cidrs":["169.254.169.254/32","fd00:ec2::254/128","127.0.0.0/8","::1/128"]} {"level":"INFO","msg":"iron-proxy starting","dns_listen":":53","http_listen":":80","https_listen":":443","metrics_listen":":9090"} {"level":"INFO","msg":"transform pipeline","transforms":"allowlist"}

值得注意:upstream deny list默认就把云元数据地址169.254.169.254/32、IPv6 链路本地元数据fd00:ec2::254/128、回环地址等纳入拒绝 CIDR,这与项目“防止沙箱窃取云实例凭证”的定位一致。

三、拓扑与 DNS 验证:唯一出口确实成立

3.1 双宿主机拓扑(Item 1)

docker inspect证实代理同时挂载两个网络,而命令面网络是 internal:

{"s7a-egress":{"Gateway":"172.29.10.1","IPAddress":"172.29.10.2",...},"s7a-int":{"Gateway":"","IPAddress":"172.28.10.2",...}} true

Internal=true且 internal 网络无网关,说明该网络上没有任何外部路由。

3.2--dns <proxy>经内嵌 DNS 生效(Item 2)

客户端/etc/resolv.conf显示nameserver 127.0.0.11(Docker 内嵌 DNS),但其注释明确记录了转发目标:ExtServers: [172.28.10.2]。实测中,包括故意不存在的anything.invalid在内,所有名字都解析到代理 IP:

Server: 127.0.0.11 Address: 127.0.0.11:53 Name: example.com Address: 172.28.10.2 Name: anything.invalid Address: 172.28.10.2

这意味着即使客户端尝试访问未允许域名,DNS 也会把它送到代理,由代理在 7 层决定放行或拒绝——拓扑“按设计工作”。

3.3 直连出口死亡(Item 5)

internal 网络上的客户端ip route只有一行:

172.28.10.0/24 dev eth0 scope link src 172.28.10.3

没有默认路由。显式直连尝试全部失败(Network unreachable,EXIT=1),包括http://1.1.1.1、TCP 443 到1.1.1.1、以及云元数据地址169.254.169.254:80。作为阳性对照,走代理访问http://example.com返回 200,证明失败并非拓扑整体故障,而是直连路径确实被切断。

3.4 无 IPv6 路由(Item 9)

客户端仅有 IPv6 回环地址,ip -6 route无任何输出,连接 Cloudflare IPv6 DNS2606:4700:4700::1111失败。Docker 29.4.0 的docker infoGo 模板不暴露.IPv6字段(模板求值直接报错),因此正确做法是逐网络检查EnableIPv6——本 spike 的两个网络均为false。

四、策略与审计:允许列表、默认拒绝与凭证脱敏

4.1 MITM 允许列表出口(Item 3)

客户端使用--cacert /ca.crt访问https://example.com得到 200;-v详细输出证实完整链路:

* Host example.com:443 was resolved. * IPv4: 172.28.20.2 * Server certificate: * subject: CN=example.com * issuer: CN=iron-proxy-spike-CA * subjectAltName: "example.com" matches cert's "example.com" * OpenSSL verify result: 0 < HTTP/1.1 200 OK

即:DNS 把客户端送到代理 → 代理为example.com动态签发由 spike CA 签发的叶子证书 → 客户端信任该 CA 完成验证 → 代理放行上游请求。代理审计日志同时记录mode: mitm, action: allow, status_code: 200。

4.2 默认拒绝与结构化审计(Item 4)

访问未允许域名https://denied.invalid/返回403,且 verbose 输出证明这是代理拒绝而非 DNS 失败(连接成功建立到代理 IP)。代理随后输出结构化 JSON 审计记录,完整样例见 audit-denial-example.jsonl:

{"time":"2026-08-19T11:26:00.564519806Z","level":"WARN","msg":"request","audit":{"host":"denied.invalid","method":"GET","path":"/","remote_addr":"172.28.20.4:34558","sni":"denied.invalid","mode":"mitm","action":"reject","status_code":403,"duration_ms":0.005},"rejected_by":"allowlist","request_transforms":[{"name":"allowlist","action":"reject","duration_ms":0.001}]}

审计记录包含rejected_by字段与逐变换的request_transforms轨迹,这为后续 Step 1 的审计可观测性(trace 溯源)提供了直接模板。

4.3 占位符→真实凭证替换(Item 6,4/4 子检查通过)

这是本 spike 最具“产品价值”的验证:代理在出站请求中把占位符 token 替换为真实凭证,同时保证真实凭证永不落日志。工作配置见 proxy.credentials.yaml,核心是secrets变换:

transforms: - name: allowlist config: domains: ["example.com", "capture.test", "other.test"] - name: secrets config: secrets: - source: { type: env, var: REAL_TOKEN } proxy_value: "icsbx_spike_placeholder_123" match_headers: ["Authorization"] require: true rules: [{ host: "capture.test" }]

四个子检查全部通过:

  • 6a 占位符仅对绑定主机替换:向capture.test发送Authorization: Bearer icsbx_spike_placeholder_123,echo 服务收到的授权头已变为Bearer real-secret-value-42;
  • 6brequire: true拒绝替代 token:向绑定主机发送some-other-token得到 403,审计记录rejected_by: secrets并带 secrets 变换拒绝注解;
  • 6c 同一占位符在未绑定主机原样保留:向other.test发送相同占位符,echo 显示授权头仍是icsbx_spike_placeholder_123,未被替换;
  • 6d 真实凭证零次出现在代理日志:对完整代理日志做字面量、区分大小写的搜索,real-secret-value-42命中次数为 0;成功替换的审计只记录变量名与位置:
{"name":"secrets","action":"allow","annotations":{"swapped":[{"secret":"REAL_TOKEN","locations":["header:Authorization"]}]}}

spike 也诚实标注了边界:只测试了字面量精确匹配,未测试编码、转义或变换后的表示。

4.4 测试专用覆盖项的警示

proxy.credentials.yaml中的upstream_deny_cidrs: []是仅限测试的覆盖项:它允许代理访问私有 echo 容器地址(capture.test/other.test),生产环境必须保留默认拒绝 CIDR。这一警示与 3.2 节中代理启动日志默认激活的 deny list 相互印证。

五、逐运行时 TLS 信任矩阵(Item 7)

允许列表 MITM 的前提是各运行时信任 spike CA。spike 使用nikolaik/python-nodejs:python3.12-nodejs22(摘要sha256:88c41488c1…,内含 curl 8.14.1 / git 2.47.3 / Node v22.23.2 / Python 3.12.13 / pip 26.1.2)实测得出关键矩阵:

运行时系统证书库即可?需要额外环境变量?说明
curl是否安装前也可用--cacert /spike/ca.crt
git HTTPS是否浅克隆只实际访问了github.com
Nodefetch否是必须NODE_EXTRA_CA_CERTS=/spike/ca.crt
Pythonurllib.request是否3.12.13 使用 Debian 系统库
pip 26.1.2是(意外)否CA 入系统库后无环境变量即可;SSL_CERT_FILE=/spike/ca.crt为可用备选

几个关键实验细节:

  • 未信任前 curl 返回(60) SSL certificate problem: self-signed certificate in certificate chain(EXIT=60);--cacert显式指定后 200;
  • cp /spike/ca.crt /usr/local/share/ca-certificates/spike.crt && update-ca-certificates后 curl、git、Python 均通过系统库信任;git 浅克隆github.com/octocat/Hello-World成功,代理日志仅记录 3 个对github.com的 smart-HTTP 请求(info/refs+ 2 个git-upload-pack),更深的克隆 / LFS 需要完整资产主机星座(codeload、objects.githubusercontent 等,它们在允许列表里但本 fixture 未用到);
  • Node v22 忽略系统证书库,即使update-ca-certificates成功,fetch("https://example.com")仍报SELF_SIGNED_CERT_IN_CHAIN;设置NODE_EXTRA_CA_CERTS=/spike/ca.crt后返回 200;
  • pip 26.1.2 是“意外惊喜”:CA 进入系统库后无需任何环境变量即可pip download six;控制实验(移除系统库中的 spike CA)证明失败确实来自系统信任缺失,而仅用SSL_CERT_FILE也能成功。

由此得出 Step 1 的两条明确工程指令:

  1. 不要全局设置SSL_CERT_FILE指向仅含代理 CA 的 bundle——它会影响所有运行时且覆盖面不对称;应采用合并 CA bundle,或将覆盖范围限定到确实需要的运行时(Node);
  2. worker 镜像必须校验NODE_EXTRA_CA_CERTS指向的路径真实存在。

六、docker exec进程与流机制(Item 8)

该任务回答“每线程容器 + 每次命令一次 exec”经济模型下的关键问题。测试容器为alpine:3.20,PID 1 是刻意选择的不回收(non-reaping)命令sleep 1800。

6.1 后台子进程继承 stdout 的“挂起”假设未复现

原假设是“后台子进程继承 stdout 会导致 exec 无限挂起”。在 Docker 29.4 上未复现:sh -c 'sleep 300 & echo done'返回done且约2.08 秒完成(而非 20 秒超时)。结论:该版本下继承流只带来约 2 秒代价,不产生挂起。

6.2 重定向足以立即关闭流,setsid 用于杀进程树

  • 仅重定向:setsid之前先测sleep 300 >/dev/null 2>&1 </dev/null & echo done→ 0.067 秒完成;
  • 重定向 +setsid:0.127 秒完成;
  • 因此ironclaw-exec需要两者兼得:重定向负责流即时关闭(延迟),setsid负责形成独立进程组以便组杀。

6.3 组杀与僵尸

  • 退出码传播精确(exit 7→code=7);
  • BusyBox 语法坑:kill -TERM -- -34被 Alpine BusyBoxsh内建拒绝(sh: invalid number '--',kill_code=1),正确写法是kill -TERM -34(kill_code=0);
  • 组杀信号确实送达:PGID 34 的 shell 与两个 sleep 全部从S(sleeping)变为Z(zombie),而无关的分离进程(PGID 17)不受影响;
  • 严格判定下该子检查 FAIL:由于 PID 1 不回收,被杀进程全部残留为僵尸,无法满足“进程条目消失”的严格条件。5 秒后僵尸仍存在,僵尸累积被直接演示。worker 镜像必须引入 tini 或等效的 reaping init,否则每次组杀都会堆积僵尸。

6.4 exec 延迟基准

5 次docker exec ... true实测:60 / 40 / 30 / 30 / 30 ms,均值 38 ms、中位数 30 ms。这为“每线程容器 + 每命令一次 exec”的开销提供了量化依据。

七、与 IronClaw 源码的落地印证

该 spike 的结论已经在ironclaw_sandbox领域代码中落地,可从源码佐证其工程化方向:

  • managed_egress.rs 中以常量钉死代理镜像摘要(ironsh/iron-proxy@sha256:c462…,与 spike 完全一致),并强制要求镜像必须按 sha256 摘要 pin、pull 后必须校验不可变镜像 ID(对应 spike 的“记录 RepoDigests”要求);
  • 代理配置与材料统一挂载到宿主机侧/run/ironclaw-proxy/目录(proxy.yaml、ca.crt、ca.key、credentials.json、invocation-id),并定义PROXY_LABEL_PREFIX = "ironclaw.proxy"标签前缀用于识别侧车容器;
  • 代码中暴露给用户的 CA 路径为/run/ironclaw/egress-ca.crt(USER_PROXY_CA_PATH),与 spike 中“把 spike CA 挂进客户端供各运行时信任”的做法一脉相承;
  • 策略侧,network_allowlist.rs与managed_egress.rs共同保证“托管出口策略必须至少包含一个允许目标、必须拒绝私网 IP 段”,对应 spike 中默认 deny list 与upstream_deny_cidrs测试覆盖项的生产警示。

八、Step 1 落地清单(Spike 结论汇总)

综合全部结果,Step 1 实现应满足以下要求:

  1. 每线程命令容器挂在--internal网络,无默认路由;唯一的出口是双宿主机iron-proxy侧车;
  2. 客户端--dns <proxy-ip>指向代理,借助 Docker 内嵌 DNS 将所有名字(含.invalid)统一送到代理;
  3. 代理默认拒绝非允许列表域名(403),并输出含rejected_by与变换轨迹的结构化 JSON 审计;
  4. 凭证注入采用“占位符 → 真实值”的主机绑定替换,require: true防替代 token,真实值永不落日志;
  5. worker 镜像信任代理 CA:系统证书库覆盖 curl/git/python/pip,Node 必须显式NODE_EXTRA_CA_CERTS;禁止全局SSL_CERT_FILE指向仅代理 CA 的 bundle;
  6. ironclaw-exec同时使用全重定向(流即时关闭)与setsid(进程组隔离),组杀采用 BusyBox 兼容写法kill -TERM -<PGID>,镜像引入 tini 级 reaping init 防止僵尸累积;
  7. 网络层同时关闭 IPv6 路由,避免 internal 网络出现第二条出口;
  8. 复现实验必须记录全部输入镜像的不可变摘要(RepoDigests),并保留所有审计与日志证据。

九、结论

这是一次证据链完整、边界标注诚实的工程 spike:9/9 假设通过(其中“组杀后进程消失”在非 reaping PID 1 下按严格标准判为 FAIL,属于“信号生效但僵尸残留”的机制性发现),为 IronClaw 的“每线程容器唯一受控出口”架构扫清了从设计到落地的关键风险。文档中所有命令块保持“当时实际执行”的原貌,未用事后拉取的摘要改写历史证据,这种可审计性本身就是值得复用的实验方法论。若需复现或深化,完整配方、配置样例与证据文件均可从 spike 目录 直接获取。

  • 人工智能
  • AI 应用
  • 交互助手
  • AI Agent

【免费下载链接】ironclaw

IronClaw is an Agent OS focused on privacy, security and extensibility

项目地址:https://gitcode.com/gh_mirrors/iro/ironclaw
点击查看免费下载

相关推荐

上一篇:Trigger.dev Webhook Provider 调研实战:签名方案分类、预设复用与 Webhook 目录工程化
下一篇:终极指南:screenFetch系统信息工具安全风险评估与防护

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:48:34

CTF实战写作规范:为何虚构赛事不能写实操指南

我无法基于“2026年第一届创宇网络安全技能大赛”这一标题生成符合要求的博文内容。原因如下&#xff1a;该标题指向一个尚未举办的、虚构或预告性质的赛事活动&#xff0c;不构成可实操、可复现、可深度拆解的技术项目。根据您设定的核心创作原则&#xff1a;所有内容必须“忠…

作者头像 李华
网站建设 2026/9/25 3:46:31

Claude Code模板实战:CLAUDE.md与斜杠命令打造AI编码助手记忆

1. 为什么说模板才是Claude Code的灵魂在GitHub上搜索claude-code-templates这个关键词的时候&#xff0c;你会发现一件有意思的事&#xff1a;大家不约而同地在做同一件事——把零散的AI编程经验固化成一整套可复用的模板。这说明Claude Code这类工具用久了之后&#xff0c;所…

作者头像 李华
网站建设 2026/9/25 3:45:36

基于SVM的降水量预测模型实战:SVR回归、特征构造与调参要点

简介&#xff1a;一套基于支持向量机&#xff08;SVM&#xff09;的降水量预测模型代码包&#xff0c;面向机器学习、人工智能及数据挖掘方向的初学者和研究人员&#xff0c;可用于算法复现、实验对比和毕业设计参考。资源内共 54 个文件&#xff0c;以 26 个 .m 主程序为核心&…

作者头像 李华
网站建设 2026/9/25 3:44:02

CISP认证含金量与备考指南:从网安高薪岗位到学习路线一次讲清

每年年中和年底&#xff0c;我都能在朋友圈里看到两类完全对立的帖子&#xff1a;一类是刚入行的安全新人晒offer&#xff0c;标题大概是“网安行业高薪岗位真的多&#xff0c;终于上岸了”&#xff1b;另一类是干了三五年还在原地打转的老哥吐槽“证书没用、内卷严重、投简历秒…

作者头像 李华