SSH密钥配置与Git仓库连接:从原理到实战

1. 从一次 Git 拉取失败说起:SSH 到底在干什么

刚入行的头半年,我踩过最多次的坑不是写错代码,而是 git clone 一个私有仓库时,终端冷冰冰地甩出一行 Permission denied (publickey)。当时我盯着屏幕想:我明明有仓库权限,浏览器里都能打开,为什么命令行就不认我?后来才明白,浏览器认的是账号密码加会话,而 Git 命令行认的是另一套东西——SSH 密钥。这两套身份体系是分开的,你在网页上登录成功,不代表你的电脑在 SSH 层面被仓库服务器信任。

这篇内容就是把我这些年带新人时反复讲的那套东西整理出来。它适合三类人:刚学 Git、第一次接触私有仓库的编程新手;在 Linux 服务器上配过 SSH 但总是连不上的运维初学者;以及用 VS Code 远程开发、被密钥配置卡住的开发者。核心就一件事——把 SSH 协议、密钥机制、Git 仓库连接这三者的关系讲透,让你下次再看到 Permission denied 时,知道该去查哪里,而不是盲目地重装 Git 或者重启电脑。

SSH 全称 Secure Shell,直译是“安全外壳”,你可以把它理解成一条加密的隧道。你在这头,服务器在那头,中间隔着公网,所有数据都塞进这条隧道里传,外面的人截获了也看不懂。Git 用 SSH 做什么?就是通过这条隧道,把你的代码推上去、拉下来。而“密钥”就是打开这条隧道大门的钥匙,注意,是钥匙,不是密码。密码是你脑子里记的一串字符,密钥是你电脑硬盘上存的一个文件。这个区别很关键,后面所有的配置逻辑都围绕它展开。

很多人第一次配 SSH 失败,根本原因不是操作错了,而是脑子里没有建立起“公钥放服务器、私钥留本地”这个模型。一旦这个模型立住了,剩下的命令只是形式。所以我不打算一上来就甩命令,而是先把这套机制讲清楚,再动手。

2. SSH 密钥机制拆解:为什么是“公钥放上去,私钥留下来”

2.1 非对称加密的通俗理解

SSH 用的是非对称加密,这个词听着唬人,其实生活里到处都是类似逻辑。想象你家门口有个特殊的信箱:任何人都能往投信口里塞信(这是公钥,可以随便给),但只有你有钥匙能打开信箱取信(这是私钥,绝不能给别人)。服务器就是那个信箱,它手里存着你的公钥;你的电脑是唯一持有私钥的人。当你发起连接时,服务器会出一道只有私钥能解的题,你解对了,它就认你。

这里有个新手最容易搞反的点:公钥是给服务器的,私钥是留在自己电脑上的。我见过有人把私钥内容复制到服务器的 authorized_keys 里,然后怎么都连不上,还以为是服务器坏了。实际上 authorized_keys 这个文件名已经说得很清楚了——它存的是“被授权”的公钥。你把私钥放进去,等于把家门钥匙插在门锁上还指望它安全,逻辑上就错了。

2.2 密钥文件长什么样,放在哪

生成密钥后,默认会在你的用户目录下的 .ssh 文件夹里出现两个文件。以常见的 RSA 或 Ed25519 算法为例,一个是 id_ed25519(私钥,没有后缀),一个是 id_ed25519.pub(公钥,带 .pub 后缀)。私钥文件开头通常是 -----BEGIN OPENSSH PRIVATE KEY-----,公钥则是一行以 ssh-ed25519 或 ssh-rsa 开头的长字符串。

在 Linux 和 macOS 上,这个目录是 ~/.ssh/;在 Windows 上,如果你用的是 Git Bash,同样是 ~/.ssh/,实际路径一般是 C:\Users\你的用户名\.ssh\。这个路径很重要,因为 SSH 客户端默认就去这里找私钥。如果你把密钥生成到了别的地方,或者改了名字,就必须在配置文件里显式指定,否则它找不到,自然就连接失败。

2.3 为什么 Git 仓库偏爱 SSH 而不是 HTTPS

