跳到主内容
快讯直播
AI智模界
教程

上手 Grok Build:仓库接入、任务下发与出网审计

这套流程跑完之后,你会得到什么

先把终点说清楚。按下面的步骤做完,你手里会有四个能直接跑的东西:

1. 一个跑不出网的沙箱容器:编码智能体(下文简称“智能体”)在里面读代码、改代码。容器只连一条内部网络,唯一的出口是一个带白名单的 HTTP 代理。

2. 一份出网审计日志:智能体每一次对外请求都会留下“谁、什么时候、访问了哪个域名、成功还是被拒”的记录,按域名聚合一眼就能看出它有没有把代码往奇怪的地方送。

3. 一道密钥扫描闸门:仓库进门之前扫一遍、提交出门之前再扫一遍,把“智能体顺手把 .env 内容写进补丁”这类事故挡在合并之前。

4. 一套收敛过的权限:单一仓库、只读起步、只能开 PR 不能推主干、密钥不落在镜像层里。

一句话总结目标:让智能体在真实仓库里干活,但它的能见范围和能去范围都被写死。

前置条件清单

动手前确认这些都在手边:

  • 一台 Linux 或 macOS 开发机 / CI runner,装好 Docker,当前用户能跑 docker 且免 sudo;
  • 一个可以克隆的 Git 仓库,最好已经接了 CI;
  • 一个 Grok 的 API Key(在 xAI 控制台申请,具体入口和额度以官方页面为准);
  • 智能体本身的安装方式与启动命令:以官方文档当前版本为准。下文用 $AGENT_CMD 代指官方给出的启动命令,你把它替换成实际的那条即可;
  • 开源工具:gitleaks(密钥扫描)、squid 或 mitmproxy(出网代理)、jq(日志处理)。安装方式以各自官方文档为准;
  • 仓库的 fine-grained token 权限:Contents: Read(先只给读)、Pull requests: Write、Metadata: Read。

建议先在个人测试仓库上走一遍,跑通再换生产仓库。

步骤一:先建隔离网络,再谈接仓库

顺序很重要。很多人是先接仓库、后想隔离,结果在“已经能跑”的路径上再加限制,很容易被自己绕过去。

先建两条网络:一条内部网络(无外网),一条给代理用:

```bash

内部网络:没有默认路由,容器里 curl 任何外网地址都会失败

docker network create --internal agent-internal

代理网络:普通 bridge,代理容器同时接入两条网络

docker network create agent-egress

```

写一个 Dockerfile,把智能体和常用工具装进去,注意三件事:非 root 用户、不把密钥写进镜像、工作目录固定:

```dockerfile

Dockerfile.agent

FROM node:20-bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \

git curl ca-certificates jq python3 \

&& rm -rf /var/lib/apt/lists/*

安装智能体:按官方文档给出的方式,这里用占位

RUN npm i -g <官方包名> # 以官方文档当前版本为准

RUN useradd -m -u 1000 agent

USER agent

WORKDIR /work/repo

```

构建:

```bash

docker build -f Dockerfile.agent -t agent-sandbox:local .

```

关键点:容器启动时用 --network agent-internal,它连 DNS 都解析不出去,所有出网只能走代理。这不是“建议”,是物理隔离。

步骤二:用白名单代理兜住出网,并留下日志

代理容器是唯一同时接两条网络的角色。用 squid 做一个只放行必要域名的正向代理,并把日志打成制表符分隔,方便后面 awk。

```conf

squid.conf

logformat agentlog %ts.%03tu%t%>a%t%rm%t%ru%t%<st%t%>st

access_log /var/log/squid/access.log agentlog

http_port 3128

只允许访问模型 API 和代码托管,其余一律拒绝

acl allowed_domains dstdomain .x.ai .github.com .githubusercontent.com

acl safe_ports port 443

acl safe_ports port 80

http_access allow allowed_domains safe_ports

http_access deny all

关闭缓存,审计日志要的是全量流水

cache deny all

```

启动代理,把日志目录挂出来:

```bash

docker run -d --name agent-proxy \

--network agent-egress \

-v "$PWD/squid.conf:/etc/squid/squid.conf:ro" \

-v "$PWD/logs:/var/log/squid" \

ubuntu/squid

让代理容器也能进内部网络

docker network connect agent-internal agent-proxy

```

