如何限制特定用户只能访问 Git 仓库的指定分支

文章导读
Git 原生协议本身不包含复杂的用户身份验证逻辑,一旦用户拥有仓库写入权限,默认即可操作所有分支。要实现“特定用户只能访问指定分支”,必须依赖托管平台的权限功能,或在自建服务器端通过 Hook 脚本结合 SSH 身份识别进行拦截。
📋 目录
  1. 方案一:使用托管平台(推荐)
  2. 方案二:自建服务器 Hook 控制
  3. 关键难点:如何在 Hook 中获取用户身份
  4. 更优替代:使用 Gitolite 管理权限
  5. 怎么验证是否生效
  6. 常见坑
A A

Git 原生协议本身不包含复杂的用户身份验证逻辑,一旦用户拥有仓库写入权限,默认即可操作所有分支。要实现“特定用户只能访问指定分支”,必须依赖托管平台的权限功能,或在自建服务器端通过 Hook 脚本结合 SSH 身份识别进行拦截。

先说结论:原生 Git 协议无法直接识别用户身份进行分支隔离,必须借助服务器端的权限管理或钩子脚本实现。

  • 适合:自建 Git 服务器或需要精细化权限管控的团队
  • 先准备:确认仓库托管方式,区分是平台托管还是自建 bare 仓库;确认 SSH 用户体系(是独立系统用户还是统一 git 用户)
  • 验收:使用受限账号尝试推送,确认被拒绝且服务端日志有明确拦截记录

方案一:使用托管平台(推荐)

如果使用 GitHub、GitLab 等平台,无需写脚本,直接在设置中配置分支保护规则:

  1. 进入项目设置,找到 Branch Protection Rules 或 Protected Branches。
  2. 选择需要保护的分支(如 master 或 main)。
  3. 勾选限制选项,例如限制谁可以推送(Only allow specific users/teams)。
  4. 保存设置,确保普通成员不在允许列表中。

方案二:自建服务器 Hook 控制

如果是自建 Git 服务器,可以在仓库目录的 hooks 文件夹中部署 pre-receive 脚本。以下是一个基础的拦截示例,限制只有 admin 用户可以推送 master 分支:

#!/bin/sh
# 获取当前 SSH 登录用户,注意:这依赖于 SSH 配置
pusher="$USER"

while read oldrev newrev refname
do
    # 修正命令参数格式,去除错误反引号
    branch=$(git rev-parse `--symbolic` `--abbrev-ref` $refname)
    
    # 逻辑修正:同时判断分支名和推送用户
    if [ "$branch" = "master" ] && [ "$pusher" != "admin" ]; then
        echo "ERROR: User $pusher is not allowed to push to $branch"
        exit 1
    fi
done
exit 0

脚本保存后,记得赋予执行权限并确保所有者正确:

chmod +x hooks/pre-receive
chown git:git hooks/pre-receive

关键难点:如何在 Hook 中获取用户身份

质检指出原生脚本无法区分用户,这是因为 Git Hook 运行环境默认只继承 SSH 会话的环境变量。实现用户级控制有以下两种常见场景:

如何限制特定用户只能访问 Git 仓库的指定分支

场景 A:每个开发者拥有独立的系统账号

如果团队成员通过各自的系统账号(如 alice, bob)SSH 登录服务器,脚本中的 $USER 变量可以直接获取当前登录用户名,上述脚本可直接生效。

场景 B:所有人共用 git 账号(常见)

如果所有人均通过 git@server 登录,$USER 恒为 git,此时需在 SSH 服务端配置 authorized_keys 强制命令或环境变量来传递身份。例如在 ~git/.ssh/authorized_keys 中:

environment="GIT_PUSH_USER=alice" ssh-rsa AAAA... alice@client
environment="GIT_PUSH_USER=bob" ssh-rsa BBBB... bob@client

然后在 Hook 脚本中读取 $GIT_PUSH_USER 而非 $USER。注意需在 /etc/ssh/sshd_config 中开启 PermitUserEnvironment yes

如何限制特定用户只能访问 Git 仓库的指定分支

更优替代:使用 Gitolite 管理权限

手动维护 Hook 脚本和 SSH 配置容易出错且难以扩展。对于需要精细化权限控制的自建仓库,推荐使用 Gitolite 等专用权限管理工具。

Gitolite 配置文件示例(gitolite.conf):

repo my_project
    RW+     =   admin
    RW      =   dev_team
    R       =   @all

部署 Gitolite 后,它会自动处理 SSH 密钥与用户映射,并在服务端拦截不符合规则的推送,无需手动编写 Hook 脚本。

怎么验证是否生效

配置完成后,不要只用管理员账号测试。找一个受限的测试账号,在本地尝试向受保护分支推送代码:

git push origin master

如果配置生效,终端会返回拒绝信息(如 remote: ERROR: User...),且推送失败。同时检查服务器端仓库日志或 /var/log/secure,确认钩子脚本被触发且没有权限错误。

常见坑

  • 钩子无执行权限:脚本文件如果没有 x 权限,Git 服务器会 silently fail 或报错,导致限制失效。
  • 仓库类型错误:Hook 必须部署在服务端的 bare 仓库中,客户端的 .git/hooks 无效。
  • 用户身份获取失败:若共用 git 账号且未配置 SSH 环境变量,脚本无法区分用户,导致所有人被误拦或所有人放行。
  • 本地缓存泄露:权限控制仅限制推送和服务器访问,如果用户之前克隆过仓库,本地仍有历史数据,敏感代码需考虑仓库拆分。
  • 分支名称匹配:脚本中分支名称匹配要精确,避免误伤同名远程分支(如 refs/heads/master 与 refs/tags/master)。