你可能会问,HTTPS 也能拉代码,为什么还要折腾 SSH?原因有三个。第一是免密,配好一次之后,以后 push、pull 都不用再输账号密码,尤其是频繁操作时体验差别巨大。第二是安全,HTTPS 每次传输都要带上凭证,而 SSH 是密钥握手,凭证不在网络上传输。第三是自动化友好,CI/CD 流水线里如果用 HTTPS,你得把密码塞进脚本或环境变量,风险高;用 SSH 密钥则可以精细控制权限,甚至给部署密钥只读权限。

所以主流代码托管平台都同时支持两种协议,但团队协作和自动化场景里,SSH 是更专业的选择。理解了这一点,你就明白为什么“配 SSH 密钥”几乎是每个程序员入职第一周就要做的事。

3. 手把手配置:从生成密钥到 Git 仓库连通

3.1 第一步:检查是否已有密钥

动手之前先别急着生成,很可能你电脑上已经有了。打开终端(Windows 用 Git Bash),执行:

BASH

复制

1

ls -al ~/.ssh

如果看到 id_rsa、id_ed25519 这类文件,说明你已经有密钥了。这时候你有两个选择:直接复用现有的,或者生成一个新的专门给这个仓库用。我的建议是,如果现有密钥已经绑定了其他平台且工作正常,就别动它,新生成一个带备注的密钥,避免互相干扰。

提示:不要随意删除已有的密钥文件。有些服务器或平台可能正依赖它,删了会导致原本能连的地方也连不上。

3.2 第二步:生成一对新密钥

生成命令如下,以 Ed25519 算法为例(它比 RSA 更短、更快、更安全,现在推荐优先用):

BASH

复制

1

ssh-keygen -t ed25519 -C "your_email@example.com"

这里的 -t 指定算法类型,-C 是备注,一般填你的邮箱,方便日后在服务器端辨认这是谁的密钥。执行后它会问你三个问题:保存路径(直接回车用默认)、设置密码短语(passphrase,可以留空,也可以设一个)、确认密码短语。

关于 passphrase,我的经验是:个人电脑可以留空,图方便;公司电脑或者笔记本经常带出门的,建议设一个。设了之后每次用密钥要输一次,但可以用 ssh-agent 缓存,后面会讲。留空的话,任何拿到你电脑的人都能直接用你的密钥,风险自己权衡。

生成成功后会输出一个随机艺术图(就是那个由字符拼成的方块),并告诉你私钥和公钥分别存在哪。看到这个图,说明密钥已经生成好了。

3.3 第三步:把公钥内容复制出来

查看公钥内容:

BASH

复制

1

cat ~/.ssh/id_ed25519.pub

输出的那一整行就是公钥,从 ssh-ed25519 开头,到邮箱结尾。注意只复制这一行,不要复制私钥文件的内容。在 macOS 上可以用 pbcopy < ~/.ssh/id_ed25519.pub 直接复制到剪贴板,Windows Git Bash 里可以用 clip < ~/.ssh/id_ed25519.pub,Linux 上则手动选中复制或用 xclip。

3.4 第四步:把公钥添加到代码托管平台

登录你的代码托管平台(无论是哪家),进入个人设置里的 SSH 密钥管理页面,点击添加新密钥。标题随便起一个能认出这台机器的名字,比如“我的笔记本”或“公司台式机”,然后把刚才复制的那一整行公钥粘贴进去,保存。

这一步做完,服务器端就存下了你的公钥。但注意,此时你的本地 SSH 客户端还不知道该用哪个私钥去连它,所以还需要下一步的验证和配置。

3.5 第五步:验证连接是否成功

执行测试命令:

BASH

复制

1

ssh -T git@你的平台域名

比如常见的写法是 ssh -T git@github.com 或 ssh -T git@gitee.com。第一次连接会提示你确认服务器的指纹,输入 yes 回车。如果配置正确,你会看到类似“Hi 用户名! You've successfully authenticated”的欢迎语。看到这句话,说明 SSH 层面已经通了。

