【免费下载链接】git-ai
Git designed for AI agents.
当 AI Agent 开始自动提交代码,如何保证它不会写坏主分支、更不会把仓库拖下水?git-ai 项目(Graft,一款"为 AI Agent 设计的 Git")给出的答案是完整的最小权限安全模型:每个 Agent 持有独立的 Token 身份,所有 gRPC 请求受RPC 白名单约束,每一次分支更新再经过分支策略裁决。三层防线层层收紧,让 Agent"能干完活,但动不了不该动的东西"。
这篇文章带你从零看懂这套机制:为什么 Agent 需要最小权限、Token 身份如何防冒充、白名单里有什么和绝不能有什么、分支策略规则怎么写,以及上线前该记住的 6 步清单。
为什么 AI Agent 必须用最小权限 🤔
Agent 是"自动化写作者"——它的价值就在于无需人工干预就能落地变更,这同时意味着风险:
- 如果 Agent 能创建/删除仓库,它可以安装服务端钩子,甚至把仓库内容推送到任意外部地址(数据外泄);
- 如果 Agent 能随意更新引用,它可以绕过保护分支直接改写
main; - 如果请求体里的用户名可以伪装身份,Agent 可以"冒充"别的 Agent 给自己批审核。
git-ai 的安全模型针对这三类风险设计了三道闸门:
| 防线 | 解决的问题 | 关键配置 |
|---|---|---|
| ① Token 身份 | 你是谁?能否冒充别人? | [[graft.agent]] |
| ② RPC 白名单 | 你能调用哪些接口? | agent_rpcs |
| ③ 分支策略 | 你能改哪些分支? | [[graft.policy]] |
完整定义见官方文档 AGENT-SECURITY.md。
Token 身份:每个 Agent 一张独立"工牌" 🔑
给每个 Agent 发独立 Token
每个 Agent 在配置文件中占一个[[graft.agent]]条目,用token_file指向自己的密钥文件(或直接用token字段):
[[graft.agent]] name = "planner" token_file = "/etc/graft/tokens/planner" [[graft.agent]] name = "builder" token_file = "/etc/graft/tokens/builder"启动时服务器会严格校验配置:名字为空、重名、两个 Agent 共用一个 Token、Agent Token 与共享auth.token相同,都会直接拒绝启动。注释完整的示例配置在 examples/graft/config.toml。
认证细节:常量时间比较 + HMAC 时效窗口
认证逻辑在 auth/token.go 与 internal/gitaly/server/auth/agent.go 中实现,有两处值得新手注意:
- 常量时间比较:Token 比对使用
crypto/subtle,防止攻击者通过响应耗时差异猜测密钥; - v2 HMAC 格式:除明文 bearer 外,还接受用 Agent Token 作为密钥计算的 HMAC-SHA256 签名 Token,带时间戳且只有约 30 秒的有效期窗口——重放旧 Token 无效。
身份只来自 Token,不来自请求体
这是最关键的设计:身份取自 Token,绝不取自请求。
Agent 请求里携带的用户 ID 字段(gl_id、gl_username)会被服务器统一覆写为 Token 对应的 Agent 名。也就是说,builder拿着自己的 Token、却在请求里自称planner去更新main,依然会被当成builder来裁决——直接拒绝。Agent 身份通过 internal/graft/identity/identity.go 中的Identity结构在请求全链路传递,changelog、策略裁决、审核记录用的都是这个身份。
⚠️ 共享
auth.token是运维凭证,不是 Agent。它保留给备份工具、校验器等管理端组件,绝不能发给 Agent 使用。
RPC 白名单:Agent 能调什么,绝不能调什么 🛡️
通过 Token 认证的 Agent,下一步还要过 RPC 白名单。白名单定义在 internal/gitaly/server/auth/agent_methods.go,分三层理解:
内置允许:恰好够 Agent 干活的接口
内置白名单正是 SDK、graftCLI 和缓存层实际用到的最小集合:
- 读类接口:对象/树读取(
ObjectService)、变更集(ChangesetService,只读)、派生数据、代码搜索(SearchService); - 只读访问器:commit/ref/blob/diff 等服务的
Find*、List*、Get*方法; - 审核:
ReviewService全部接口——它的写操作只写审核存储、绝不碰引用,且记录的是 Token 身份,Agent 无法"替别的 Agent 批审核"; - 落地与提交:
UserLand、UserCommitFiles、UserCreateBranch、UserDeleteBranch,且全部要过分支策略; - 杂项:
ServerInfo、RepositoryExists与健康检查。
永久禁止:连配置也开不了的"红线"
有一批接口对 Agent永远关闭,即使你写进agent_rpcs也不行,启动时会被直接报错拦下:
| 禁止的接口 | 为什么危险 |
|---|---|
CreateRepository、CreateRepositoryFromURL/Snapshot等 | 可从任意 URL 拉数据建库,快照甚至可携带钩子文件,构成服务端请求伪造与钩子植入 |
BackupRepository、RemoveRepository、ReplicateRepository | 可把仓库数据写到调用者指定的任意位置(外泄) |
UpdateRemoteMirror、FindRemoteRepository | 向调用者自选的 URL 推数据/发请求(外泄与 SSRF) |
UpdateReferences、DeleteRefs、WriteRef | 绕开策略直接写引用 |
HookService、CleanupService、PartitionService等 | 伪造钩子回调、破坏存储结构等管理操作 |
代码中对每条红线都写了注释说明攻击场景,可以对照 agent_methods.go 阅读。
默认拒绝 + 可控扩展
白名单之外的接口默认全部拒绝,错误码PermissionDenied,且信息不泄露更多细节。如果确实需要额外接口(比如查仓库大小),可以用全限定 gRPC 方法名扩展:
[graft] agent_rpcs = ["/gitaly.RepositoryService/RepositorySize"]服务器启动时逐条校验:方法未注册、或属于红线清单,都会拒绝启动而不是静默忽略。值得一提的是,SSHService也不在内置白名单里——Agent 默认无法通过 Git 协议 fetch/push,这堵住了绕过UserLand冲突处理直接推分支的口子。
两类"夹带私货"的请求也会被拒
白名单只管"调哪个方法",还有两个请求级检查专门防绕过:
git_config_options一律拒收:Git 会应用客户端传入的配置,Agent 可以借此覆写core.hooksPath跳过访问检查,甚至用uploadpack.packObjectsHook执行任意命令;gitaly-servers元数据一律拒收:它指定服务器地址与 Token,Agent 可借它让服务器连接任意内网地址。
分支策略:用几行 TOML 决定谁能动哪个分支 🌿
前两层管"你是谁、能调什么",最后一层管"能改什么"。策略规则写在[[graft.policy]](定义见 CONFIGURATION.md),每个规则只有三个字段:
actor:执行者 glob,匹配 Token 对应的 Agent 名;refs:引用 glob 列表,refs/heads/**可匹配任意深度;allow:是否放行。
规则按顺序求值:第一条"actor 匹配且 refs 全部匹配"的规则说了算;没有任何规则命中的引用 = 拒绝;一次更新触及多个规则管辖的引用时,必须全部放行才算数。保护main的推荐写法(与 examples/graft/config.toml 一致):
# 1. planner 可以更新 main [[graft.policy]] actor = "planner" refs = ["refs/heads/main"] allow = true # 2. 其他人不能碰 main [[graft.policy]] refs = ["refs/heads/main"] allow = false # 3. 其余分支对所有认证者开放 [[graft.policy]] refs = ["refs/heads/**"] allow = true两个新手容易忽略的细节:
HEAD也在管辖范围内:策略覆盖HEAD符号引用,Agent 无法通过"偷偷把HEAD指到别处"绕过保护分支;- 策略对 Agent 的每一次引用更新生效,不止
UserLand:pre-receive 钩子检查推送与 OperationService 调用,reference-transaction 钩子还会在引用加锁、变更之前再查一遍,所以没有 pre-receive 的 RPC 也逃不掉。被拒的更新整体中止、不写入 changelog,服务器日志会记下是哪条规则拒绝的。边界情况汇总见 LIMITATIONS.md。
审计追溯:谁动过仓库,一条日志说清楚 🔍
最小权限的最后一块拼图是"事后可查"。Agent 的每次变更都会写入变更集日志,记录的是Token 对应的身份而非请求自称的用户,用graft log即可逐条查看操作者。认证失败也会被 Prometheus 指标(gitaly_authentication_errors_total)分类计数——"HMAC 签名错误""Token 过期"各有独立标签,异常调用一查便知。
请求在服务器内部的完整路径同样可观测:下图是一次 gRPC 请求经过各服务与拦截器的调用链路,认证与安全检查正是发生在这一层。
如果搭建了 ELK 日志栈,还可以按字段过滤 Agent 请求、认证错误与钩子回调记录,把安全事件从日志里"捞"出来复核。
新手上线清单:6 步给 Agent 配好最小权限 📋
源自官方 AGENT-SECURITY.md 的运维检查清单,照着做即可:
- 一 Agent 一 Token:每个 Agent 独立
token_file,文件权限只对 Agent 自身账号可读; - 共享
auth.token只留给自己:交给运维与工具,绝不发给 Agent; - 保护关键分支:用
[[graft.policy]]锁住默认分支与 release 分支,其他分支放开; - 自动解冲突按需开启:
[graft.land.resolver]默认关闭,只对你信任的 actor 和路径启用; - 隔离特权组件:
graft-resolve与校验器尽量放在独立账号上(它们用运维凭证而非 Agent 凭证写入); - 定期审计:用
graft log检查变更集日志,每条记录都带着操作者身份。
小结
git-ai 的安全模型可以浓缩成一句话:身份来自 Token,接口受白名单,分支看策略。三层都是"默认拒绝、显式放行",配置错误宁可拒绝启动也不静默降级,红线接口连配置都无法打开。对新手来说,只要记住 Token 三不(不共用、不发给 Agent 共享 Token、不在请求体里自称身份)、白名单两禁(git_config_options、gitaly-servers)、策略一原则(没规则命中就是拒绝),就能为自动化流水线配出一套既好用又安全的 AI Agent 环境。
【免费下载链接】git-ai
Git designed for AI agents.
相关推荐
F3D 3D查看器指南:一条命令打开 60 多种 3D 格式,还能批量导出截图
F3D 3D查看器指南:一条命令打开 60 多种 3D 格式,还能批量导出截图 同事发来一个 glb 模型、供应商给了一份 stp、实验室里还有一个 ply 和
桌面应用图形学3D渲染数据可视化launch-your-agent的三层安全护栏:always_ask权限策略、网络白名单与预算上限
launch your agent的三层安全护栏:always_ask权限策略、网络白名单与预算上限 launch your agent 是一个 Claude
三步恢复任务栏:用 ExplorerPatcher 找回 Windows 11 经典界面
三步恢复任务栏:用 ExplorerPatcher 找回 Windows 11 经典界面 Windows 11 的居中任务栏和磁贴式开始菜单让你回不去 Win1
桌面应用逆向工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考