这里有个容易被忽略的点:不需要解密 TLS 也能做审计。代理在 CONNECT 阶段就能看到目标域名、时间、字节数和状态码,对“有没有外传”这个判断已经够了。真要看完整 URL 才需要做 TLS 中间人,那要额外分发 CA 证书,复杂度高一截,建议先用前者。

步骤三:进仓库前先做密钥扫描

顺序上,扫描要在“把仓库交给智能体”之前,而不是之后。因为智能体读到的每一个文件,都可能被它写进提示词、写进日志、写进补丁。

在仓库根目录跑一次全量扫描,把当前基线存下来:

```bash

全量扫描,输出 JSON 报告

gitleaks detect --source . --report-format json --report-path leaks-baseline.json

看看有多少条

jq 'length' leaks-baseline.json

```

如果历史提交里本来就有历史遗留的密钥(很常见),别急着全量清理,先建一个基线:把已确认的历史条目写进 .gitleaksignore,让后续扫描只对新增内容报警。

提交前的增量扫描接到 pre-commit 上(用 pre-commit 框架或直接写 git hook,具体命令以 gitleaks 官方 README 当前版本为准):

```yaml

.pre-commit-config.yaml

repos:

  • repo: https://github.com/gitleaks/gitleaks

rev: <以官方仓库当前 release 为准>

hooks:

  • id: gitleaks

```

再补一条:智能体的会话记录也要扫。它把 .env 读进去之后,内容可能出现在 transcript 里,而不是代码里。跑完任务后对日志目录再扫一次:

```bash

gitleaks detect --source ./logs --no-git --report-format json --report-path leaks-agent.json

```

步骤四:收敛权限,token 不进镜像

三件事。

第一,token 用文件挂载,不用环境变量明文。 环境变量容易被打进 docker inspect、CI 日志和崩溃转储:

```bash

mkdir -p /run/secrets && chmod 700 /run/secrets

printf '%s' "$XAI_API_KEY" > /run/secrets/xai_key

chmod 400 /run/secrets/xai_key

```

第二,Git 用 HTTPS remote,不用 SSH。 SSH 不走 HTTP_PROXY,会直接绕过你的审计代理;HTTPS 会尊重 https_proxy:

```bash

git remote set-url origin https://github.com/<org>/<repo>.git

```

第三,仓库侧收口。 开分支保护:主干禁止直接 push、必须走 PR、必须过 CI。给智能体的 token 只覆盖这一个仓库。这样即使它在容器里撒野,能造成的最大破坏也就是“开了一个需要人点合并的 PR”。

顺手加个 .agentignore,把不该进上下文的东西排掉:

```gitignore

.agentignore

.env

.env.*

secrets/

*.pem

*.key

node_modules/

dist/

```

步骤五:下发任务,用文件约束范围

别在聊天框里打字下任务——不可复现、不可审计。把任务写成仓库里的一个文件:

```markdown

<!-- tasks/TASK-123.md -->

任务:为 /api/orders 增加分页参数

允许修改

  • src/api/orders.ts
  • src/api/orders.test.ts
  • tests/fixtures/orders.json

禁止修改

  • .github/**
  • package.json / lock 文件
  • 任何以 .env 开头的文件

验收

  • 新增单测覆盖 page/pageSize 的边界值
  • npm test 全绿
  • 改动不超过 300 行

交付方式

新建分支 agent/task-123,开 PR,不要合并。

```

然后把容器拉起来跑:

```bash

docker run --rm -it \

--name "agent-run-$(date +%s)" \

--network agent-internal \

--user 1000:1000 \

-e HTTP_PROXY=http://agent-proxy:3128 \

-e HTTPS_PROXY=http://agent-proxy:3128 \

-e NO_PROXY=agent-proxy,localhost \

-v /run/secrets/xai_key:/run/secrets/xai_key:ro \

-v "$PWD/repo:/work/repo:rw" \

-v "$PWD/tasks:/work/tasks:ro" \

-v "$PWD/logs:/work/logs:rw" \

-w /work/repo \

agent-sandbox:local \

bash -lc "$AGENT_CMD --task /work/tasks/TASK-123.md"

```

$AGENT_CMD 换成官方文档给的实际启动命令,参数名以官方文档当前版本为准。注意 tasks/ 是只读挂载——任务描述本身就是约束,不该被智能体改写。

