1. 项目概述:为什么配置 GitLab 的 SSH Key 总要先搞清原理
1.1 这玩意儿到底解决什么问题
先说个场景。某次帮A同学排查推送代码失败,他一脸茫然地说“我明明已经配置了SSH key啊,为什么还是让我输账号密码?”我让他执行一条命令,结果提示Permission denied,再看他的仓库地址,用的是HTTPS链接。这类事我遇到过太多次,所以开篇先把这个概念理清楚。
GitLab作为代码托管平台,支持两种常见的远程访问协议:HTTPS和SSH。用HTTPS方式克隆仓库,每次 push 和 pull 都可能要输账号、密码或者令牌;一旦项目启用了两步验证,密码方式基本就废了,得去生成Personal Access Token再当密码用,麻烦且容易过期。而SSH方式,靠的是一对密钥文件在本地完成身份验证,配置好之后,push、pull都不需要再输密码,整个过程对日常开发效率的提升非常明显。这里说的“配置 GitLab 的 SSH key”,就是把本地生成的一对公钥和私钥中的公钥登记到GitLab账号上,让平台能识别你这台电脑、这个用户。
1.2 公钥和私钥的分工逻辑
很多人分不清.pub文件和没有后缀的那个文件分别干嘛。你可以把私钥理解成你自己的身份证原件,绝对不能离身、不能发给别人;公钥则像是身份证复印件,可以放心交给GitLab这样的服务方存档。
当你执行 ssh -T git@gitlab.example.com 这类命令时,GitLab会先检查你的账号下有没有对应的公钥记录,然后通过一条加密握手流程来验证你手里确实持有配对的私钥。只有两者匹配,服务器才肯放行。这套机制的好处是,私钥永远不离开本机,网络上传输的只是验证过程中生成的临时挑战数据,所以即使传输被截获,对方也拿不到你的真实私钥。
1.3 配置前必须纠正的几个错误认知
第一个误区,有人以为把两个文件都上传到GitLab就完成了。实际上GitLab只需要填公钥内容,私钥要留在本地,配给本地SSH客户端使用。你如果把私钥内容粘到网页上,等于把大门钥匙交给保安之外的其他人。
第二个误区,把GitLab的SSH key和GPG签名key混为一谈。SSH key负责传输层的认证,GPG key负责给提交记录做签名,两者互不替代。
第三个误区,以为配好SSH key之后,之前已经用HTTPS方式克隆下来的仓库会自动切换。不会。你得显式修改 remote 地址,否则后面依旧一套密码操作走到底。
2. 环境准备与密钥生成:动手前先盘一遍家底
2.1 检查本机到底有没有现成的密钥
很多人一上来就执行 ssh-keygen 狂按回车,结果生成了第二、第三套密钥,最后自己都分不清哪一套在哪儿。正确的操作是先看看家目录下的 .ssh 文件夹:
ls -la ~/.ssh如果有 id_rsa、id_ed25519 这类文件,说明之前已经生成过密钥。再看对应 .pub 文件的内容:
cat ~/.ssh/id_ed25519.pub注意,这里有个常见分歧点:已有密钥能不能复用?我的习惯是,如果是自己维护的个人电脑,之前的密钥没有任何异常泄露风险,直接复用它最省事。但如果你不确定密钥有没有被人拷走过,或者压根不知道这套密钥是哪个项目留下来的,那就别犹豫,重新生成一套。信息安全这条线,该较真的时候必须较真。
如果完全没有 .ssh 目录,那正好,干净的机器生成新密钥最省心。
2.2 生成密钥时的参数选择:用 ed25519 还是 RSA
现在新配置SSH key,我优先推荐 Ed25519 算法:
ssh-keygen -t ed25519 -C "work-account@example.com"这里 -C 后面的内容其实只是备注。GitLab的SSH key列表页会把这段备注显示出来,方便你以后认出来这把钥匙是哪台电脑用的。建议大家写清楚用途,比如 company-laptop、home-desktop,别留空。
至于为什么不用经典的RSA,一句话总结:Ed25519密钥更短、生成速度更快、安全性在当前主流实践中足够可靠。但如果你所在团队还在维护老旧的自动化脚本、远程设备上跑着较老版本的SSH客户端,某些环境可能还不支持Ed25519算法。这种情况退回RSA,指定4096位长度更稳妥:
ssh-keygen -t rsa -b 4096 -C "legacy-server@example.com"另外一个容易被忽略的参数是 -f,它控制密钥文件生成到哪个路径、叫什么名字。我强烈建议在生成时就给密钥起一个有意义的名字,而不是顺手用默认文件名。比如我要配置公司GitLab和某个个人实验环境,我可以这样生成两套完全分开的密钥:
ssh-keygen -t ed25519 -C "gitlab-work-2025" -f ~/.ssh/gitlab_work_ed25519 ssh-keygen -t ed25519 -C "gitlab-personal-lab" -f ~/.ssh/gitlab_personal_ed25519这样后面管理多账号、多平台时,一眼就能看明白哪个文件对应哪个场景。
2.3 passphrase 到底要不要设
生成过程中会提示 Enter passphrase。如果直接按回车,私钥文件本身没有额外口令保护,任何拿到你私钥文件的人都能直接使用。如果你不想每次push都敲一次口令,就容易选择空密码,我也能理解,但风险确实存在。
折中方案是设置一个passphrase,然后用ssh-agent把密钥加载到内存。这样你只需要在电脑开机后或首次会话中输入一次口令,之后一段时间内后台自动解密。
eval "$(ssh-agent -s)" ssh-add ~/.ssh/gitlab_work_ed25519macOS用户可以用 ssh-add --apple-load-keychain 把密钥加载到系统钥匙串,减少重复输入次数。Windows用户则可以直接用系统自带的OpenSSH,并在服务中启用“SSH Agent”,把私钥添加进去。
我自己在实际项目中,凡是配公司生产相关环境的密钥,一定设passphrase;纯个人玩具项目可能就不设了,看风险承受能力分场景处理。
3. 把公钥配置到 GitLab 并验证全链路
3.1 提取公钥内容,别带多余字符
生成完密钥后,公钥文件是带 .pub 后缀的那个。复制内容有几种方式:
cat ~/.ssh/gitlab_work_ed25519.pub也可以借助剪贴板命令,比如macOS用 pbcopy,Linux桌面可以看具体发行版,Windows可以用 PowerShell 的 Get-Content 配合剪贴板。复制时注意,整个公钥是单行内容,千万不要自己手动换行,也不要复制时带上文件路径提示符之类的东西。我在实际排查中见过太多次因为复制到部分粘贴,导致开头少了 ssh-ed25519 前缀,GitLab界面报格式错误。
3.2 GitLab 网页端添加位置
登录GitLab后,点击右上角头像,进入 Edit Profile(偏好设置),左侧菜单找到“SSH Keys”。页面里常见三个字段:
- Key:粘贴完整的公钥内容
- Title:给这把钥匙起个可识别的名字,比如 macbook-pro-work
- Expiration date:过期时间
过期时间这个字段很容易被忽视。GitLab现在允许你设置多个SSH密钥,并给每个密钥单独设置最长无效日期。建议大家都养成设有效期的习惯,比如90天或180天,到期后重新轮换。虽然多了一点维护成本,但真到了员工离职、电脑丢失的时候,你就能体会到临时密钥自动失效省了多少事。
添加成功后,列表页会显示密钥的指纹,一串形如 SHA256:xxxxxxxx 的字符串。这个指纹后面可以用来对照排查。
3.3 验证连接:一条命令看全局
配置完公钥,别急着push,先跑验证命令:
ssh -T git@gitlab.example.com第一次连接时,SSH会问你是否确认远程主机的指纹,输入 yes 回车即可。接着如果看到:
Welcome to GitLab, @yourname!那就说明整个链路已经通了。这里的 git@ 是固定前缀,不管你的账号是什么,连GitLab统一用它。真正有意思的是输出里显示的这个 @用户名,它直接反映了你是以哪个GitLab账号身份进入的。如果你有多个账号,就能通过这个输出来判断当前SSH会话到底认了哪个身份。
3.4 把已有仓库从 HTTPS 切换到 SSH
很多老仓库clone下来时就是HTTPS地址。查看一下:
git remote -v如果显示的是 https://gitlab.example.com/group/project.git,那就需要手动改:
git remote set-url origin git@gitlab.example.com:group/project.git注意,SSH地址里的 group/project 不是随便猜的,它是你在网页上打开项目时URL里跟在域名后面的那一串路径。改完之后,再跑一次 git pull 或者 git push 验证。新手最容易在这步出错:remote地址里把账号名代替了 group 段,结果配多少密钥都是白搭。
4. 多账号与多平台的 SSH config 配置实践
4.1 默认逻辑下密钥会被怎么选择
如果我的 .ssh 目录里同时存在 id_ed25519、gitlab_work_ed25519、gitlab_personal_ed25519 多个私钥,而我只往GitLab传了其中一把公钥,那连接时SSH默认会尝试用默认文件名的那把钥匙去验证。如果默认钥匙不在GitLab的记录里,对方直接返回 Permission denied。这就是很多同时使用多个代码托管平台的人经常踩坑的原因。
解决办法核心就一句话:让SSH客户端在连接不同主机时,明确指定该用哪把私钥。你不需要每用一次就通过命令行参数去指定,完全可以用 ~/.ssh/config 把这些规则固化下来。
4.2 config 文件的字段说明
config 文件写在 ~/.ssh/config 下,没有扩展名。基本模板如下:
Host gitlab.example.com HostName gitlab.example.com User git IdentityFile ~/.ssh/gitlab_work_ed25519 IdentitiesOnly yes每个字段都有讲究。Host 是你在命令行里实际输入连接对象时用的别名;HostName 是真实的服务器域名或IP;User 固定为 git;IdentityFile 指定要用哪把私钥;IdentitiesOnly 设为 yes 最关键,意思是“只用我指定列表里的私钥,别自作主张把我所有密钥都试一遍”。如果不设这一项,SSH客户端可能会把当前代理里的所有身份都尝试提交,导致服务器日志里一堆过量的签名尝试记录,还容易匹配到错误的账号上。
如果公司内部有多个不同的GitLab服务,比如一个正式环境、一个测试环境,建议分别写不同的Host块。如果两个环境的域名一样但端口不同,那还要补充 Port 字段。
4.3 同一台机器管理两套 GitLab 账号
我自己的实践是给每个场景设置独立别名。比如:
Host gitlab-work HostName gitlab.example.com User git IdentityFile ~/.ssh/gitlab_work_ed25519 IdentitiesOnly yes Host gitlab-lab HostName lab.gitlab.example.com User git IdentityFile ~/.ssh/gitlab_personal_ed25519 IdentitiesOnly yes然后 clone 或 remote 地址就可以写:
git clone git@gitlab-work:group/project.git git clone git@gitlab-lab:group/my-test-project.git有人可能会担心,Host 改了之后,验证命令还能不能跑。照样可以,只是域名换成别名:
ssh -T git@gitlab-work输出里如果显示 @真实的GitLab用户名,则说明这个别名对应的是工作账号;再验证 gitlab-lab 会看到另一个用户名。这就是我处理多账号最推荐的方式,直观、好维护、不用反复改remote。
5. 常见问题排查与避坑经验汇总
5.1 Permission denied (publickey) 的根源排查顺序
这条错误几乎占掉GitLab SSH配置问题的一大半。出现之后不要慌着删密钥重做,按顺序查:
第一步,先看SSH实际用了哪把钥匙:
ssh -vT git@gitlab.example.com在输出的 debug 日志里找 “Offering public key” 相关字样,后面跟着的文件路径就是当前尝试的私钥。如果这把私钥对应的公钥根本没加到GitLab里,后面肯定被拒。
第二步,确认公钥粘贴时格式完整。把本地 .pub 文件内容复制出来,用编辑器检查开头是否为 ssh-ed25519 或 ssh-rsa,结尾有没有隐藏换行。
第三步,检查本机文件权限。SSH对密钥文件的权限很较真,如果私钥文件权限太宽松,它会直接拒绝使用。建议执行:
chmod 700 ~/.ssh chmod 600 ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pubWindows上可能没有这么严格的权限机制,但如果用了WSL环境,同样适用。
5.2 key_load_public: invalid format 怎么处理
这个报错一般是公钥文件内容损坏或格式不对。一种典型场景,是拿Windows记事本打开过公钥,保存时悄悄把编码或换行符改坏了。另一种场景,是复制公钥时手动加了换行。
处理办法很简单,别在原有文件上纠结,直接重新生成一套密钥,然后重新把公钥加进GitLab。这种格式问题修复成本远低于排查成本。另外,GitLab界面上如果提示“公钥格式无效”,先反思是不是粘贴了私钥文件内容。私钥通常以 BEGIN OPENSSH PRIVATE KEY 开头,公钥则直接由算法名开始。
5.3 Could not resolve hostname 意味着什么
这个报错和密钥本身没关系,纯粹是域名解析不到。常见原因包括:地址敲错、公司内网DNS没有配置对应的解析记录、或者本机hosts文件里没有写映射。
如果你知道GitLab服务在内网IP上,可以临时在 /etc/hosts 里加一条映射,但要记得这只是临时手段。更规范的做法是请管理员确认正确的访问域名,统一走DNS。用config文件设置Host别名时,如果HostName写错了,也会出现类似报错,我排查时通常先 ssh -T 真实域名,再通过config别名验证。
5.4 连接超时、端口被阻断的应对思路
GitLab默认的SSH端口是22。如果网络环境对22端口有限制,有些运维会将GitLab的SSH服务独立到一个高位端口,或者直接提供443端口的SSH访问方式。
配置时候可以在config块里显式指定:
Host gitlab.example.com HostName gitlab.example.com User git Port 443 IdentityFile ~/.ssh/gitlab_work_ed25519 IdentitiesOnly yes如果超时反复出现,先确认是不是只有GitLab这一个服务连不上。如果是所有外部连接都异常,那大概率是本地网络环境或安全策略的问题,别埋头改SSH配置。
5.5 换电脑、换SSH客户端时的迁移建议
很多人喜欢把 .ssh 目录整个打包拷到新电脑,追求“无缝迁移”。我个人的建议是:能拷,但尽量别只依赖拷贝。原因在于,私钥文件被复制到新机器后,如果新机器本身裸露风险更高,等于扩大暴露面。更稳妥的做法是,在旧机器上确认没有遗留未知风险后,直接在新机器生成新密钥,把新公钥单独加到GitLab,旧密钥顺手在GitLab后台删除。这样能避免旧密钥散落多地的风险,还能让GitLab上的密钥列表始终保持干净。
如果确实因为自动化部署原因必须迁移私钥,那至少要在拷贝后立即重置passphrase:
ssh-keygen -p -f ~/.ssh/id_ed25519它会提示你先输入旧口令,再设置新口令,然后文件内容会以新口令重新加密。
我自己平时维护多个平台密钥的习惯,是每半年例行检查一次GitLab后台的SSH key列表,能删的旧钥匙绝不手软,同时在本地用 ssh-keygen -l -f 看看每把私钥对应的指纹。如果你哪天发现某台旧设备的key还在有效期,而你根本想不起来它放在哪,那大概率就是潜在的访问风险。别看SSH key配置只是开发环境里一个不起眼的步骤,把原理和维护逻辑想明白,后面能避免很多连接问题。