从 macOS 钥匙串弹窗到 GitLab SSH:一次自建 GitLab 访问方式优化实践
一、问题背景
某些项目在 macOS 上通过 IDE 打开时,会不断弹出类似下面的系统授权窗口:
git-credential-osxkeychain wants to use your confidential information stored in "gitlab.example.internal" in your keychain.
这类弹窗通常不是 GitLab 服务端异常,而是本机 Git 在访问 HTTPS remote 时,调用 macOS Keychain 凭据助手读取用户名、密码或 token。
典型本机 Git 配置如下:
git config --system --get credential.helper
输出:
osxkeychain
如果项目 remote 是 HTTPS:
git remote -v
示例输出:
origin https://gitlab.example.internal/group/project.git (fetch)
origin https://gitlab.example.internal/group/project.git (push)
那么 IDE 后台执行 fetch、status、pull 等操作时,就可能触发钥匙串访问。
二、初步判断:本机问题还是 GitLab 服务端问题?
排查时不要直接改配置,先收集证据。
1. 检查本机 Git 配置
git config --global --list --show-origin
git config --system --list --show-origin
重点看:
credential.helper=osxkeychain
这说明 Git 会使用 macOS 钥匙串管理 HTTPS 凭据。
2. 扫描本地仓库 remote
可以扫描常见项目目录:
find ~/Projects -path '*/.git/config' -type f -print0 \
| xargs -0 grep -n 'https://gitlab.example.internal'
如果能看到大量类似配置:
/path/to/project/.git/config: url = https://gitlab.example.internal/group/project.git
说明这些项目打开时都有可能触发 HTTPS 凭据读取。
3. 检查 GitLab 服务端域名配置
如果是 Docker 方式部署 GitLab,可以进入容器检查:
docker exec gitlab grep -nE 'external_url|gitlab_shell_ssh_port|gitlab_ssh_host' /etc/gitlab/gitlab.rb
示例:
external_url 'https://gitlab.example.internal'
再检查生成后的配置:
docker exec gitlab grep -nE 'ssh_host|ssh_port' \
/var/opt/gitlab/gitlab-rails/etc/gitlab.yml
如果 HTTPS 页面、证书、external_url 都正常,那么弹窗大概率不是 GitLab 服务端故障,而是本机 HTTPS 凭据链路导致。
三、为什么推荐从 HTTPS 改为 SSH?
HTTPS remote 的认证依赖凭据管理器,例如 macOS Keychain。
SSH remote 则使用本地 SSH key 认证,IDE 后台访问时不会再调用 git-credential-osxkeychain。
HTTPS remote:
https://gitlab.example.internal/group/project.git
SSH remote:
git@gitlab.example.internal:group/project.git
如果 GitLab SSH 暴露端口不是标准 22,例如 Docker 宿主机使用 3022 映射容器内 22,可以通过 ~/.ssh/config 隐藏端口差异。
四、检查 GitLab SSH 服务端配置
假设 GitLab 运行在 Docker 中:
docker ps
示例输出:
gitlab/gitlab-ce:18.x.x 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:3022->22/tcp
这说明:
宿主机 3022 端口 -> GitLab 容器 22 端口
此时需要在 GitLab 配置中显式声明外部 SSH 端口:
external_url 'https://gitlab.example.internal'
gitlab_rails['gitlab_ssh_host'] = 'gitlab.example.internal'
gitlab_rails['gitlab_shell_ssh_port'] = 3022
修改后执行:
docker exec gitlab gitlab-ctl reconfigure
验证:
docker exec gitlab grep -nE 'ssh_host|ssh_port' \
/var/opt/gitlab/gitlab-rails/etc/gitlab.yml
预期类似:
ssh_host: gitlab.example.internal
ssh_port: 3022
五、本机生成 GitLab 专用 SSH Key
建议为自建 GitLab 单独生成一把 key,不混用服务器登录 key。
ssh-keygen -t ed25519 \
-C "user@gitlab.example.internal" \
-f ~/.ssh/id_ed25519_gitlab_example
如果希望 IDE 后台 fetch 不再弹其他密码提示,可以不设置 passphrase。
如果安全要求更高,也可以设置 passphrase,并配合 ssh-agent 使用。
查看公钥:
cat ~/.ssh/id_ed25519_gitlab_example.pub
示例:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExamplePublicKey user@gitlab.example.internal
将这行公钥添加到 GitLab:
头像 -> Preferences -> SSH Keys -> Add new key
标题可以写:
MacBook Pro gitlab.example.internal
六、本机配置 SSH Host
如果 GitLab 实际端口是 3022,建议写入 ~/.ssh/config:
Host gitlab.example.internal
HostName gitlab.example.internal
User git
Port 3022
IdentityFile ~/.ssh/id_ed25519_gitlab_example
IdentitiesOnly yes
这样后续 remote 可以保持简洁:
git@gitlab.example.internal:group/project.git
而不需要每个 remote 都写成:
ssh://git@gitlab.example.internal:3022/group/project.git
验证 SSH 登录:
ssh -T git@gitlab.example.internal
成功时通常会看到:
Welcome to GitLab, @username!
七、单项目试迁移
不要一开始就批量改,先选一个项目验证。
查看当前 remote:
git remote -v
修改 remote:
git remote set-url origin git@gitlab.example.internal:group/project.git
验证:
git remote -v
git fetch --prune origin
如果 fetch 成功,说明 SSH 认证、GitLab 权限、remote 地址都正常。
也可以用更轻量的方式验证:
git ls-remote --heads origin
八、批量迁移本地仓库
确认单项目无误后,再扫描并迁移多个项目。
示例扫描:
find ~/Projects -path '*/.git/config' -type f -print0 \
| xargs -0 grep -n 'https://gitlab.example.internal'
逐个改:
git -C /path/to/project-a remote set-url origin \
git@gitlab.example.internal:group/project-a.git
git -C /path/to/project-b remote set-url origin \
git@gitlab.example.internal:group/project-b.git
验证:
git -C /path/to/project-a ls-remote --heads origin
git -C /path/to/project-b ls-remote --heads origin
九、特殊情况:Submodule 仓库
如果项目里有 submodule,不能只改主仓库的 origin。还需要检查 .gitmodules:
cat .gitmodules
HTTPS 示例:
[submodule "modules/example"]
path = modules/example
url = https://gitlab.example.internal/group/submodule.git
branch = main
应改为:
[submodule "modules/example"]
path = modules/example
url = git@gitlab.example.internal:group/submodule.git
branch = main
同步 submodule 配置:
git submodule sync --recursive
验证:
git submodule status --recursive
git config --get submodule.modules/example.url
注意:.gitmodules 是版本化文件,修改后会出现在 Git 工作区中,需要按团队流程提交。
十、如何确认已经没有 HTTPS GitLab remote?
可以全目录扫描:
find ~/Projects -path '*/.git/config' -type f -print0 \
| xargs -0 grep -n 'https://gitlab.example.internal'
如果没有输出,说明这些仓库的 GitLab remote 已经不再走 HTTPS。
也可以扫描 SSH remote:
find ~/Projects -path '*/.git/config' -type f -print0 \
| xargs -0 grep -n 'git@gitlab.example.internal'
十一、回滚方式
如果某个项目切换 SSH 后发现权限或流程问题,可以随时改回 HTTPS:
git remote set-url origin https://gitlab.example.internal/group/project.git
如果是 submodule,也需要改回 .gitmodules:
url = https://gitlab.example.internal/group/submodule.git
然后同步:
git submodule sync --recursive
十二、附:关闭 GitLab Auto DevOps
如果当前不使用 GitLab Auto DevOps,可以关闭实例级默认开关,避免项目被默认套用 Auto DevOps 流水线。
使用 Rails runner:
docker exec gitlab gitlab-rails runner '
s = ApplicationSetting.current
s.update!(auto_devops_enabled: false)
puts s.auto_devops_enabled
'
验证:
docker exec gitlab gitlab-rails runner '
s = ApplicationSetting.current
puts({
auto_devops_enabled: s.auto_devops_enabled,
auto_devops_domain: s.auto_devops_domain
}.inspect)
'
预期:
{:auto_devops_enabled=>false, :auto_devops_domain=>nil}
总结
这类 macOS 钥匙串弹窗,本质上往往不是 GitLab 服务不可用,而是 HTTPS remote 触发了本机凭据助手访问。排查时建议按下面顺序处理:
- 确认项目 remote 是否使用 HTTPS。
- 确认本机 Git 是否启用了
osxkeychain。 - 检查 GitLab 服务端 SSH 端口和 clone URL 配置。
- 生成专用 SSH key 并绑定到 GitLab 用户。
- 先单项目迁移验证,再批量迁移。
- 如果有 submodule,同步修改
.gitmodules。
迁移到 SSH 后,IDE 后台 fetch、pull、branch refresh 等操作就不再依赖 macOS Keychain,弹窗问题也会自然消失。