repo权限管理
需求速览
“任意目录级源码读取权限控制”
1 | 不给某人 gateway/ 权限 |
那最适合的方向不是 SOPS / git-crypt,也不是 Bitbucket,而是:
使用支持 path-based read/write ACL 的版本控制系统,例如 Perforce Helix Core 或 SVN,再接 TeamCity。
1. 需求文档草案
1.1 背景与问题定义
当前 ariel 项目存在按文件夹控制源码读取权限的需求。以 ariel 为例(假设有这些目录):
1 | ariel/ |
其中某些目录可能包含敏感逻辑、特殊配置、交易网关、内部策略或高价值实现,不适合对所有 repo 成员开放。例如:
1 | 某些开发者可以访问 strategy/ |
需求的关键不是简单的“谁能改”,而是:
没有权限的人,从 origin 拉代码时,本地就不应该出现对应 folder 的内容。
因此,这不是普通 GitLab 权限配置问题,而是 repository 内部 path-level read permission 问题。
1.2 从版本控制工具的“制度理念”看这个问题
这个需求很有意思,因为它不仅是一个工具选型问题,也反映了不同版本控制系统背后的治理哲学。
Git 的理念更接近自由分布式。
Git 鼓励每个开发者拥有完整仓库副本,在本地自由 commit、branch、merge,然后再与其他人同步。GitLab 文档也明确说明,Git 是分布式版本控制系统,项目成员 clone 后会获得完整 repository 的本地副本;Reporter、Developer、Maintainer、Owner 等角色都可以 clone repository。
这套理念非常适合开源协作、快速迭代、高信任团队和去中心化开发。它的优势是自由、灵活、离线能力强、分支成本低。但它的天然边界也很明显:
1 | Git 的权限边界主要是 repo |
也就是说,在一个 Git repo 内部,想做到:
1 | A 能拉 strategy/ |
这和 Git 的分布式模型是冲突的。因为一旦给了 repo 读取权限,用户通常就能获得完整仓库内容和历史。
SVN / Perforce 的理念更接近集中式治理。
SVN 和 Perforce 这类集中式版本控制系统,把中央服务器作为权威中心。开发者不是天然拥有完整仓库副本,而是从中央服务器 checkout / sync 自己有权限的路径。
这就像一种“中央统一管理 + 局部差异化授权”的制度设计。中央服务器知道完整代码版图,但每个开发者只能进入自己被授权的区域。SVN 官方文档明确支持 path-based authorization,可以按目录授予或拒绝 read / write 权限;某些目录甚至可以只允许少数人读取。
这正好对应当前需求:
1 | 代码整体仍然属于 ariel |
1.2.1 一个更形象的类比:代码治理中的“一国两制”
如果把 ariel 看成一个完整的软件共同体,那么普通目录和敏感目录其实不一定要被拆成独立 repo。
拆成独立 repo 的做法,确实可以解决权限边界问题,但也会带来新的麻烦:
1 | 代码和 config 要跨 repo 同步 |
这有点像一遇到特殊治理问题,就把一块区域切出去另立门户。技术上可行,但治理上会形成割裂。
更优雅的思路是:
保持一个统一的 ariel 代码版图,但对特殊目录实行特殊权限制度。
也就是技术治理里的“一国两制”:
1 | “一国”: |
这个类比能帮助我们理解:
软件工具不是中性的,它们背后都有自己的组织哲学。
Git 更像自由分布式协作:每个开发者拿到完整副本,强调个人本地自治。
SVN / Perforce 更像集中式治理:中央服务器掌握完整版图,根据路径发放访问权限。
而当前 ariel 的需求,本质上不是“自由复制完整仓库”,而是“统一项目下的分区授权”。
所以,这个问题不能简单地问:
1 | GitLab 有没有 folder permission? |
更应该问:
1 | 我们的项目治理模型,到底是自由分布式,还是集中式分区治理? |
当前答案更接近后者。
1.3 目标
目标 1:目录级读取权限
系统需要支持按目录配置读取权限:
1 | user A 可以读取: |
无权限用户从 origin checkout / pull / sync 时,本地不应出现无权限目录的源码内容。
目标 2:目录级写入权限
除读取权限外,也需要支持写入权限:
1 | user A 可以改 strategy/ |
也就是说,权限最好区分:
1 | no access |
目标 3:TeamCity 拥有全量读取权限
TeamCity 使用独立 service account,例如:
1 | svc_teamcity_ariel |
该账号可以读取完整 repo:
1 | ariel/config/ |
TeamCity 用全量源码 build binary。
目标 4:低权限开发者通过 CI/CD 获取 binary
没有完整源码权限的人,本地不能 build 完整 ariel binary。流程改为:
1 | 1. 开发者修改自己有权限的目录 |
目标 5:权限不能通过历史、分支、tag、网页、API 绕过
无权限用户不应通过以下方式拿到受限目录内容:
1 | checkout / pull / sync |
这个点很关键。不能只限制“当前目录”,还要避免历史版本泄露。
1.4 非目标
当前阶段可以先不支持:
1 | 低权限开发者本地直接用完整源码 build ariel |
也就是说,当前可以接受:
1 | 本地源码不完整 |
1.5 关键安全风险
如果 TeamCity 能看到完整源码,而低权限开发者能让 TeamCity 执行他提交的任意脚本,那么他可能通过 CI/CD 把受限目录内容打印到 log 或打包进 artifact。
例如低权限开发者没有 gateway/ 权限,但如果他能改 build script,他可能提交:
1 | cat gateway/secret_file.cpp |
然后 TeamCity build log 里就泄露了 gateway/。
即使他不能改 build script,如果 TeamCity 会运行他提交的测试代码、代码生成脚本、pre-build hook、post-build hook,也可能泄露源码。
所以 CI/CD 方案必须加限制:
1 | 低权限用户触发的 TeamCity build,不能执行任意用户提交的脚本 |
3. 方案详细评估
方案 A:GitLab 单 repo + CODEOWNERS
GitLab CODEOWNERS 可以指定某些文件或目录的 owner,并要求相关 owner approve 后才能合并。这个方案适合控制:
1 | 谁能改 gateway/ |
但它不能控制:
1 | 谁能读取 gateway/ |
所以 CODEOWNERS 只能解决“写入审批”,不能解决“读取隔离”。
结论:不满足当前需求。
方案 B:Git sparse-checkout / partial clone
Git sparse-checkout 可以让本地 working tree 只显示部分目录。它适合大仓库性能优化,或者让开发者本地工作区更清爽。
但它不是权限系统。
用户如果本身拥有 Git repo 读取权限,sparse-checkout 不能被当作安全边界。它不能保证用户无法主动拉取其他目录内容。
结论:不满足当前需求。
方案 C:SOPS / git-crypt 加密敏感内容
SOPS / git-crypt 适合保护配置文件、secret、token 等内容。
效果是:
1 | 无权限用户可以拉到文件 |
这适合 config/ 里有密码、token、证书的场景。
但当前的需求是:
1 | 无权限 folder 本地就不应该拉下来 |
而不是:
1 | 拉下来但看不懂 |
并且如果 gateway/ 这类目录是源码逻辑,不可能把整个源码目录都当成配置文件长期加密维护。
结论:适合保护 secret/config,不适合任意 folder 权限管理。
方案 D:拆 repo / Git submodule
可以把敏感目录拆成独立 repo:
1 | ariel-main/ |
这个方案符合 Git 的权限模型,因为 Git 的权限边界是 repo。
用户没有 ariel-gateway.git 权限,就无法拉取 gateway 内容。
但缺点也明显:
1 | 相关代码要跨 repo 一起更新 |
这就是说“相关的要一起更新的时候会麻烦”。
从治理类比上看,这更像把特殊区域拆出去单独建制。能解决权限,但破坏了 ariel 作为统一工程体的完整性。
结论:如果必须继续使用 Git,这是现实方案;但不符合当前“不希望拆 repo”的方向。
方案 E:SVN path-based authorization + TeamCity
SVN 的理念天然符合这个需求。
SVN 是集中式模型,中央服务器可以按路径授权。官方文档明确说明 SVN 可以定义细粒度访问规则,例如一组用户可以写某个目录,另一个目录只有少数人可读。
可以把 ariel 放在一个 SVN repository 里:
1 | /ariel/trunk/ |
然后配置权限:
1 | strategy_devs: |
TeamCity 已经支持 Subversion VCS root,并且 TeamCity bundled SVNKit,不需要额外在 TeamCity server 或 agent 上安装 SVN client。
这套方案非常符合“一国两制”的技术类比:
1 | ariel 仍然是一个统一项目 |
结论:非常适合当前需求,尤其适合作为第一阶段落地方案或 POC。
方案 F:Perforce Helix Core + TeamCity
Perforce 也是集中式版本控制系统,并且在大型 monorepo、游戏开发、芯片、金融工程等需要复杂权限和大规模源码管理的场景中比较常见。
Perforce 支持通过 protections table 按路径配置访问权限;官方文档中也说明可以按 Users、Groups、Depot Tree 查看用户或组对文件/文件夹的权限,没有授权的路径会显示 no access。
TeamCity 也支持 Perforce VCS root。
Perforce 的治理模型也非常符合当前需求:
1 | 统一 depot |
相比 SVN,Perforce 更适合长期维护复杂权限、大型代码库、大文件和多团队工程。
缺点是:
1 | 采购成本更高 |
结论:适合长期正式方案;如果当前只是快速验证,SVN 更轻。
方案 G:Bitbucket
Bitbucket 本质上仍是 Git-based code hosting。Bitbucket Cloud 官方文档中的 branch permissions 主要用于控制谁可以 write / merge 到某些 branch 或 branch pattern。
这和当前需求不同。
当前需求是:
1 | 控制 repo 内某个 folder 是否可读 |
而 Bitbucket 的主能力是:
1 | 控制 branch workflow |
所以 Bitbucket 不应该作为这个需求的核心解决方案。
结论:不建议为了 folder-level read permission 采购 Bitbucket。
4. 方案对比表
| 方案 | 无权限 folder 是否拉不下来 | 是否支持任意目录读权限 | 是否保留统一工程体 | 是否保留单源码树 | TeamCity 全量 build | 迁移成本 | 结论 |
|---|---|---|---|---|---|---|---|
| GitLab CODEOWNERS | 否 | 否 | 是 | 是 | 是 | 低 | 只能控写,不能控读 |
| Git sparse-checkout | 否 | 否 | 是 | 是 | 是 | 低 | 性能工具,不是权限工具 |
| SOPS / git-crypt | 否,能拉到密文 | 部分 | 是 | 是 | 是 | 中 | 适合 secret/config,不适合源码目录 |
| Git submodule 拆 repo | 是 | 是,按 repo 边界 | 否 | 否 | 是 | 中 | 保留 Git 的现实方案,但 repo 会变多,工程割裂 |
| SVN path ACL | 是 | 是 | 是 | 是 | 是 | 中 | 适合低成本验证,当前最适合落地 |
| Perforce Helix Core | 是 | 是 | 是 | 是 | 是 | 高 | 最适合长期正式方案,但成本更高 |
| Azure TFVC / Plastic | 是 | 是 | 是 | 可集成 | 中到高 | 可行,看公司生态 | |
| Bitbucket | 否 | 否 | 是 | 是 | 是 | 中 | 不推荐 |
推荐结论
1 | 如果要低成本快速验证: |
5. SVN path-based auth + TeamCity build 目标架构
1 | SVN Repository: ariel |
权限组:
1 | ariel_common_readers |
权限示例:
1 | ariel_strategy_devs: |
开发者流程:
1 | 1. 只 checkout 自己有权限的目录 |
6. TeamCity 安全要求
6.1 低权限用户不能控制 TeamCity 执行的任意脚本
不要让低权限用户通过修改 repo 里的脚本来控制 TeamCity 做什么。
危险例子:
1 | cat gateway/secret.cpp |
如果 TeamCity workspace 有完整源码,这类脚本会泄露受限目录。
6.2 Build 配置应由 TeamCity admin 固定
建议:
1 | TeamCity build steps 固定在 TeamCity 配置里 |
如果 build 必须使用 repo 里的脚本,例如:
1 | build/build_ariel.sh |
那这个目录也要被视为敏感控制面:
1 | build/ |
这些目录的写权限必须只给 trusted build owner。
6.3 区分两类 build
建议至少分两类:
1 | 普通验证 build: |
6.4 Artifact 也要做权限控制
TeamCity 产出的 binary 可能包含敏感信息,例如:
1 | debug symbols |
所以发布 artifact 时要检查:
1 | 不要发布源码包 |
7. 阶段性落地建议
Phase 1:SVN POC
先做一个最小验证:
1 | /ariel/trunk/common/ |
设置三个用户:
1 | alice: |
验收标准:
1 | alice checkout 后看不到 gateway/ |
Phase 2:接入真实 TeamCity workflow
验证:
1 | 提交代码后触发 TeamCity |
Phase 3:长期评估 Perforce
如果后续出现这些情况:
1 | 目录权限规则越来越多 |
再评估 Perforce Helix Core 作为长期治理平台。
8. 总结
现在这个需求已经不是单纯的 config 加密,而是任意 folder 的 read permission。
如果要求是“不给某人 gateway 权限,他从 origin 拉代码时本地就完全没有 gateway 内容”,那么 GitLab / Bitbucket / Git sparse-checkout / CODEOWNERS 都不能严格满足。Git 的权限边界是 repo,不是 repo 内 folder。CODEOWNERS 只能控制谁 approve / merge,不能控制谁读取。
SOPS / git-crypt 适合保护敏感 config 或 secret,但它的效果是“拉下来的是密文”,不是“folder 拉不下来”,所以也不满足当前需求。
真正匹配这个需求的是支持 path-based read/write ACL 的集中式版本控制系统,比如 SVN 或 Perforce。TeamCity 用 service account 读取完整源码并 build binary,低权限开发者只 sync 自己有权限的目录,需要完整 binary 时通过 TeamCity workflow 生成并从 artifact storage / 网盘获取。
建议长期方案评估 Perforce Helix Core,因为它更适合长期维护多个目录、多组权限、monorepo 和 binary build 场景;如果我们想先低成本验证,可以先用 SVN path-based authz + TeamCity 做一个小 POC。
另外要注意 TeamCity 安全:如果 TeamCity 能看到完整源码,不能让低权限开发者通过修改 build script 或 CI 配置把受限目录内容打到 log/artifact 里。TeamCity build 配置、build script、artifact 发布都要受控。
结论
这次需求本质上不是“GitLab 权限没配好”,而是版本控制系统治理理念的选择。
Git 的哲学是自由分布式:每个人拿完整副本,本地自治,最后同步。
SVN / Perforce 的哲学是集中式治理:中央服务器掌握完整版图,按路径授权访问。
而 ariel 当前需要的是:
1 | 统一项目 |
所以最贴切的方案不是拆 repo,而是:
在统一 ariel 代码版图内,对 gateway/config 等特殊目录实行“特别行政区式”的 path-level 权限治理。
当前建议先用:
1 | SVN path-based authorization + TeamCity |
作为落地方案;后续如果规模扩大,再评估:
1 | Perforce Helix Core + TeamCity |
这样既能避免 Git 单 repo 无法做目录级读取权限的问题,也能避免拆 repo 带来的工程割裂。