如果这里报 Permission denied (publickey),别慌,这是最高频的错误,下一章专门讲排查。

3.6 第六步:让 Git 用上 SSH

验证通过后,Git 还需要知道你要用 SSH 协议来访问仓库。检查你的远程地址:

BASH

复制

1

git remote -v

如果显示的是 https://... 开头,说明还在用 HTTPS。改成 SSH 格式:

BASH

复制

1

git remote set-url origin git@你的平台域名:用户名/仓库名.git

改完再 git remote -v 确认一下,地址变成 git@ 开头就对了。之后 git pull、git push 就会走 SSH 通道,不再问你要密码。

4. 连接失败排查实录:那些年我踩过的坑

4.1 高频错误速查表

报错信息

大概率原因

解决方向

Permission denied (publickey)

公钥没加到服务器,或本地私钥不匹配

检查公钥是否上传,用 ssh -v 看用了哪个私钥

Host key verification failed

服务器指纹变了或首次连接未确认

清理 known_hosts 里对应条目后重连

Could not resolve hostname

域名写错或网络不通

检查远程地址拼写,测试网络连通性

Connection timed out

端口被挡或服务器不可达

确认网络环境,检查是否限制了 22 端口

Bad owner or permissions on ~/.ssh/config

配置文件权限过宽

chmod 600 ~/.ssh/config

no such identity

指定的私钥文件不存在

检查路径和文件名是否正确

4.2 用 ssh -v 把问题看穿

排查 SSH 问题最有效的工具就是 -v 参数,它会把整个握手过程打印出来。命令:

BASH

复制

1

ssh -vT git@你的平台域名

输出会非常多,但你只需要关注几个关键行。找 debug1: Offering public key 这一行,看它列出的是哪个密钥文件。如果列出的不是你刚生成的那个,说明 SSH 客户端选错了私钥。再看 debug1: Authentications that can continue: publickey,如果后面一直循环这个,最后 Permission denied,基本就是服务器端没有匹配的公钥。

我一般会加两个 -v,也就是 ssh -vvT,信息更全。看输出时从下往上倒着找第一个 debug1 报错,往往就是根因。

4.3 多密钥共存时的配置文件写法

当你同时有多个平台的密钥,或者一台机器上有多对密钥时,SSH 默认只会尝试 id_rsa、id_ed25519 这些默认名字。如果你给密钥起了别的名字,就得靠 ~/.ssh/config 来指定。写法如下:

BASH

复制

1

Host github.com

2

HostName github.com

3

User git

4

IdentityFile ~/.ssh/id_ed25519_github

5

IdentitiesOnly yes

Host 是你在命令行里用的别名,HostName 是真实域名,IdentityFile 指定用哪个私钥,IdentitiesOnly yes 表示只用指定的这个,不要乱试其他的。这个配置能解决 90% 的多密钥冲突问题。配好之后,ssh -T git@github.com 就会精确地用你指定的密钥。

注意:~/.ssh/config 文件的权限必须是 600,也就是只有你自己能读写。权限太宽 SSH 会拒绝加载,报 Bad owner or permissions。执行 chmod 600 ~/.ssh/config 即可。

4.4 权限问题:被忽视的隐形杀手

Linux 和 macOS 对 .ssh 目录及其中文件的权限非常敏感。私钥文件权限必须是 600,目录必须是 700。如果你从别的地方拷贝密钥过来,权限可能变成了 644 甚至 777,SSH 会直接拒绝使用。修复命令:

BASH

复制

1

chmod 700 ~/.ssh

2

chmod 600 ~/.ssh/id_ed25519

3

chmod 644 ~/.ssh/id_ed25519.pub

4

chmod 600 ~/.ssh/config

5

chmod 600 ~/.ssh/authorized_keys

这套权限设置我建议背下来,换新机器或者迁移环境时第一时间执行,能省掉大量莫名其妙的连接失败。

4.5 ssh-agent 让密码短语不再烦人

如果你给密钥设了 passphrase,每次连接都要输一遍,很烦。ssh-agent 可以帮你缓存。启动并添加:

BASH

复制

1

eval "$(ssh-agent -s)"

