Git HTTPS 与 GCM 凭证治理
开发者第一次通过 HTTPS 克隆私有仓库时,常把弹出的浏览器登录页、PAT、Git Credential Manager(下文简称 GCM)和操作系统凭证库都叫作“Git 密码”。这会让排障迅速失去方向。它们其实位于不同层:Git 根据远端 URL 发起 HTTP(S) 请求;Git 凭证子系统向 helper 询问账号和秘密;GCM 负责与支持的平台完成 OAuth、PAT 等认证;最后由 Windows 凭据管理器、macOS 钥匙串或 Linux 上显式选择的凭证库保存结果。
理解这条链路后,Authentication failed 就不再只有“重新登录”一种解法。你可以判断问题发生在远端 URL、helper 选择、平台授权、代理、系统密钥库还是仓库权限,并且只清理真正失效的那一层。
最容易误判的现场是浏览器已经显示授权成功,git fetch 却继续返回 401;或者读取仓库正常,git push 返回 403。前者优先检查请求使用了哪组凭据,后者优先检查这组身份是否拥有写权限以及分支规则是否允许该动作。提交签名和 DCO Signed-off-by 都不会改变 HTTPS 请求身份:它们描述提交对象,GCM 处理的是网络认证。
先选一个没有生产数据的团队测试仓库。账号至少要有读取权限;写入实验还需要允许创建和删除临时分支。企业代理、VPN、MFA、SSO 与系统凭证库都会参与结果,因此操作必须在开发者日常账号和真实网络路径中进行,不能用共享系统账号或管理员会话代替。进入测试仓库后先做只读盘点:
git --version
git remote -v
git config --show-origin --get-all credential.helper
git config --show-origin --get-regexp '^credential\.'
git config --show-origin --get-regexp '^http\..*proxy$'--show-origin 很重要。相同配置可能来自系统级、用户级和仓库级文件;只看最终值会漏掉 helper 链和后续覆盖关系。命令没有输出不等于 Git 损坏,只说明对应配置尚未显式设置。
Git 凭证机制把 helper 定义为 Git 获取、保存和擦除凭据的外部程序;GCM 官方项目进一步提供部分托管平台的 OAuth、MFA 和安全存储集成,而且只服务 HTTP(S) 远端。安装前先运行 git remote get-url origin:若远端是 git@host:group/repo.git 这样的 SSH 地址,安装 GCM 不会改变这条连接。
Windows
Git for Windows 当前把 GCM 作为默认 credential helper 选项。已经安装 Git 的机器先验证,不要重复装独立包:
git credential-manager --version
git config --show-origin --get-all credential.helper如果命令存在但未配置,可让 GCM 写入当前用户配置:
git credential-manager configure只有在 Git for Windows 未包含可用 GCM,或企业软件分发明确要求独立更新时,才使用 GCM 官方安装包。官方说明提醒:Windows 独立安装会覆盖 Git for Windows 自带版本,即使捆绑版本更新,因此不能把“单独装一个更新版”当作无影响操作。独立包还分当前用户安装和全系统安装,团队必须明确配置写入 user 还是 system 作用域。
macOS
GCM 安装说明把 Homebrew cask 作为 macOS 首选入口:
brew install --cask git-credential-manager
git credential-manager configure
git credential-manager --version升级仍使用 Homebrew:
brew upgrade --cask git-credential-manager确定不再使用 GCM 时,先撤销 helper 配置再卸载:
git credential-manager unconfigure
brew uninstall --cask git-credential-manager先 unconfigure 再卸载,可以撤销 GCM 写入的 helper 配置;若只是版本升级,不要先清空系统钥匙串,否则会把软件升级变成凭证轮换。
如果团队只需要基本的系统钥匙串存储,Git 自带或平台提供的 osxkeychain helper 也可能足够;需要浏览器 OAuth、多平台一致体验或托管平台特定流程时,再选择 GCM。两者不应在不知道调用顺序的情况下叠加。
Linux
Linux 没有统一的默认 GCM 凭证库。官方提供发行包、压缩包和 .NET tool 等入口;当前安装说明把 .NET tool 列为首选,并要求 .NET SDK 10.0。先用 dotnet --info 确认 SDK 与工具目录,再安装:
dotnet tool install -g git-credential-manager
git credential-manager configure
git credential-manager --version工具目录必须进入运行 Git 的同一个 PATH。升级继续沿 .NET tool 渠道:
dotnet tool update -g git-credential-manager确定卸载时先解除 helper 配置:
git credential-manager unconfigure
dotnet tool uninstall -g git-credential-manager回退到组织保存的上一受支持包时,先解除新版本配置,再安装旧包并重新 configure;不要在新旧二进制都留在 PATH 时只凭版本字符串判断切换成功。
安装 GCM 只是第一步,随后必须在“配置”章节选择可用的安全存储。桌面环境可考虑 Secret Service;已有 GPG/pass 体系可选 gpg;无图形界面的临时环境可用短时内存 cache。不要因为省一步初始化就退回明文存储。
验证 helper 已接入
git config --show-origin --get-all credential.helper
git credential-manager --version预期能看到 GCM 对应 helper,且版本命令成功。不同安装渠道写出的 helper 字符串可能不同,不要机械要求输出必须等于某个固定单词;判断标准是配置来源合理、可执行文件存在、后续 Git 操作确实会调用它。
先理解 helper 调用链
Git 可以配置多个 credential.helper。它会按顺序询问,直到用户名和秘密都已获得。空字符串会重置此前从较低优先级继承的 helper 列表。因此,发现多个 helper 时先审计,不要直接执行 git config --global credential.helper manager 覆盖现场。
git config --show-origin --get-all credential.helper
git help -a需要由 GCM 接管当前用户时,优先使用其自带配置命令:
git credential-manager configure回退也使用对称入口:
git credential-manager unconfigure这比手工猜 helper 名称更能适应不同安装渠道。
在 Git Bash、macOS 或 Linux 的一次性命令中,可以制造一个不接触真实凭证的反例。下面的临时 helper 会先返回演示账号和演示秘密,因此后续 helper 不会再被询问:
printf "protocol=https\nhost=example.invalid\n\n" |
git -c credential.helper= \
-c 'credential.helper=!f() { test "$1" = get && printf "username=stale-user\npassword=stale-secret\n"; }; f' \
-c credential.helper=manager \
credential fill预期输出包含 stale-user 与 stale-secret。它们只是保留域名和一次性示例值,但仍不要把输出提交到仓库。这个反例证明:配置里出现 manager 不代表 GCM 一定执行;按照 Git credential helper 调用规则,前面的 helper 一旦凑齐用户名和未过期秘密,调用链就会停止。命令级 -c 随进程结束自动消失,不会修改 global 或 local 配置。
选择安全凭证库
GCM 当前在 Windows 默认使用 Windows Credential Manager,在 macOS 默认使用 Keychain;Linux 默认不替你选择。可用 Git 配置显式指定:
# Linux 桌面会话,需有可解锁的 Secret Service
git config --global credential.credentialStore secretservice
# Linux 或 macOS,前提是 GPG 与 pass 已正确初始化
git config --global credential.credentialStore gpg
# 临时终端,只在内存中缓存,默认时长以当前官方配置为准
git config --global credential.credentialStore cache选择标准不是“哪个命令最短”,而是秘密如何加密、谁能解锁、是否有图形会话、远程会话能否访问、机器丢失后如何撤销。plaintext 和 Git 内置的 store 都会把秘密写入明文文件,不应成为团队基线。Windows Credential Manager 在 Windows 的网络/SSH 会话中可能无法持久化,远程桌面则不是同一限制;遇到反复登录时要先判断会话类型。
OAuth 与 PAT 怎么选
对支持的托管平台,交互式开发机优先采用 GCM 驱动的浏览器 OAuth:它能配合 MFA,开发者不必手工复制长期 token。PAT 适用于平台不支持该 OAuth 流程、受控无浏览器环境或明确的兼容场景,但必须满足:
使用平台当前推荐的细粒度 token 类型。只授权所需仓库和最小操作范围,读取与写入分开判断。设置合理过期时间,并记录 owner、用途和轮换日期。
不出现在远端 URL、命令历史、脚本参数、.env、日志或工单正文里。平台要求组织 SSO 单独授权时,完成授权后再验证仓库权限。
PAT 在 Git 提示的“Password”位置输入,不代表它是账号登录密码。不要把真实 token 直接拼到 https://TOKEN@host/repo.git。
OAuth 也不是“登录一次永久有效”。GCM 可能在系统凭证库中保存可续期材料,并在访问令牌失效后按平台流程刷新;账号禁用、组织授权撤销、refresh token 失效或条件访问策略变化都会终止这条链。PAT 通常依附创建它的用户身份,轮换时必须在平台侧撤销旧值,再清理本地匹配项。以 GitHub 为例,官方凭证类型参考区分 classic PAT、fine-grained PAT、GitHub App 令牌等生命周期和撤销方式;其他平台也必须按自己的凭证类型选择,不应把 GitHub 的字段照搬过去。
同一主机使用多个账号
Git 默认通常按协议和主机匹配 HTTP 凭证,不把仓库路径作为隔离维度。这意味着同一域名下的工作账号和个人账号可能串用。GCM 官方建议为 URL 加入唯一用户名:
git remote set-url origin https://work-id@code.example.com/team/service.git用户名只是帮助 Git/GCM 区分身份,真正的权限仍由 OAuth 或 token 决定。提交里的 user.name、user.email 与网络认证账号是两套对象,改提交邮箱不会切换 GCM 登录账号。
如果自托管平台确实按 URL 路径划分不同认证域,可审慎启用:
git config --global credential.https://code.example.com.useHttpPath true启用后每个路径可能产生独立凭据和更多登录提示。对同一账号访问大量仓库的公共平台,贸然全局启用会增加凭据碎片;先在测试域名验证,再决定作用域。
代理配置
GCM 官方建议优先复用 Git 标准 http.proxy,这样 Git 传输和 GCM 授权请求使用一致入口:
git config --global http.proxy http://proxy.example.com:8080
# 只让当前仓库的 origin 使用代理
git config --local remote.origin.proxy http://proxy.example.com:8080代理 URL 中不要写密码。Git 支持只写代理用户名并通过凭证机制获取秘密,但具体代理认证方式和企业终端策略需由网络 owner 给出。代理还必须透明转发 Git HTTP 流量;会改写、缓冲或截断请求的代理可能造成克隆或推送异常。
查看和回退:
git config --show-origin --get-regexp '^http\..*proxy$'
git config --global --unset-all http.proxy
git config --local --unset-all remote.origin.proxy不要用 http.sslVerify=false 绕过企业证书问题。证书信任链属于网络与证书专题;这里的正确动作是确认失败发生在 Git 传输、GCM 授权端点还是代理 TLS,并导入经过组织确认的 CA。
验证要分成四层,避免“浏览器登录成功”被误判成“仓库可写”。
验证远端和 helper
git remote get-url origin
git config --show-origin --get-all credential.helper远端应为 https://...,helper 来源应符合团队基线。
验证读取
git ls-remote --exit-code origin HEAD首次运行可能打开浏览器或安全提示。预期输出一个对象 ID 和 HEAD;没有输出、401/403、代理错误或证书错误都要按后文分层排查。
在测试仓库验证写入和清理
在专用测试仓库创建临时分支并推送:
git switch -c auth-check
git commit --allow-empty -m "验证 HTTPS 写权限"
git push -u origin auth-check
git push origin --delete auth-check
git switch -
git branch -D auth-check预期临时分支能创建并删除。受保护分支拒绝直推不等于凭证失败;测试仓库应预先约定允许创建临时分支。
删除凭据后重新认证
使用 Git 的 credential 接口通知 helper 擦除指定主机凭据,而不是在系统凭证库里盲删相似条目:
printf "protocol=https\nhost=code.example.com\n\n" | git credential reject
git ls-remote --exit-code origin HEADPowerShell 可使用:
"protocol=https`nhost=code.example.com`n" | git credential reject
git ls-remote --exit-code origin HEAD预期第二条命令重新触发认证,并在授权后读取成功。多账号环境还应在输入中加入对应 username;启用了 useHttpPath 时也要加入 path,否则可能删除过宽或没有命中。最后再做一次测试分支推送,才算完成矩阵要求的“删除、重新认证并 push”。
新项目
从平台复制 HTTPS URL,不把 token 放进 URL:
git clone https://code.example.com/team/service.git
cd service
git remote -v
git ls-remote --exit-code origin HEAD多账号时,在域名前加入团队约定的账号标识:
git clone https://work-id@code.example.com/team/service.git已有项目切换为 HTTPS
git remote set-url origin https://code.example.com/team/service.git
git remote get-url origin
git ls-remote --exit-code origin HEAD切换协议只改变网络认证,不会修改提交作者、分支历史或提交签名。项目文档可记录 URL 形态和认证入口,但不得记录 PAT。
用仓库级配置控制例外
全局配置适合统一 helper,仓库级配置适合明确的代理或特殊账号例外:
git config --local remote.origin.proxy http://proxy.example.com:8080
git config --local credential.username work-id
git config --local --list --show-origin仓库级配置仍位于本地 .git/config,不会随普通提交共享。团队需要共享的是一份无秘密的配置说明或初始化脚本,而不是开发者机器上的凭据条目。
# 查看实际远端
git remote -v
git remote get-url origin
# 查看 helper 和凭证相关配置的来源
git config --show-origin --get-all credential.helper
git config --show-origin --get-regexp '^credential\.'
# 查看代理来源
git config --show-origin --get-regexp '^http\..*proxy$'
# 只读验证认证与仓库可见性
git ls-remote --exit-code origin HEAD
# 配置或解除 GCM
git credential-manager configure
git credential-manager unconfigure
# 删除指定凭据,让下次操作重新认证
printf "protocol=https\nhost=code.example.com\n\n" | git credential reject需要调试 HTTP 时,可在一次性终端中启用 GIT_TRACE、GIT_TRACE_CURL 或 GIT_CURL_VERBOSE。调试输出可能包含仓库路径、代理地址、账号标识和敏感请求元数据;只在受控环境查看,分享前脱敏,排障结束立即关闭。不要把完整日志直接贴进公开 issue。
登录过一次后仍然报 401 或 Authentication failed
浏览器显示授权成功,但 git fetch 仍返回 401,或者持续使用旧账号。
运行 git remote get-url origin 和 git config --show-origin --get-all credential.helper;确认 URL 主机、用户名维度和 helper 调用链。再用 git credential reject 精确删除该上下文。
旧密码或旧 token 仍在更靠前的 helper;远端 URL 指向另一个企业实例;同域多账号被匹配为同一凭据;组织 SSO 尚未授权。
纠正 URL,清理准确的 credential context,按平台流程重新完成 OAuth/SSO;不要一次性清空整台机器的所有 Git 凭据。
先执行 git ls-remote --exit-code origin HEAD,再在测试仓库推送并删除临时分支。
读取成功,推送返回 403
fetch 正常,push 被拒绝。
区分 403、保护分支提示和服务端 hook/ruleset 提示。用临时非保护分支验证写权限。
token 只有读取权限、账号没有仓库写角色、组织授权缺失,或目标分支禁止直推。
由仓库 owner 授予最小必要角色或重新签发最小范围 token;如果是保护规则,走 PR/MR,不通过扩大 token 权限绕过治理。
推送测试分支成功后删除分支;保护分支仍应保持拒绝直推。
Linux 每次操作都重新登录
GCM 能认证,但新终端或重启后凭据消失。
查看 credential.credentialStore、桌面会话和 Secret Service 是否可解锁;无界面环境检查 GPG_TTY、pinentry 或内存 cache 生命周期。
Linux 未选择凭证库;Secret Service 依赖图形会话;gpg/pass 未初始化;选择了短时 cache;远程会话无法访问底层存储。
桌面机选可用的 Secret Service,受管终端初始化 gpg/pass,临时环境接受短时 cache 并明确重新认证预期。不要切换到明文 store。
关闭当前终端、重新打开后运行 git ls-remote,确认行为符合所选存储的生命周期。
代理返回 407,或浏览器能登录而 Git 不通
Web 页面可访问,Git/GCM 报代理认证失败、连接超时或 TLS 错误。
比较系统代理、http.proxy、环境变量和仓库级 remote.origin.proxy;分别测试托管主机和授权端点。
浏览器使用系统代理而 Git 未使用;Git 与 GCM 使用了不同代理;代理用户名或认证方式错误;代理改写 TLS 或 Git HTTP 流量。
优先配置 Git 标准代理;密码交给凭证机制或企业代理客户端,不写进配置;证书问题交由受信 CA 方案解决。
git ls-remote 成功后再做小分支推送,并确认配置中没有代理密码。
GCM 存在,但 Git 没有调用它
git credential-manager --version 成功,Git 仍在终端询问密码,或调用另一个 helper。
查看所有 credential.helper 的值和来源,检查是否有空值重置、仓库级覆盖或不可执行路径。
安装未执行 configure;旧 helper 排在前面并返回了完整凭据;配置文件层级覆盖;运行 Git 的环境与安装 GCM 的环境不同,例如 Windows Git 与 WSL Git 混用。
在实际执行 Git 的环境中运行 GCM 配置命令;清理经过确认的旧 helper;为 WSL 按官方方案单独接入,不假设 Windows 配置自动生效。
重新查看配置来源,并在删除旧凭据后观察是否触发预期登录方式。
HTTPS 认证方案至少要区分三类主体:
| 主体 | 推荐凭证 | 保存位置 | 关键边界 |
|---|---|---|---|
| 开发者交互式终端 | GCM 驱动的 OAuth,必要时细粒度 PAT | 个人系统凭证库 | MFA、最小仓库范围、离职回收 |
| 自动化或机器人 | 平台 App、服务账号或短期 token | CI/密钥管理系统 | 不进入个人 GCM,不与人共用账号 |
| 临时无界面环境 | 短时 token 或内存 cache | 进程/受控临时存储 | 任务结束失效,不持久化到镜像 |
GCM 适合人在开发机上的交互式认证,不是 CI secret manager。把个人 OAuth token 从系统凭证库导出给流水线,会破坏身份归属、轮换和审计。CI 应使用平台支持的短期身份、App 安装令牌或专用服务账号,并由 CI 专题管理注入和遮罩。
凭证泄漏后的顺序是:先在托管平台撤销,立即阻断继续使用;再从 GCM/系统凭证库清理;检查审计日志、仓库访问和异常操作;最后签发最小权限替代凭证并验证。仅删除本机缓存不能让已泄漏 token 失效。
团队基线应是一套无秘密、可检查、可退出的规则:
明确 owner:开发体验 owner 维护 GCM 安装与配置基线;身份平台 owner 维护 OAuth/PAT/SSO 规则;仓库 owner 维护仓库权限。规定首选路径:交互式开发机优先 OAuth + 系统安全存储;PAT 只用于明确例外;明文 store 不准入。固定验证仓库:提供无生产数据的仓库,让新成员完成读取、临时分支推送、删除和重新认证演练。
管理多账号:约定 HTTPS URL 的用户名标识或路径隔离策略,禁止靠“弹窗时记得选对”维持正确性。轮换与离职:记录 token owner、用途、范围和到期;离职时同时撤销平台授权、服务账号关系和本地设备会话。保留回退:GCM 升级异常时可 unconfigure 并回到经批准的系统 helper;回退不允许降级到明文凭证。
审计例外:代理、无界面环境、自托管旧平台等例外要有到期日,不能永久留在个人 .gitconfig 里无人维护。
共享配置脚本只写 helper、代理主机和无秘密的账号匹配规则。任何会生成 PAT、导出 token 或读取系统凭证库的脚本,都不应进入普通项目模板。
凭证匹配粒度是一项架构决策
默认按主机复用,登录次数少,但同域多账号容易串号;开启 useHttpPath 隔离更细,却会制造大量凭据和授权弹窗;在 URL 中加入用户名通常是同平台多账号的平衡方案。团队要依据“账号数、平台域名结构、仓库数量、SSO 方式”选择,不应每位开发者各自试错。
helper 链会产生隐蔽的优先级故障
系统包、IDE、Git 发行版和手工安装都可能写入 helper。前一个 helper 一旦返回完整用户名和秘密,后面的 GCM 就不会被调用。升级前后保存 git config --show-origin --get-all credential.helper 的基线,比只记录 GCM 版本更有排障价值。
“安全存储”不等于“永不失窃”
系统凭证库降低明文落盘风险,但已登录的用户进程、恶意扩展、被接管的桌面会话仍可能代表用户调用凭证。端点加密、屏幕锁、最小权限、MFA、短有效期和平台异常登录审计仍不可缺少。
无界面和远程会话不能照搬桌面配置
Linux Secret Service 需要可解锁会话,Windows Credential Manager 在网络/SSH 会话中有限制,浏览器 OAuth 也可能无法弹出。架构上应把“个人桌面”“远程开发机”“临时云终端”“CI runner”分开设计,而不是给所有环境下发同一份 GCM 配置。
网络日志和诊断包本身可能敏感
GCM 诊断、Git trace 和代理日志可以暴露主机、仓库路径、账号标识、租户信息和请求元数据。团队排障流程应规定采集范围、脱敏、访问权限和删除期限;日志不应作为普通聊天附件长期留存。
轮换必须验证旧凭证已经不可用
“新 token 能用”只证明替代链路成功,不证明旧 token 已撤销。完整轮换要在平台侧撤销旧凭证、清理本地匹配项、重新认证、推送测试分支,并从审计入口确认旧凭证没有继续活动。
安装后
git credential-manager --version 成功,安装渠道与团队基线一致。credential.helper 的所有值和来源已审计,没有未知旧 helper 抢先返回凭据。Windows/macOS 使用系统安全存储;Linux 已显式选择并验证可解锁的凭证库。
未启用 Git store 或 GCM plaintext 作为团队方案。
使用前
origin 是预期的 HTTPS 主机和仓库,URL 中不含 token 或代理密码。多账号仓库有明确用户名维度或经过评审的路径隔离策略。OAuth/PAT 已按最小仓库、最小操作范围授权,并满足 MFA/SSO 要求。
代理和企业 CA 配置来源清楚,没有关闭 TLS 校验。
验证时
git ls-remote --exit-code origin HEAD 能读取目标仓库。专用测试仓库能创建并删除临时分支。使用 git credential reject 删除准确上下文后能重新认证。
重新认证后再次推送成功,旧凭证在平台侧已撤销。
团队推广前
已明确 GCM、身份平台、仓库权限和网络代理的 owner。有无秘密的配置模板、验证仓库、轮换和离职回收流程。个人凭证、机器人凭证和 CI 凭证没有混用。
诊断日志有脱敏、访问控制和删除期限。升级和回退都不会落到明文凭证方案。
