- add references/safe-operation.md: credentials, client settings, secrets, blast radius, monitoring, incident response, update discipline - SKILL.md, READMEs, AGENTS.md, llms.txt now present four parts: deploy, egress, operate, respond — gate remains the enforcement core - version 1.3.0, installer pin updated
15 KiB
claude-egress-guard(中文说明)
一站式 skill + 一键安装:在 Linux 上安全地部署和运行 Claude Code——从裸机到可用、 可审计的客户端,以及出问题时知道怎么处置。
在 VPS 上跑 Claude Code,最容易出问题的不是安装,而是出口泄漏:写在
/etc/profile.d 里的 claude() 函数只覆盖交互式 shell,Python、sh -c、
cron、systemd 和任何直接调用 /usr/bin/claude 的路径都会绕过去;代理中途挂掉时,
客户端还会悄悄从机房 IP 直连出去。
所以项目分成四块,只有一个是强制核心:
| 部分 | 覆盖什么 | 入口 |
|---|---|---|
| 部署 | 资格判断、主机基线、代理先稳定、装客户端但不启动、首次登录走闸门 | preflight、zero-to-deployed.md |
| 出口 | 固定出口、注入代理、内核白名单、更新自愈、失败不回落 | install、verify --deep、drill、set-exit |
| 运行 | 凭据、客户端设置、密钥卫生、影响范围、备份、监控 | safe-operation.md |
| 处置 | 出口变化、被要求验证、凭据泄漏、怀疑绕过 | safe-operation.md §6 |
强制核心仍然是二进制闸门:claude 命令本身核验代理的真实出口地址、注入代理变量,
可选地把整个进程树关进内核级 IP 白名单。任何异常都拒绝启动,没有回落。另外三块存在
的意义,是让这道闸门按正确顺序装上,并且在第一周之后仍然有意义。
给 agent:从这里开始
这个仓库是给 agent(Hermes、Codex、Claude Code 或任何支持 skill 的运行器)消费的, 不只是给人读的 README。
# 把 skill 装进 agent 的 skills 目录(不需要 root,不改主机)
curl -fsSL https://raw.githubusercontent.com/yinyues2/claude-egress-guard/main/install.sh \
| bash -s -- --skill-only --dest ~/.hermes/skills
然后对 agent 说:
用
claude-egress-guard这个 skill:先问我代理入口、预期出口地址、国家、额外网段, 然后安装、跑verify --deep、做一次故障演练,最后把 PASS/FAIL 行贴给我。
skill 里写清的约束(SKILL.md):
- 只问 4 个事实——代理入口、预期出口地址、国家、额外白名单;其余从主机自己读,不重复追问;
- 三档授权——只读诊断直接做;安装/验证/演练/卸载整计划批一次;可能切断操作者自身访问的操作当场确认;
- 证据而非形容词——每条结论都要贴对应的 PASS 行,演练结果要记录,没输出不算通过;
- 硬性禁止——不停 ssh/tailscale/docker 单元,不把
ALLOW_IPS放大到0.0.0.0/0,不为通过检查而关闭某一层,不碰账号/付款/身份核验。
配套文件:AGENTS.md(机器入口)、
references/agent-playbook.md
(带判断点的分步手册)、llms.txt(给 LLM 工具用的链接索引)、
references/troubleshooting.md。
从零开始:新机器、还没注册过
下面的内容默认 Claude Code 已经装好并登录过。如果是从零开始,顺序决定第一次
部署安不安全:references/zero-to-deployed.md
给了九个阶段,并标明每一阶段由谁执行。
0 资格与环境判断:一人一号、一个固定出口 操作者
1 主机基线:时钟、时区、磁盘、IPv6 决策 agent
2 先把代理跑通并确认稳定,再谈注册 agent
3 安装 Claude Code,装完不要启动 agent
4 装闸门,verify --deep + drill agent
5 第一次登录:走闸门,在你自己的浏览器完成 操作者
6 首次设置:客户端里不要再单独设代理 操作者 + agent
7 留证:记录证据、打开拒绝日志 agent
8 日常:升级后 verify,出口漂移用 set-exit agent
claude-egress-guard preflight --proxy http://127.0.0.1:1080 --exit-ip 203.0.113.10 --country JP
preflight 只读:检查平台、systemd、bash、时钟、时区、IPv6、磁盘、Node、
Claude Code,给了代理还会实测出口,最后输出 PASS/WARN/FAIL 和阻塞项数量。
出现 FAIL 就不要继续。
注册账号、付款、身份核验故意不做自动化:skill 明确禁止 agent 代办,runbook 会 告诉操作者在什么情况下应当停下来,而不是硬凑材料。
一键安装
curl -fsSL https://raw.githubusercontent.com/yinyues2/claude-egress-guard/main/install.sh \
| sudo bash -s -- --proxy http://127.0.0.1:1080 --exit-ip 203.0.113.10 --country JP
这条命令会下载固定版本的发布包、打印安装计划、安装闸门、备份被替换的文件,最后自动跑
verify --deep。任何一层失败就停下并指出是哪一层,不会把装了一半的闸门当成功。
| 参数 | 含义 |
|---|---|
--proxy URL |
本地 HTTP/mixed 代理入口(必填) |
--exit-ip ADDR |
代理出口的公网 IPv4(必填) |
--country CC |
要强制的两位国家码(可选) |
--allow LIST |
内核白名单里额外放行的网段,逗号分隔 |
--strict-net on|off |
是否给进程树加内核白名单(默认 on) |
--dry-run |
只打印计划,不写任何东西 |
--yes |
跳过确认提示(自动化用) |
--ref REF |
指定 tag 或分支(默认固定 tag) |
--uninstall |
移除闸门并从备份恢复 |
--skill-only |
只把 skill 复制到 skills 目录 |
203.0.113.10 是文档专用地址(RFC 5737),请换成你代理实际报告的地址。回环和主机
IPv4 DNS 解析器会自动放行;代理上游、Tailscale 网段、内网 API 用 --allow 显式加。
给 root 之前先读一遍。 install.sh 是不混淆的纯 shell 脚本,下载的是固定 tag 的
压缩包,不是移动的分支。也可以先 clone 再 sudo ./install.sh ...。
卸载走同一条通道:
curl -fsSL https://raw.githubusercontent.com/yinyues2/claude-egress-guard/main/install.sh \
| sudo bash -s -- --uninstall
装好之后是什么样
$ sudo claude-egress-guard verify --deep
PASS config present: /etc/claude-egress-guard.conf
PASS real binary executable
PASS gate installed: /usr/local/bin/claude
PASS gate mirrored: /usr/bin/claude
PASS egress check: ok exit=203.0.113.10 country=JP
PASS kernel jail enabled, allow list: 127.0.0.0/8 ::1/128 192.0.2.53/32
PASS systemd-run available
PASS IPv6 disabled system-wide
PASS jail blocks direct egress
PASS jail still allows the proxy path
---- 0 check(s) failed
每一行都是实测观察,不是「配置里写了」:最后两行是真的在白名单里发请求验证的。
claude(任意入口:shell、Python、cron、systemd、绝对路径)
└─ /usr/local/bin/claude 闸门:核验出口 -> 注入代理 -> 内核白名单
├─ /usr/bin/claude 同一份闸门(npm 更新会改回符号链接,闸门自愈)
└─ /opt/claude-real/claude 真身(符号链接,跟随包更新)
└─ 你的代理 127.0.0.1:1080 -> 预期出口地址
每次启动的顺序:
- 读
/etc/claude-egress-guard.conf,缺失或不可读 → 拒绝(77); - 如果包更新把
/usr/bin/claude改回符号链接,先补回闸门; - 问代理要真实出口地址和国家,不符/超时/HTTP 错误 → 拒绝(77),不重试、不回落直连;
- 把
ALL_PROXY/http_proxy/https_proxy(大小写)导出给客户端和所有子进程,NO_PROXY只留回环; STRICT_NET=1时,在临时 systemd scope 里以IPAddressDeny=any+ 白名单 exec, 子进程继承的是内核级限制,不是口头承诺。
诊断路径不碰网络:--version、--help、--ceg-check 在探测之前就短路返回。
保证什么
| 层 | 行为 |
|---|---|
| 出口核验 | 每次启动前核验地址和国家;不符、超时、报错一律退出码 77 |
| 代理环境 | 六个代理变量导出给客户端和全部子进程 |
| 内核白名单 | 整个进程树 IPAddressDeny=any,只放行回环、白名单和显式允许的网段 |
| 入口覆盖 | /usr/local/bin/claude 和 /usr/bin/claude 都是闸门,真身放在 /opt/claude-real |
| 自愈 | 包自更新改写的 /usr/bin/claude 会在下一次调用时补回 |
| fail-closed | 配置缺失、检查器缺失、代理不通、出口不符、国家不符、没有 systemd-run、非 root 且 STRICT_NET=1:全部拒绝 |
| 审计线索 | LOG_REFUSALS=1 时每次拒绝写入 journald,tag claude-egress-guard |
| 可回滚 | 被替换的文件都在 /var/lib/claude-egress-guard/backup/<时间戳>/,uninstall 恢复 |
环境要求与兼容性
| 项目 | 要求 |
|---|---|
| 系统 | Linux + systemd 235+(IPAddressDeny= 需要) |
| Shell | bash 4.3+ |
| 工具 | curl、install、readlink、mktemp、awk、sed |
| 权限 | 正式安装要 root;--prefix 可做无特权沙箱 |
| Claude Code | 已安装,npm 全局或官方安装器均可 |
| 代理 | 本地 HTTP 或 mixed 入口(只有 SOCKS 的话,客户端代理变量用不了) |
已实测:Debian 13 / systemd 257 / Claude Code 2.1.296(npm 全局),代理在回环上的 住宅 HTTP 入口。CI 在 Ubuntu 24.04 上跑全套。
未验证、不支持:macOS/BSD、WSL、systemd 235 以下、非 root 用户 scope 下开
STRICT_NET=1、容器内的 Claude。
每次启动成本:一次经代理的 HTTPS 往返(视距离约 100-400 ms);--version/--help 不探测。
命令
| 命令 | 用途 |
|---|---|
preflight [--proxy URL --exit-ip ADDR --country CC] |
新机器只读体检,含实时出口探测;--json 给工具用 |
install ... |
打印计划、备份、安装(支持 --dry-run) |
verify |
静态检查 + 实时出口核验 |
verify --deep |
额外证明「白名单挡住直连、仍放行代理」 |
status |
查看当前策略(代理凭据打码) |
set-exit --ip ADDR [--country CC] |
跟随变化的出口:先探测,代理确实在该地址才改策略,再自动 verify |
drill --service UNIT |
停代理单元,证明闸门拒绝,再拉起并等待恢复 |
show <guard|check|config> |
打印已安装的闸门/检查器/策略,便于审计 |
uninstall |
从最新备份恢复并移除闸门 |
退出码:0 成功,1 校验失败,2 用法错误,77 闸门拒绝启动。
出口变了怎么办
住宅出口会漂。别手改配置,让闸门跟着走:
sudo claude-egress-guard set-exit --ip 198.51.100.23 --country JP
它先用候选地址探测代理,代理确实在该地址才原子改写 EXPECTED_IP/EXPECTED_COUNTRY
(权限保持 0600),然后自动跑 verify;不爆料就拒绝,不会把没验证过的地址写进策略。
不知道新地址时先看一眼:
curl -4 -fsS --max-time 8 -x http://127.0.0.1:1080 https://www.cloudflare.com/cdn-cgi/trace
取输出里的 ip= 和 loc=。
拒绝日志
拒绝默认只出现在终端,没人盯着就看不见。开一次 journald 记录:
sudo sed -i 's/^LOG_REFUSALS=0/LOG_REFUSALS=1/' /etc/claude-egress-guard.conf
之后:
journalctl -t claude-egress-guard --since -24h
每条是 refused: <原因>。这决定了你是「我觉得没拒绝过」还是「它拒绝过 11 次,都在这」。
日常维护
| 事件 | 动作 |
|---|---|
npm i -g 或 Claude Code 自更新 |
sudo claude-egress-guard verify --deep(备用路径会自动补回) |
| 代理出口变了 | sudo claude-egress-guard set-exit --ip 新地址 --country CC |
| 代理改走远程中转 | 用 --allow 加中转地址,再 verify --deep |
| 不要内核白名单 | 设 STRICT_NET=0 再 verify(会出现 WARN),进程树出口不再强制 |
| 怀疑有绕过 | status,然后看 /proc/<pid>/cgroup 里有没有 run-r...scope |
不做什么
- 不创建、不管理代理。 入口要你自己提供并保持存活。
- 只闸一个二进制。
npx @anthropic-ai/claude-code不经过/usr/local/bin/claude, 因此不受保护;要么别这么用,要么自己再包一层。另一份装在别的前缀里的 Claude 同样不受保护。 - 不是 VPN。 主机上其他服务、包管理器、其他用户不受影响,白名单只管 Claude 进程树。
- 挡不住 root。 有 root 就能设
STRICT_NET=0、改配置或直接跑/opt/claude-real/claude。 这是护栏,不是强制访问控制边界。 - 不覆盖容器。 Docker/Kubernetes 的流量不经过主机的出口路径。
- 不保证账号安全。 注册、付款、身份核验和平台判定都由账号本人承担;这个项目不用于规避服务商条款。
完整威胁模型见 SECURITY.md。
从源码手动安装
git clone https://github.com/yinyues2/claude-egress-guard.git
cd claude-egress-guard
# 一条命令,参数和 curl 版一致
sudo ./install.sh --proxy http://127.0.0.1:1080 --exit-ip 203.0.113.10 --country JP
# 或者直接驱动 CLI
sudo skills/claude-egress-guard/scripts/claude-egress-guard install \
--proxy http://127.0.0.1:1080 --exit-ip 203.0.113.10 --country JP --dry-run
开发与测试
make check # shellcheck + 44 项自测 + 泄漏扫描
make test # 只跑沙箱自测(不需要 root,不联网)
make lint
make scan
自测在临时前缀里用假的 curl/id/logger/systemd-run 跑,不碰主机。
仓库结构
install.sh 一键安装(下载固定 tag)
AGENTS.md agent 运行器的机器入口
llms.txt 给 LLM 工具的链接索引
skills/claude-egress-guard/
SKILL.md 部署 skill 本体
references/ playbook 与故障排查
scripts/claude-egress-guard 安装/验证/演练/卸载 CLI
scripts/guard-claude.sh 闸门本体(安装为 `claude`)
scripts/egress-check.sh 出口检查器(可单独运行)
scripts/selftest.sh 44 项沙箱断言
templates/ 策略文件示例
tools/leak-scan.py 拦截凭据、个人标识和非文档网段
许可、安全、隐私
Apache-2.0,见 LICENSE。安全模型与漏洞报告见 SECURITY.md, 数据收集见 PRIVACY.md(简短版:不收集任何东西,只探测你自己的代理)。