1. 先别急着改配置:给问题分个层
1.1 密钥连接失败到底错在哪一步
先说个我自己的实际经历。有一台刚开好的云服务器,我在本地终端里用 ssh root@<服务器IP> 一条命令就能直接登上去,root 的 /root/.ssh/authorized_keys 里也确认过公钥存在。结果回到 VSCode 里,Remote-SSH 连接,界面卡一下之后弹出一个密码输入框。第一次遇到这种情况,人的第一反应往往是"是不是密钥文件权限不对""是不是 VSCode 不支持密钥",然后开始盲目改权限、换密钥格式,折腾一下午发现没用。
后来我把整个链路在脑子里过了一遍,才意识到问题不在"密钥是否有效",而在"VSCode 通过哪种方式、在哪个目录、用哪把钥匙去连接服务器"。密钥认证的完整过程其实是这样的:
- 客户端发起 TCP 连接,和 SSH 服务端协商加密算法;
- 服务端返回自己的 host key,完成主机身份校验;
- 客户端开始认证阶段,向服务端提供"可用公钥列表";
- 服务端拿这些公钥和当前用户的
authorized_keys比对,命中后返回挑战; - 客户端用对应私钥完成签名,服务端验证通过,会话建立。
VSCode 的 Remote-SSH 扩展不是一个独立的 SSH 客户端,它在 Windows 上调用的是系统自带的 OpenSSH(或者你手动指定的 ssh.exe),在 Linux/macOS 上调用系统的 ssh。所以第 3 步里的"可用公钥列表"从哪里来、包含哪些密钥、走的是哪个用户的 HOME 目录,直接影响认证结果。很多时候你以为 VSCode 和终端用的是同一把钥匙,其实它俩读的根本不是同一份配置文件。
1.2 命令行能连、VSCode 连不上,说明了什么
这是一个特别重要的判断信号。如果本地终端里执行:
bash复制ssh -i ~/.ssh/id_ed25519 user@192.168.1.100
能够正常登录,至少能证明以下几件事:
- 服务器端的 SSH 服务是正常的;
- 密钥对本身是有效的(公钥在服务器上、私钥在本地);
- 服务器没有限制该用户登录,SSH 端口也可达;
- 认证方式里公钥认证是开启的。
那问题范围就缩小到了 VSCode 这一侧:要么它读的 ~/.ssh/config 不是你以为的那个文件,要么它用了错误的 IdentityFile 路径,要么它连接的 Host 定义和你命令行里用的不是同一个。反过来还有一种很常见的情况:命令行用 ssh -v 也会卡住,服务器日志里直接出现 Failed publickey for user,那问题基本就锁定在服务器端或者密钥本身。
所以我的第一个建议永远都是:先做减法,再定位问题。 不要让 VSCode 这个外壳干扰你,先在裸 SSH 层面上验证密钥是否真的可用。
1.3 通过错误信息快速归类
VSCode 连接失败时,有的错误在弹窗里显示,有的藏在输出日志里,还有的只在终端模式下才看得见。根据我踩过的坑,下面这些提示基本能帮你快速归档:
| 错误提示(或日志关键词) | 问题大概率在哪个环节 | 优先检查方向 |
|---|---|---|
Permission denied (publickey,password) |
认证阶段 | 服务器 authorized_keys、密钥路径、服务端日志 |
No supported authentication methods available |
认证方式协商 | sshd_config 中 PubkeyAuthentication / PasswordAuthentication |
Remote host identification has changed |
host key 校验 | known_hosts 中旧的主机指纹 |
Failed to establish a socket connection |
网络层 | 端口、防火墙、服务器的 sshd 是否启动 |
Server installation failed 或 Resolving remote environment 卡住 |
远端执行层 | vscode-server 安装失败、远程用户是否有写权限 |
Connection closed by remote host |
会话层 | sshd 日志、内存、PAM 配置、Too Many Authentication Failures |
其中和"使用秘钥无法连接"最相关的是前两行,尤其是 Permission denied (publickey,password)。这个提示的意思是:服务器允许公钥认证也允许密码认证,但在本次连接尝试里,你的客户端提供的公钥没有被服务器接受,同时又拒绝了密码输入弹窗,所以协商多次后两边都过不去。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 命令行与日志配合的排查链路
2.1 先用 ssh -vvv 验证认证全流程
无论你觉得服务器端多么正常,排查密钥问题最可靠的方法是开 verbose 日志跑一次完整连接。在本地终端里执行:
bash复制ssh -vvv user@192.168.1.100
注意区分 -v 和 -vvv,排查密钥时直接用 -vvv 最省事。输出会非常多,但你只需要盯几个关键行:
code复制debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxx
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug1: Server accepts key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxx
如果认证成功了,你会看到 Server accepts key;如果失败,则可能看到:
code复制debug1: Authentications that can continue: publickey,password
debug1: Trying private key: /home/user/.ssh/id_rsa
debug1: Trying private key: /home/user/.ssh/id_ed25519
debug1: No more authentication methods to try.
user@192.168.1.100: Permission denied (publickey,password).
看到 Trying private key 表示客户端手里有密钥,但服务端不认;如果连 Trying private key 都没有,说明客户端压根没找到对应的私钥文件,常见原因是 HOME 目录不对、IdentityFile 路径写错、文件名不匹配。用 ssh -vvv 就能把这两类问题彻底分开,省得瞎猜。
2.2 VSCode 的 Remote-SSH 日志怎么看
VSCode 的 Remote-SSH 扩展运行时会输出一套比终端更啰嗦的日志。在 VSCode 里按 F1 或 Ctrl+Shift+P,输入 Remote-SSH: Show Log,会打开输出面板。这里面的日志分两部分:本地 SSH 客户端产生的日志,以及远程服务器上 vscode-server 的初始化日志。
与密钥问题直接相关的是本地 SSH 客户端部分。你会在里面看到它到底调用了哪个 ssh、加载了哪个配置、尝试了哪个 IdentityFile。有一次我遇到的场景就是:VSCode 实际使用的用户目录是 C:\Windows\System32\config\systemprofile\.ssh(因为当时 VSCode 是以服务方式启动的),而我的密钥明明放在 C:\Users\admin\.ssh 下。如果不看日志,光在配置文件里改来改去,永远也找不到原因。
如果你不想翻输出面板,也可以直接在当前用户目录下看:
text复制C:\Users\<你的用户名>\.ssh\config
~/.ssh/config
VSCode 默认会读取这个位置的 config。你可以用 Remote-SSH: Open SSH Configuration File... 命令直接打开,确认它指向的文件路径是否和预期一致。这个命令也会明确告诉你 VSCode 现在到底在编辑哪个文件,很多"我在 config 里改了没用"的问题,其实就是因为改了另一个用户的 config。
2.3 服务器端日志里的关键线索
服务器端日志是定位"服务端到底为什么拒绝"的唯一可信来源。Debian/Ubuntu 上看 /var/log/auth.log,CentOS/RHEL 系看 /var/log/secure。用下面的命令实时观察:
bash复制# Debian/Ubuntu
sudo tail -f /var/log/auth.log
# CentOS/RHEL
sudo tail -f /var/log/secure
当你在本地再次触发一次失败连接时,日志里会出现类似这样的记录:
code复制Failed publickey for root from 203.0.113.10 port 51234 ssh2: ECDSA SHA256:xxxx
connection closed by authenticating user root 203.0.113.10 port 51234 [preauth]
Failed publickey 是核心线索。这说明服务端在认证时确实收到了你的公钥,但拒绝接受。常见的拒绝原因有:公钥不在 authorized_keys 里、authorized_keys 文件的属主或权限不对、家目录权限太宽松、SELinux 上下文异常等。如果日志里根本没有 Failed publickey,而是 Connection closed by authenticating user,那通常意味着认证还没走到公钥比对这一步就被 PAM 或其他策略拦截了。
还有一个容易被忽略的点:如果客户端用了多个密钥,OpenSSH 默认最多尝试 6 个密钥,超过之后服务端直接断开,日志里显示 Too many authentication failures。这在 VSCode 里尤其常见,因为你可能不自觉地给同一个 Host 配置了好几个 IdentityFile,或者 ssh-agent 里有大量缓存密钥。
3. 服务器端的三座大山:权限、sshd_config、SELinux
3.1 OpenSSH 的目录"洁癖":权限不对真的会拒绝
这是新手最容易翻车的地方。OpenSSH 对密钥相关文件的权限要求非常严格,而且它遵循一套"只要目录或文件权限过于开放,就宁可拒绝认证也不冒险"的安全策略。
在服务器上检查:
bash复制# 查看家目录权限
ls -ld /home/user
ls -ld /root
# 查看 .ssh 目录及内部文件
ls -la ~/.ssh
常见的权限要求如下:
| 路径 | 推荐权限 | 原因 |
|---|---|---|
| 用户家目录 | 755 或 700 | 不能 group/world 可写 |
~/.ssh 目录 |
700 | 只有本人能进入 |
~/.ssh/authorized_keys |
600 | 只有本人能读 |
~/.ssh/id_rsa(私钥) |
600 | 私钥必须保密 |
~/.ssh/id_rsa.pub(公钥) |
644 | 公钥可以公开 |
如果权限不对,修正后立刻生效,不需要重启 sshd:
bash复制chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
还要检查 .ssh 目录的属主是否对得上:
bash复制chown -R user:user /home/user/.ssh
之所以要单独说一遍,是因为很多 VSCode 密钥连接失败,服务器端日志里 Failed publickey 出现的原因不是公钥不存在,而是 authorized_keys 权限为 644 甚至 777,OpenSSH 直接拒绝读取。这种问题在命令行里用 ssh 也可能直接报错,但有些旧的客户端行为不一致,你换了 VSCode 才发现连不上。
3.2 sshd_config 里的认证开关
服务器的 /etc/ssh/sshd_config 中有几个参数直接决定密钥认证是否可用的。用下面的命令确认:
bash复制sudo grep -E "^PubkeyAuthentication|^PasswordAuthentication|^AuthorizedKeysFile|^PermitRootLogin" /etc/ssh/sshd_config
需要关注的值:
PubkeyAuthentication yes:必须开启,否则任何公钥认证都会失败;PasswordAuthentication no:如果强制只用密钥,这里可以设为 no,但前提是你在把公钥加进去之前不要先把它改成 no,否则你连密码登录的机会都没有;AuthorizedKeysFile .ssh/authorized_keys:默认路径,一般不需要改;PermitRootLogin:如果直接连 root,需要保证不是no。通常云服务器默认是prohibit-password,意思是禁止 root 用密码登录,但允许密钥登录,这种状态下 VSCode 用密钥连 root 是可以的。
修改配置后一定要重启 sshd 才生效:
bash复制sudo systemctl restart sshd
# 或
sudo service ssh restart
重启前最好在另一个终端里保留一个已建立的连接,防止配置改错后自己把自己锁在服务器外面。
3.3 SELinux 上下文:一个让你怀疑人生的坑
CentOS/RHEL 系的服务器上有一个很容易被忽略的因素:SELinux 为 ~/.ssh/authorized_keys 设置了特定的安全上下文。如果你把密钥文件从别处拷贝过来,或者用编辑器覆盖保存过,SELinux 上下文可能会变成 unconfined_u:object_r:user_home_t,而不是 sshd 需要的 etc_t 或 sshd_key_t 一类上下文,sshd 在验证时会拒绝读取。
排查方法:
bash复制ls -Z ~/.ssh/authorized_keys
ls -Z ~/.ssh
如果上下文不对,修复:
bash复制sudo restorecon -R -v ~/.ssh
这条命令会恢复 .ssh 目录及文件的默认 SELinux 上下文。修复后再试一次 VSCode 连接,很多莫名其妙的问题就这么消失了。需要说明的是,如果你用的是 Ubuntu/Debian 服务器,一般默认不开 SELinux,可以跳过这步;但如果你没把握,还是执行一下 getenforce 确认当前状态。
4. 客户端与 VSCode 侧的隐形坑
4.1 Windows 用户目录不一致:VSCode 找错了密钥
这是 Windows 上最高频的问题。VSCode 在 Windows 上调用 OpenSSH 时,会读取某个用户主目录下的 .ssh 文件夹。但"某个用户主目录"不一定是你在文件中看到的那个。以下几种情况都会导致路径错位:
- VSCode 以管理员身份运行,
$HOME可能指向C:\Users\Administrator; - VSCode 通过某些远程启动脚本或服务方式运行时,HOME 可能指向系统账户路径;
- 如果你在 Windows 上安装了多个版本的 OpenSSH 或使用了 Git Bash,不同的 ssh 客户端可能使用不同的 HOME 定义。
在 VSCode 的终端里执行:
bash复制echo $HOME
看一下实际输出路径和你存放密钥的路径是否一致。要避免这个问题,最稳妥的办法不是改系统 HOME 环境变量,而是在 SSH config 的 Host 块里写死 IdentityFile 的绝对路径,同时用 IdentitiesOnly yes 告诉客户端只用指定的密钥,不要额外去 ~/.ssh 里找其他钥匙。
4.2 密钥格式:ppk 与 OpenSSH 的兼容问题
很多开发者在 Windows 上使用 PuTTY 生成密钥,得到的是 .ppk 格式,PuTTY 自己能识别,但 VSCode 调用的 OpenSSH 客户端不认。如果你在连接时看到类似 Unable to load key 的提示,说明密钥格式不匹配。
解决方法有两种。第一种,在 PuTTY Key Generator 里加载 .ppk 文件,然后通过 Conversions 菜单导出 OpenSSH 格式的私钥;第二种,用命令行转换:
bash复制puttygen mykey.ppk -O private-openssh -o id_rsa
puttygen mykey.ppk -O public-openssh -o id_rsa.pub
转换完成后再把公钥重新追加到服务器的 authorized_keys 里。这类问题在命令行连接时也一样会被拦截,但如果你之前一直用 PuTTY 而没用过原生 ssh,就会感觉"VSCode 连不上,但 PuTTY 明明可以"——本质原因是 VSCode 使用的 SSH 客户端和 PuTTY 不是同一种。
4.3 ssh-agent 服务与 IdentitiesOnly:多密钥互相打架
还有一个非常隐蔽的问题:ssh-agent 缓存了多把密钥,OpenSSH 在认证时会逐个尝试这些密钥。如果你的 config 里没有 IdentitiesOnly yes,即使你写明了 IdentityFile,客户端也可能先去 agent 里尝试所有密钥。当 agent 里有一把密钥曾经被服务器拒绝过(比如公钥已经从服务器上删除了),会消耗一次认证机会,导致后边的正确密钥还没轮到就被服务器以"过多失败次数"断开。
Windows 上检查 ssh-agent 服务是否在运行:
powershell复制Get-Service ssh-agent
如果未运行,可以设置开机自启并启动:
powershell复制Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
更关键的是在 config 里加一行:
code复制Host myserver
...
IdentitiesOnly yes
IdentitiesOnly yes 的意思是:连接这台主机时,只用 IdentityFile 指定的密钥,不向 agent 请求其他任何密钥。这能极大减少认证被干扰的概率。
5. 可复现的完整修复流程与 VSCode 配置模板
5.1 服务器端修复步骤清单
如果你的问题同时伴随 Failed publickey 日志,按顺序执行这些步骤,每完成一步就去 VSCode 试一次,不要一口气全做完再试,否则你根本不知道是哪一步起效的。
- 确认服务器上用户家目录、
.ssh、authorized_keys的权限符合要求; - 确认
authorized_keys里确实有对应当前客户端私钥的公钥,且公钥内容没有被意外换行; - 确认 sshd_config 中 PubkeyAuthentication 为 yes,然后重启 sshd;
- 如果是 RHEL/CentOS 系,执行
getenforce检查 SELinux 状态,必要时restorecon -R -v ~/.ssh; - 查看
/var/log/auth.log或/var/log/secure确认新的尝试是否从Failed publickey变成Accepted publickey。
如果再试一次看日志:
code复制Accepted publickey for user from 203.0.113.10 port 50001 ssh2: ED25519 SHA256:xxxx
说明服务器端已经接受你的公钥,问题已经解决,剩下就是 VSCode 客户端连接的事情了。
5.2 客户端修复步骤清单
客户端侧的顺序建议是:
- 先确保裸 SSH 能用密钥登录目标服务器;
- 确认 VSCode 的
Remote-SSH: Show Log里用的 ssh 路径和用户目录符合预期; - 编辑
~/.ssh/config,写清楚 Host、HostName、User、Port、IdentityFile、IdentitiesOnly; - 如果是在 Windows 上,检查 ssh-agent 服务状态;
- 如果密钥是
.ppk,转换为 OpenSSH 格式再使用; - 如果使用多把密钥,为每台服务器单独配置 IdentityFile 并开启 IdentitiesOnly。
一个标准的 config 模板如下:
code复制Host my-server
HostName 192.168.1.100
User root
Port 22
IdentityFile C:\Users\admin\.ssh\id_ed25519
IdentitiesOnly yes
StrictHostKeyChecking no
ServerAliveInterval 30
说明一下 ServerAliveInterval 30:这是让客户端每 30 秒发送一个心跳包,防止长时间空闲后连接被路由器或防火墙断开。配合 VSCode 的 Remote-SSH 反而很实用,因为你在编辑器里打开一个文件挂一个晚上,第二天回来大概率还连着。
5.3 修复后怎么验证
配置改完后,如果 VSCode 还是报错,先不要反复点重试。把 VSCode 完全退出重开一次,然后再看 Remote-SSH: Show Log。因为 Remote-SSH 扩展在同一个窗口里会缓存一些连接状态,不重开的话可能还在用旧的会话上下文。
还可以在 VSCode 的集成终端里手动执行:
bash复制ssh my-server
这里的 my-server 是 config 里的 Host 名称。如果这一步能通,说明配置本身没问题,问题出在 VSCode 扩展与 ssh 的对接上;如果连这个也不能通,说明 config 内容、密钥路径或服务器端还有问题。通过这样一个"隔离变量"的验证方式,基本能在五分钟内判断出该改哪一边。
6. 连接成功之后:让密钥连接更好用的小经验
6.1 多台服务器统一管理:一个 config 文件搞定
当你有云服务器、公司内网服务器、客户现场机器好几台设备时,建议把所有连接配置统一维护在 ~/.ssh/config 里。不要每次连接都手动指定端口、用户名、密钥文件,那样既容易出错,也无法发挥 VSCode Remote-SSH 的"记住主机"特性。
我的个人习惯是这样组织的:
code复制Host dev-server
HostName dev.example.com
User developer
Port 22
IdentityFile ~/.ssh/id_ed25519_dev
IdentitiesOnly yes
Host prod-server
HostName 203.0.113.5
User root
Port 22
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
在 VSCode 里按 F1 输入 Remote-SSH: Connect to Host,它会列出 config 里定义的所有 Host,选一下就能连。这样比每次手敲 IP 地址舒服得多,也方便按环境隔离密钥。
6.2 公钥批量部署与更新
VSCode 的 Remote-SSH 连接完成后,会在远程用户目录下创建 ~/.vscode-server 文件夹,用来安装语言服务、终端集成等依赖。第一次连接如果比较慢,大概率是在下载和安装这个运行时。如果遇到 Failed to install the VS Code server 之类的错误,首先检查远程用户是否有权限写入 ~ 目录,其次考虑是不是网络下载受限,必要时手动在服务器上设置 HTTP 代理,但这已经超出密钥认证的范畴了,我建议遇到时先单独排查,不要和密钥问题混在一起处理。
另外,公钥更新在团队场景里是常事。如果你把某台服务器的公钥从一个客户端迁移到另一个客户端,最稳妥的方式是重新生成密钥对并把新公钥添加到服务器,而不是在旧机器上拷贝私钥。私钥在多个设备之间复制扩散,泄露面太大,一旦哪台终端被攻破,所有服务器都会暴露。如果确实需要让多台设备共用同一个密钥,至少给私钥设置 passphrase 并通过 ssh-agent 暂时缓存,而不要让裸私钥散落在磁盘上。
6.3 密钥安全与备份:连得顺也要连得稳
最后说一点日常维护经验。密钥连接有一个特点:一旦服务器端 authorized_keys 里的公钥被误删,或者本地私钥文件损坏,你将失去所有访问通道。所以在配置完密钥认证后,强烈建议做两件事:
- 在另一个安全的位置备份私钥(比如密码管理器),并记录公钥指纹;
- 保留至少一把备用密钥,放在不同的终端上,防止手上这台电脑突然坏了连登录都进不去。
我见过不少开发者在 VSCode 密钥连接失败后着急忙慌地改 sshd_config、改权限、重装 VSCode,最后连密码登录都被临时关掉,整个人卡在服务器外面。遇到这种情况,先停一下,用 ssh -vvv 和服务器日志一步步确认,比什么都管用。排查密钥问题本身不复杂,复杂的是被各种表象干扰,没有把问题控制在正确的范围内。
