适用场景
本方案适合在本机或内网服务器上运行编码智能体、代码补全工具、自动化脚本的开发者与运维人员。核心目标是:当智能体在后台读取仓库、调用远端模型或遥测接口时,能看清它到底把哪些数据发到了哪里,并在发现异常上传时直接阻断。以“ZCode 式静默上传 Git 历史”为切口,用出口代理、进程日志、权限隔离三件套,搭一套可复用的外传审计配置。
环境与前置条件
- 操作系统:主流 Linux 发行版,内核支持 nftables 与 auditd。Windows/macOS 可参考思路,但命令需按平台调整。
- 运行时:Python 3,用于运行 mitmproxy;pipx 或虚拟环境均可。具体版本要求以 mitmproxy 官方文档当前版本为准。
- 权限:需要 root 或 sudo,用于创建专用用户、写 nftables 规则、配置 auditd。
- 磁盘:代理日志与审计日志会持续增长,建议预留数 GB,并配置轮转。
- 内存:代理常驻内存通常在几百 MB 以内,以实际运行和官方说明为准。
- 网络:智能体只允许访问出口代理端口,代理再按白名单访问外部模型服务域名。
分步骤部署
1. 安装基础组件
安装 nftables、auditd、Python 工具链。包名以发行版仓库为准。
```bash
sudo apt update
sudo apt install -y nftables auditd python3 python3-pip pipx
pipx install mitmproxy
```
确认命令可用:
```bash
which mitmdump
sudo systemctl status auditd
sudo nft --version
```
mitmdump 能输出版本信息、auditd 处于 active、nft 能正常执行,即视为成功。
2. 创建专用用户与目录
不要让智能体以你的日常账号运行,也不要让代理以 root 运行。创建两个系统用户:zagent 跑智能体,proxyuser 跑代理。
```bash
sudo useradd --system --create-home --shell /bin/bash zagent
sudo useradd --system --create-home --shell /usr/sbin/nologin proxyuser
sudo mkdir -p /srv/agent-work /etc/mitmproxy /var/log/agent-egress
sudo chown root:root /srv/agent-work
sudo chmod 755 /srv/agent-work
sudo chown proxyuser:proxyuser /var/log/agent-egress
sudo chmod 750 /var/log/agent-egress
```
3. 准备最小仓库视图
静默上传的前提通常是智能体能读到完整 .git 历史。给它一个浅克隆,并去掉写权限,能显著缩小暴露面。
```bash
sudo mkdir -p /srv/agent-work/project
sudo chown zagent:zagent /srv/agent-work/project
sudo -u zagent git clone --depth 1 --single-branch https://example.com/your/repo.git /srv/agent-work/project
sudo chown -R root:root /srv/agent-work/project
sudo chmod -R go-w /srv/agent-work/project
sudo mkdir -p /srv/agent-work/workspace
sudo chown zagent:zagent /srv/agent-work/workspace
```
这样智能体只能读项目文件,临时输出写到 workspace,没有完整历史可传。
4. 配置出口代理与白名单
创建审计 addon。把 api.example-model.com 换成实际使用的模型服务域名,白名单只放必须访问的域名。
```bash
sudo tee /etc/mitmproxy/egress_audit.py >/dev/null <<'PY'
import json
import time
from mitmproxy import http
ALLOW = {
"api.example-model.com",
"registry.npmjs.org",
"pypi.org",
}
LOG = "/var/log/agent-egress/access.jsonl"
def _allowed(host: str) -> bool:
return any(host == d or host.endswith("." + d) for d in ALLOW)
def _write(record: dict) -> None:
with open(LOG, "a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
def http_connect(flow: http.HTTPFlow) -> None:
host = flow.request.host
_write({"ts": time.time(), "type": "connect", "host": host})
if not _allowed(host):
flow.response = http.Response.make(403, b"blocked by egress audit\n")
def request(flow: http.HTTPFlow) -> None:
host = flow.request.pretty_host
body = flow.request.raw_content or b""
_write({
"ts": time.time(),
"type": "request",
"method": flow.request.method,
"host": host,
"path": flow.request.path,
"body_size": len(body),
})
if not _allowed(host):
flow.response = http.Response.make(403, b"blocked by egress audit\n")
PY
```
启动代理服务。ExecStart 里的 mitmdump 路径用 which mitmdump 的结果替换。
```bash
sudo tee /etc/systemd/system/agent-egress-proxy.service >/dev/null <<'EOF'
[Unit]
Description=Agent egress audit proxy
After=network.target
[Service]
User=proxyuser
Group=proxyuser
ExecStart=/home/proxyuser/.local/bin/mitmdump -s /etc/mitmproxy/egress_audit.py --listen-host 127.0.0.1 --listen-port 8080
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now agent-egress-proxy
```
5. 用 nftables 限制智能体出网
只允许 zagent 访问本机代理端口,其余出站流量丢弃。这样即使智能体不认代理,也发不出去。
```bash
sudo nft add table inet agent_egress
sudo nft add chain inet agent_egress output '{ type filter hook output priority 0; policy accept; }'
sudo nft add rule inet agent_egress output meta skuid zagent ip daddr 127.0.0.1 tcp dport 8080 accept
sudo nft add rule inet agent_egress output meta skuid zagent ip6 daddr ::1 tcp dport 8080 accept
sudo nft add rule inet agent_egress output meta skuid zagent drop
```
持久化规则。若本机已有 nftables 配置,请合并,不要直接覆盖。
```bash
sudo sh -c 'nft list ruleset > /etc/nftables.conf'
sudo systemctl enable --now nftables
```
6. 配置 auditd 进程与文件读取日志
记录 zagent 执行过的命令,以及它对 .git 目录的读取行为。
```bash
AGENT_UID=$(id -u zagent)
sudo tee /etc/audit/rules.d/agent.rules >/dev/null <<EOF
-a always,exit -F arch=b64 -S execve -F uid=$AGENT_UID -k agent_exec
-w /srv/agent-work/project/.git -p r -k agent_git_read
EOF
sudo augenrules --load
sudo systemctl enable --now auditd
```
7. 以专用用户启动智能体
在子 shell 中显式设置代理环境变量,再启动智能体。启动命令按实际工具替换。
```bash
sudo -u zagent -H bash -lc '
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
export NO_PROXY=127.0.0.1,localhost
cd /srv/agent-work/workspace
此处替换为实际编码智能体的启动命令
'
```
验证部署是否成功
1. 代理是否在监听:
```bash
ss -lntp | grep 8080
sudo systemctl status agent-egress-proxy
```
预期看到 127.0.0.1:8080 处于 LISTEN,服务 active。
2. 放行域名测试:
```bash
sudo -u zagent env HTTPS_PROXY=http://127.0.0.1:8080 \
curl -sS -o /dev/null -w "%{http_code}\n" https://api.example-model.com
```
预期返回 200、401、403 等 HTTP 状态码,而不是连接失败。若返回 blocked by egress audit,说明该域名不在白名单。
3. 阻断测试:
```bash
sudo -u zagent env HTTPS_PROXY=http://127.0.0.1:8080 \
curl -sS https://example.net
```
预期返回 blocked by egress audit。
4. 直连测试:
```bash
sudo -u zagent curl -sS --max-time 5 https://example.net
```
预期连接超时或被拒绝,因为 nftables 只允许该用户访问代理端口。
5. 查看代理日志:
```bash
tail -n 5 /var/log/agent-egress/access.jsonl
```
预期看到 JSON 行,包含 host、method、body_size 等字段。
6. 查看审计日志:
```bash
sudo ausearch -k agent_exec -ts recent | tail -n 20
sudo ausearch -k agent_git_read -ts recent | tail -n 20
```
预期看到 zagent 执行的命令,以及读取 .git 的记录。
常见报错与解决
1. 报错:curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused
原因:代理没有启动,或监听地址/端口不一致。
解决:
```bash
sudo systemctl restart agent-egress-proxy
ss -lntp | grep 8080
```
2. 报错:curl: (60) SSL certificate problem: unable to get local issuer certificate
原因:mitmproxy 要解密 HTTPS,但客户端不信任它的 CA。
解决:把 mitmproxy 的 CA 证书加入对应运行时的信任库。不同语言环境变量不同,例如 CURL_CA_BUNDLE、NODE_EXTRA_CA_CERTS、REQUESTS_CA_BUNDLE。具体路径与导入方式以 mitmproxy 官方文档当前版本为准。
3. 报错:Error: Could not process rule: Operation not permitted
原因:nftables 规则需要 root,或内核模块未加载。
解决:
```bash
sudo nft list ruleset
sudo modprobe nf_tables
```
4. 报错:ausearch: no matches 或审计规则为空
原因:auditd 未运行,规则未加载,或 uid 写错。
解决:
```bash
sudo systemctl enable --now auditd
sudo auditctl -l
AGENT_UID=$(id -u zagent)
sudo auditctl -a always,exit -F arch=b64 -S execve -F uid=$AGENT_UID -k agent_exec
```
5. 报错:Permission denied 读取 /srv/agent-work/project/.git
原因:权限隔离生效,zagent 对完整 .git 没有写权限或没有读权限。
解决:若确实需要读取,使用 ACL 单独授权;更推荐保持只读,把需要修改的内容放到 workspace。
后续维护
- 日志轮转:为
/var/log/agent-egress/*.jsonl和 auditd 日志配置 logrotate,避免磁盘写满。 - 白名单更新:编辑
/etc/mitmproxy/egress_audit.py后重启agent-egress-proxy,不要把未知域名直接放行。 - 定期审计:每周查看
ausearch -k agent_exec -ts this-week和代理 JSONL,重点关注大body_size、异常域名、非工作时段请求。 - 升级:mitmproxy、nftables、auditd 的升级方式以官方文档当前版本为准;升级前备份配置。
- 备份:备份
/etc/mitmproxy/、/etc/nftables.conf、/etc/audit/rules.d/、/var/log/agent-egress/。 - 监控:把代理做成 systemd 常驻服务并开启
Restart=on-failure,同时监控进程存活、端口监听和日志磁盘占用。