2

ssh-add ~/.ssh/id_ed25519

输入一次 passphrase 后,当前会话内就不用再输了。macOS 上可以加 --apple-use-keychain 参数把密码存进钥匙串,Windows 上 Git Bash 也自带 agent 支持。这个技巧在需要频繁 push 的日子里能救命。

5. 进阶场景:远程开发与自动化里的 SSH

5.1 VS Code 远程连接服务器的密钥配置

现在很多人用 VS Code 的 Remote-SSH 插件连远程服务器写代码。它的底层也是 SSH,所以配置逻辑完全一样。你需要在本地 ~/.ssh/config 里写好服务器的连接信息,然后在 VS Code 里选择这个 Host 即可。如果连不上,先在终端用 ssh 别名 测试,终端能通,VS Code 基本就能通;终端不通,先解决终端的问题。

常见的一个坑是:VS Code 用的 SSH 客户端可能和终端不是同一个(比如 Windows 上可能用了系统自带的 OpenSSH 而不是 Git Bash 的)。这时候密钥路径和配置文件位置可能不一致。解决办法是在 VS Code 设置里指定 remote.SSH.path 指向你终端里能用的那个 ssh 可执行文件。

5.2 服务器端 authorized_keys 的正确姿势

如果你是在配置一台 Linux 服务器,让别人能 SSH 登录,那服务器端的 ~/.ssh/authorized_keys 文件就是存放所有被授权公钥的地方。每行一个公钥,直接粘贴即可。注意这个文件也必须是 600 权限,.ssh 目录是 700。很多人服务器连不上,就是因为这个文件权限不对,SSH 服务端出于安全考虑直接忽略它。

另外,如果你改了 SSH 服务端的配置(比如 /etc/ssh/sshd_config),改完一定要重启服务:sudo systemctl restart sshd。我见过改完配置不重启,然后对着旧配置排查半天的,纯属浪费时间。

5.3 自动化传输与批量操作

SSH 除了给 Git 用,还能做很多事。比如用 scp 在本地和服务器之间传文件:

BASH

复制

1

scp -i ~/.ssh/id_ed25519 local_file.txt user@server:/remote/path/

-i 指定私钥。批量在多台服务器上执行命令,可以写个循环:

BASH

复制

1

for host in server1 server2 server3; do

2

ssh -i ~/.ssh/id_ed25519 user@$host "uptime"

3

done

前提是这些服务器都存了你的公钥。这种免密登录在运维自动化里是基础能力,配好一次,后面写脚本就顺畅了。

5.4 密钥轮换与安全习惯

最后说点安全习惯。密钥不是配好就一劳永逸的。建议定期轮换,尤其是离职交接或者怀疑泄露时。轮换流程很简单:生成新密钥、上传新公钥、验证新密钥能连、删除旧公钥、删除本地旧私钥。整个过程不影响正在运行的服务。

还有一点,私钥文件永远不要提交到 Git 仓库里。我见过有人把 .ssh 目录整个 commit 上去的,这等于把家门钥匙公开挂在网上。在项目根目录的 .gitignore 里加上 .ssh/ 和 *.pem 是基本操作。

6. 我个人的几条实操心得

配 SSH 这件事,说难不难,但细节特别多。我自己总结下来,最省时间的做法是:每次换新电脑,先花十分钟把密钥生成、config 文件、权限设置一次性做对,后面几个月都不用再碰。反过来,如果一开始图省事随便配,后面就会在各种奇怪的报错里反复消耗时间。

另外,遇到连接失败,第一反应不要是重装软件或者重启,而是用 ssh -vT 看日志。日志里 90% 的情况会直接告诉你问题在哪:是密钥没被提供,还是服务器拒绝了,还是网络根本没通。学会读这个输出,比记住一百条命令都有用。

最后一个习惯:给每台机器、每个用途的密钥起不同的名字,并在平台端用清晰的标题标注。半年后你回头看,能一眼认出哪把钥匙开哪扇门,而不是对着一堆 id_rsa 发呆。这个习惯在团队协作和交接时尤其值钱。