刚进实验室时,老师把服务器的用户名、IP 和初始密码发给了我,让我之后在服务器上跑代码,顺便建议我配一下 SSH Key。
登录服务器这一步倒没有卡很久。b站上搜索“VS Code 连接远程服务器”,基本上都推荐 Remote - SSH 插件。安装插件,在 VS Code 中按 Ctrl+Shift+P,选择:
Remote-SSH: Add New SSH Host
然后输入:
ssh 用户名@服务器IP
例如:
ssh yuhan@203.0.113.10
第一次连接会询问是否信任服务器,并让我输入老师给的密码。输入完成后,VS Code 会自动在服务器上安装 VS Code Server,然后就可以像打开本地文件夹一样打开服务器目录。
所以真正让我开始研究 SSH Key 的原因,并不是“不知道怎么登录服务器”,而是每次重新连接都要输入一遍密码。老师说配好 Key 以后会方便很多,我才开始看它到底是什么。
notes:文中的 IP、用户名和仓库地址都是示例,需要替换成自己的真实信息。
先把服务器登录改成 SSH Key
我一开始把 SSH Key 理解成“另一种登录密码”。实际使用时,它是一对文件:一个私钥和一个公钥。
在 Windows PowerShell 中可以这样生成一对专门用于实验室服务器的密钥:
ssh-keygen -t ed25519 -C "lab-server" -f "$env:USERPROFILE\.ssh\id_ed25519_lab"
生成后,Windows 用户目录下的 .ssh 文件夹里会出现:
id_ed25519_lab # 私钥
id_ed25519_lab.pub # 公钥
这里最需要注意的是:没有 .pub 后缀的是私钥,请妥善保存;有 .pub 后缀的是公钥,后续需要传到服务器上。
把公钥传到服务器
如果使用 Linux、macOS、WSL,或者环境里有 ssh-copy-id,上传公钥只需要一条命令:
ssh-copy-id -i ~/.ssh/id_ed25519_lab.pub yuhan@203.0.113.10
它会要求输入最后一次服务器密码,然后把公钥追加到服务器的:
~/.ssh/authorized_keys
Windows PowerShell 默认不一定有 ssh-copy-id。不想额外安装时,可以用下面这条等价命令:
Get-Content "$env:USERPROFILE\.ssh\id_ed25519_lab.pub" |
ssh yuhan@203.0.113.10 "umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys"
GitHub SSH key
服务器能登录之后,我做的下一件事就是拉项目代码。和大多数刚开始使用 GitHub 的人一样,我直接复制仓库页面上的 HTTPS 地址:
git clone https://github.com/OWNER/REPO.git
如果是公开仓库,clone 和 pull 通常不需要任何 GitHub SSH Key,所以这一步也没有让我意识到还有另一套认证。
真正麻烦的是后来需要把自己的修改推送到 GitHub。
git add 修改的文件
git commit -m "更新代码"
git push
每次 push 都要处理 GitHub 认证。终端看起来像是在要求用户名和密码,但 GitHub 的 Git 操作已经不再接受账号密码:走 HTTPS 时,需要使用 Personal Access Token,或者让 Git Credential Manager 代为保存凭据。
Token 很长,也不适合每次手动复制。我这才想起 GitHub 设置里还有一个 SSH and GPG keys,又联想到仓库页面的 Code 按钮下面一直有两个 clone 选项:HTTPS 和 SSH。以前我把它们理解成两种“复制下载地址的格式”,直到这时才发现,它们其实代表连接同一个仓库的两条路线。
- HTTPS 地址形如
https://github.com/OWNER/REPO.git。Git 通过 HTTPS 传输数据,需要认证时使用 Token 或 Credential Manager 中保存的凭据。 - SSH 地址形如
git@github.com:OWNER/REPO.git。Git 通过 SSH 传输数据,GitHub 使用账号中保存的公钥验证本机私钥的签名。
两种方式访问的是同一个仓库,提交记录、分支和文件没有任何区别;不同的是连接协议以及用什么证明身份。公开仓库允许匿名读取,所以用 HTTPS clone 时往往感觉不到认证。到了 push 需要写权限,这个区别才真正显现出来。
还有一个容易忽略的细节:git clone 不只是下载代码,它还会把当时使用的地址保存为本地仓库的 origin。以后执行 pull 或 push,Git 都会继续沿着这个地址所指定的路线连接 GitHub。配置了 SSH Key 并不会自动把已有的 HTTPS origin 改成 SSH。
服务器登录 Key 和 GitHub Key 是什么关系
从原理上说,它们都是 SSH 公钥认证,没有本质不同。区别只在于“这把公钥交给了谁”。
前面登录实验室服务器时:
我的电脑 ──SSH──> 实验室服务器
私钥留在我的电脑上,公钥放在实验室服务器的 authorized_keys 中。
现在从自己的电脑向 GitHub 推送时:
我的电脑 ──SSH──> GitHub
私钥仍然留在电脑上,但公钥需要添加到 GitHub 账号。
技术上可以把同一把公钥同时放到服务器和 GitHub,但我后来还是为 GitHub 单独生成了一把,文件名也写清楚用途。这样以后电脑丢失、密钥泄露或需要撤销权限时,不会牵连其他连接。
在 Windows 上配置 GitHub SSH Key
生成 GitHub 专用密钥:
ssh-keygen -t ed25519 -C "你的GitHub邮箱" -f "$env:USERPROFILE\.ssh\id_ed25519_github"
把公钥复制到剪贴板:
Get-Content "$env:USERPROFILE\.ssh\id_ed25519_github.pub" | Set-Clipboard
然后进入 GitHub:
头像 → Settings → SSH and GPG keys → New SSH key
给它起一个能认出设备的名字,例如 Yuhan Windows Laptop,选择 Authentication Key,粘贴公钥并保存。
因为我的 GitHub 私钥也不是默认文件名,所以在 Windows 的 ~/.ssh/config 中继续加入:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
然后测试:
ssh -T git@github.com
如果认证成功,GitHub 会显示类似“successfully authenticated”的提示,同时说明它不提供 shell access。这是正常的,因为这里只是用 SSH 完成 Git 身份认证,并不是要进入 GitHub 的服务器终端。
配完 Key,为什么 push 还是要求认证
这是我实际卡住的一步。
我已经把 SSH Key 添加到 GitHub,测试也成功了,但回到原来的项目执行 git push,它仍然走 HTTPS 认证。
原因是添加 SSH Key 不会自动修改已有仓库的远程地址。可以先检查:
git remote -v
如果看到的是:
https://github.com/OWNER/REPO.git
就需要改为 SSH 地址:
git remote set-url origin git@github.com:OWNER/REPO.git
再次执行 git remote -v,确认变成:
git@github.com:OWNER/REPO.git
这时 push 才会真正使用刚配置的 GitHub SSH Key。
回头看,这个问题其实很简单,但第一次配置时很容易把“GitHub 已经认识我的公钥”和“这个仓库正在使用 SSH 地址”当成一回事。
服务器同步配置Github SSH key
先通过 Remote SSH 登录实验室服务器,然后在服务器终端生成一把新的密钥:
ssh-keygen -t ed25519 -C "lab-server-github" -f ~/.ssh/id_ed25519_github
查看服务器生成的公钥:
cat ~/.ssh/id_ed25519_github.pub
再把这行公钥添加到 GitHub。
最简单的方式是添加到个人账号的 SSH and GPG keys。如果服务器只需要访问某一个仓库,也可以在仓库的 Settings → Deploy keys 中添加,这样权限范围会更小。一般只需要拉代码时保留只读权限即可。
服务器的 ~/.ssh/config 可以写成:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
测试:
ssh -T git@github.com
之后再使用 SSH 地址:
git clone git@github.com:OWNER/REPO.git
总结
逻辑一直是同一套:
谁发起 SSH 连接,私钥就留在谁那里;
谁负责验证身份,公钥就交给谁。
对应到这次使用服务器的过程就是:
| 操作 | 私钥在哪里 | 公钥交给谁 |
|---|---|---|
| Windows 登录实验室服务器 | Windows | 实验室服务器 |
| Windows 向 GitHub push | Windows | GitHub 账号 |
| 实验室服务器访问 GitHub | 实验室服务器 | GitHub 账号或目标仓库 |