步骤六:看审计日志,回收结果

跑完之后,第一件事不是看 diff,是看出网日志。

```bash

#!/usr/bin/env bash

scripts/egress-report.sh

set -euo pipefail

LOG="${1:-./logs/access.log}"

echo "== 按域名聚合(次数 / 字节) =="

awk -F'\t' '{c[$4]++; b[$4]+=$6} END {for (k in c) printf "%6d %10d %s\n", c[k], b[k], k}' "$LOG" \

sort -rnhead -30

echo

echo "== 被拒绝的请求 =="

awk -F'\t' '$5 ~ /^403|^5/ {print $1, $3, $4, $5}' "$LOG" | head -20

```

判断标准很简单:

  • 域名列表里出现 api.x.ai、github.com 之外的任何东西,就要问一句“为什么”;
  • 出现大量 403,说明智能体在尝试访问白名单外的地址,这本身就是一个信号;
  • 单次任务的出网字节数突然比平时大一个数量级,去看它到底发了什么。

然后再看 diff。给 diff 设个软上限(比如 300 行),超了就不自动开 PR,转人工。

常见坑与排错

curl 报 Could not resolve host

内部网络没有 DNS,这是设计如此。让所有请求走代理:确认 HTTP_PROXY / HTTPS_PROXY 指向 http://agent-proxy:3128,并且 NO_PROXY 里包含代理自己的主机名,否则代理请求会被自己代理掉。

HTTPS 请求报证书错误

容器里缺 CA 证书。在 Dockerfile 里装 ca-certificates。如果后面上了 TLS 中间人代理,还得把自签 CA 装进容器的信任库。

git push 失败,提示 403

两种可能:token 权限给窄了(需要目标仓库的 Contents: Write + Pull requests: Write),或者分支保护拦住了直推主干。后者是好事,让它走 PR 就行。

出网日志是空的

三个常见原因:容器没走代理(检查 --network 是不是 agent-internal);squid 日志路径没挂载出来;access_log 那行的 logformat 名字和上面定义的对不上。

密钥扫描全是误报

先建 .gitleaksignore 基线,别急着把规则关掉。规则一关,真漏的也就扫不出来了。测试用的假密钥统一加 EXAMPLE 前缀,多数扫描器会自动跳过。

智能体把 .env 读进上下文了

--network 隔离只能防外传,防不了“读进来”。真正管用的是把敏感文件放在仓库外、用 .agentignore 排除,以及给容器用独立用户、只挂载必需的子目录而不是整个 home。

容器跑完文件属主变成 root

忘了 --user 1000:1000。宿主机上的仓库目录被 root 写过之后,下一次普通用户改不动,CI 也会挂。

大仓库上下文爆掉

把 node_modules、dist、大体积 fixtures 排进 .agentignore。任务文件里显式写“允许修改的文件”,比让它自己找要省得多。

一个任务跑太久

给容器加超时:docker run 外层套 timeout 1800,或者用 CI 的 job 超时。编码智能体在错误的路上会一直走下去,超时是最便宜的止损。

下一步建议

把日志接出去。 把 access.log 定期推到集中日志(Loki、ELK 都行),设一条告警:出现白名单外域名、或单任务出网字节数超过阈值就通知。日志留在本地磁盘上,出事时往往已经滚掉了。

把白名单做成仓库里的文件。 现在域名写在 squid.conf 里,改成从 egress-allowlist.txt 读取,任何新增域名都要走一次 PR 评审。这样“智能体为什么能访问这个域名”永远有一个可追溯的答案。

多仓库时按仓库发 token。 不要图省事用一个组织级 token 打通全部仓库。每个仓库一个 fine-grained token、一份白名单、一个日志前缀,出问题时能立刻定位到是哪条流水线。

定期轮换密钥,并且演练一次吊销。 智能体接触过的 key 都应该有明确的轮换周期。写个脚本,能在 5 分钟内把 token 吊销、把容器网络断开、把相关分支冻结——演练过一次,真出事时才不会手忙脚乱。

最后是心态上的建议。 编码智能体的价值在于“能连续做很多小事”,风险也在这里:单步都合理,串起来就跑偏了。所以别指望一条 prompt 约束住它,把约束放在环境里——网络、权限、扫描闸门——这些它绕不过去。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。