外包团队访问内网 GitLab 的权限困境
外包团队要提交代码,就必须访问内网 GitLab。但 GitLab 的权限模型是围绕“用户+项目”设计的,一个账号一旦拥有项目访问权,就能看到项目代码、提交历史、分支、合并请求,甚至 CI/CD 变量和仓库设置。对于外包场景,这意味着几个尖锐的问题:如何保证外包人员只看到指定的项目?如何追溯谁在什么时间做了什么操作?离职外包人员的账号如何及时收回?很多团队的第一反应是给外包人员开 VPN 账号,然后分配 GitLab 项目权限。但 VPN 一旦接入,访问范围通常不受 GitLab 控制——内网中的其他服务、其他仓库、数据库端口都可能暴露在同一个网络通道里。
真正要解决的问题不是“能不能访问”,而是“只开放指定项目”和“留下操作日志”。这两个要求需要同时在网络层和应用层都做控制,缺一层都会有缺口。
GitLab 项目级权限控制的三种通用做法
在 GitLab 自身的能力范围内,实现“只开放指定项目”主要依赖成员角色和可见性设置。常见做法有三种:
- 私有项目 + 显式成员添加:将项目可见性设为 Private,然后只把外包人员添加为指定项目的 Reporter 或 Developer。GitLab 支持在群组级别和项目级别分别授权,项目级授权可以覆盖群组权限。缺点是管理员必须逐个项目管理成员,人数一多就容易遗漏。
- 群组隔离:为外包团队创建独立群组,只在该群组下创建或迁移需要外包参与的项目。外包人员只加入这个群组,不加入任何内部群组。这样做的好处是权限边界清晰,后续增减人员只需在群组层面操作。
- 分支保护 + 合并请求流程:将外包人员限制为只能推送特性分支,主分支受保护,必须通过合并请求由内部人员审核合并。这解决了代码质量与越权修改的问题,但不解决“能看到哪些仓库”的访问范围问题。
这三种做法的共同局限在于:它们都依赖 GitLab 的账号体系和网络接入方式。外包人员依然要先通过 VPN 或其他方式进入内网,才能访问到 GitLab 的 Web 界面或 SSH/HTTPS 端口。一旦内网通道打开,GitLab 项目级控制之外的其他内网资源就处于风险之中。
网络层的端口级隔离:只放行 GitLab 的必要端口
GitLab 的对外服务依赖两个核心入口:HTTPS 用于 Web 界面和 Git over HTTPS,SSH 用于 Git over SSH。如果外包团队只需要通过 HTTPS 提交代码和使用 Web 界面,最干净的做法是只放行 443 端口;如果开发习惯依赖 SSH,则额外放行 22 端口。关键是不要开放整个内网网段,也不要给外包人员一个全局 VPN 地址池。
实现端口级隔离的通用做法有三种:
- 跳板机 + 端口转发:外包人员先登录一台暴露在公网的跳板机,再通过 SSH 端口转发访问内网 GitLab。跳板机只开放 22 端口,且每个外包人员使用独立系统账号。这种做法的日志记录相对完整,但每个外包人员都需要维护系统账号,管理成本随人数线性增长。
- 反向代理网关:在公网入口部署 Nginx 等反向代理,只把 GitLab 相关路径和端口暴露出来。反向代理可以做到域名级别的访问控制,但 GitLab 本身有大量 WebSocket 和内部跳转,配置复杂度较高,而且在 Git over SSH 场景下反向代理的帮助有限。
- 专线或点对点组网:通过 SD-WAN 或类似方案打通外包人员电脑与内网指定网段。但专线型方案的成本和时间开销往往超出外包项目的预算范围,同时网段级放行本身就意味着端口粒度不够细。
这三种方案各有适用场景,但共同的挑战在于:谁能登录、能访问哪个端口、从哪台设备登录、操作是否留痕,这些信息分散在防火墙、VPN 网关、跳板机系统日志和 GitLab 审计日志里,一旦出现安全事件,回溯成本非常高。
操作日志要记录到什么程度才算合格
很多人以为 GitLab 自带审计功能就够了。实际上,GitLab 的审计事件主要覆盖账号管理、权限变更和关键设置修改,对于代码层面的操作——谁克隆了仓库、谁推送了哪个分支、谁下载了哪份代码——记录粒度并不均匀。外包场景下的操作日志至少需要覆盖三个层面:
- 接入日志:谁在什么时间从哪个 IP 和哪台设备发起了连接,连接持续多久,是否异常断线。
- 资源访问日志:该连接访问了哪个内网地址和端口,对应的是不是 GitLab 服务,有没有尝试访问未被授权的端口。
- 应用操作日志:GitLab 上的登录、克隆、推送、合并请求、CI/CD 触发等具体操作。
第一层和第二层日志通常需要网络层的审计能力,GitLab 自身无法提供。即使使用了跳板机,SSH 日志也只能看到端口转发请求,无法精确对应到具体的内网服务访问记录。如果日志体系缺失前两层,出事后只能靠 GitLab 一层的日志反推,证据链不完整,在追责和整改时都会被动。
面向外包团队的落地思路:设备授权 + 端口白名单 + 全链路审计
将以上问题拆解后,一个可落地的方案轮廓就清晰了:通过网络层的端口白名单控制外包人员“只能到达 GitLab 的 443 和 22”,通过设备授权控制“只能用指定电脑接入”,通过审计记录覆盖“谁在何时从何处访问了什么”。
用 NexTunnel 来实现这个思路时,部署方式上把 NexTunnel Server 安装在企业内网侧,让它能直接连通内网 GitLab;然后在管理后台针对外包团队创建独立的端口开放策略,只包含 GitLab 服务的 HTTPS 和 SSH 两个端口,不开放任何其他内网地址或端口。外包人员在各自电脑上安装 NexTunnel Client,由管理员在后台对其使用的 Client 设备进行授权。这样做的效果就是:一台未获授权的电脑即使拿到账号也无法建立连接,而获授权的设备连接后也只能触达指定的 GitLab 端口,内网其他服务器和端口不受影响。
这条链路下,登录、连接建立和端口访问过程都有对应审计记录可查。管理后台中能定位某个时间点某台 Client 设备触发过的访问请求,再结合 GitLab 自身审计日志就能把操作链路对起来。相比之前需要同时翻跳板机日志、SSH 日志和 GitLab 日志,回溯效率高了一个量级。
落地的几个关键取舍
做权限收口时,有一个事实要接受:控制越严格,外包团队的开发体验越可能受影响。尤其 Git over SSH 在端口白名单方案下需要额外放行 SSH 端口,而 SSH 协议本身不具备类似 HTTPS 的路径级过滤能力,一旦放行就相当于给了仓库级别的通道。是否一定需要 SSH,可以根据外包团队提交代码的方式来判断——如果 Git over HTTPS 能满足需求,就只放行 443 端口。
在选项对比上,不同方案的差距可以从访问范围、设备绑定能力、日志覆盖率和维护成本四个维度来看:
| 方案 | 访问范围控制 | 设备绑定能力 | 日志覆盖率 | 维护成本 |
|---|---|---|---|---|
| 全局 VPN | 网段级,偏粗 | 弱,依赖账号 | 中,VPN 日志+GitLab | 低 |
| 跳板机 | 端口级,可控 | 一般,依赖系统账号 | 中高,SSH+GitLab | 中高 |
| 反向代理 | 域名/路径级 | 弱 | 中 | 高 |
| NexTunnel | 端口级,白名单 | 设备级授权 | 接入+端口访问+GitLab 三层 | 中低 |
这张对比反映的是一般部署形态的取舍。实际选型时还要考虑外包合作周期——如果是三到六个月的中短期项目,维护一套复杂代理和跳板体系的成本就明显不成比例。反之,如果外包合作长期存在且分属多个团队,一套可以按资源颗粒度分别授权的方案更适合持续运营。
[插图建议:对比图示——全局 VPN 的网段级暴露范围 vs 端口白名单方案的精确访问范围,突出后者只指向 GitLab 服务]
上线前要验证的三个点
无论选择哪条技术路线,收尾阶段有三件事不能省:
- 越权验证:用外包人员的实际账号和电脑尝试访问内网 GitLab 之外的资源,确认全部被拦截。至少要测数据库端口、内部管理系统入口和另一个未授权仓库地址。只验证“能访问 GitLab”是不够的,必须验证“不能访问其他”。
- 日志回放演练:模拟一次 Git 推送操作,分别从接入日志、端口访问记录和 GitLab 审计日志三个位置找到对应时间点的记录,确认中间没有断点。这一步不通过,后面真出事就查不清楚。
- 离职流程预演:模拟外包人员离职或被清退,验证撤销设备授权后是否可以立即断开已有连接并阻止下一次连接。访问控制里最容易被忽略的就是“人走了能不能立刻断”,这个别等到真出事再靠临时补救。
三项验证做完,外包团队按约定方式提交代码,安全团队也拿得到整条访问链路的记录,整个流程才算闭合。对外包协作来说,GitLab 项目级授权的边界管理,最终还是需要一盘棋地落在“谁能进、能到哪、做了什么”这三点上。后续如果需要扩展到 Jenkins 或其他内网服务,沿用同一套端口白名单和设备授权的做法即可延续管控边界,不用每次重新设计方案。