ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSH私钥登录实战:从密钥生成到VSCode Remote-SSH无缝连接

SSH私钥登录实战:从密钥生成到VSCode Remote-SSH无缝连接 你有多久没有输过 SSH 密码了如果还在用账号密码登服务器每次输入超长口令、担心密码泄露、碰到堡垒机要求定期改密那这篇文章就是给你准备的。SSH 私钥登录不是什么新概念但真正做到“配一次、用一年、多台服务器都能无缝切换”的人并不多尤其是在 Windows 上折腾过C:\Users\用户名\.ssh\config权限问题的朋友应该都体会过那种“明明配置写对了但 VSCode 连不上、Git 又认证失败”的绝望。这篇记录我从零搭建 SSH 私钥登录方案、并且让 VSCode Remote-SSH 完全吃这套配置的完整过程覆盖密钥算法选择、公钥部署、Windows 权限坑位、VSCode 连接配置再到多主机管理和 Git 复用适合刚接触 SSH 的开发者也适合被各种连接报错折磨过、想彻底理顺方案的运维和前端同事。1. 私钥登录背后的协议逻辑为什么服务器只认“钥匙”不认“口令”一开始我也不太理解既然服务器上存的是公钥那这个公钥不是谁都能看到吗为什么反倒比密码更安全后来啃了几篇文档、自己抓包对比了几次连接日志才彻底想明白。密码登录时客户端把密码发给服务器服务器比对哈希后放行。这里的问题在于密码本身就是“唯一凭证”它出现在网络传输里、出现在服务器日志的误录里、出现在你同事的聊天截图里。一旦密码泄露只要不改所有人都能进服务器。而私钥登录走的是非对称加密的逻辑服务器保存的是公钥私钥永远留在你自己的电脑上网络传输过程中只有签名和挑战值没有私钥本身也没有密码本身。换句直白的话说别人看到你的公钥也推导不出私钥别人截获了通信内容也拿不到你的私钥。1.1 公钥和私钥的“锁与钥匙”模型我最喜欢用小区单元门的例子来解释这套机制。服务器配一把“公锁”authorized_keys 里那串字符这把锁可以公开贴在门口任何人都能看见。你手里那把开锁的“私钥匙”只有你有正常情况下不会交出去。你进门时门锁会出一道题你的私钥匙能算出正确答案门就开了。这里的关键点是锁是公开的但锁本身不包含钥匙的信息就算你把锁拆下来研究半天也配不出那把钥匙。对应到 SSH 里服务器端只需要验证“你确实持有那把私钥”验证通过就放人这个验证过程就是签名与挑战。1.2 为什么服务器端要严格校验 .ssh 目录权限很多新手看不懂服务器日志里的Permissions 0777 for /home/user/.ssh/authorized_keys are too open然后直接chmod 777想“放开权限”结果更连不上。这里要理解 sshd 的安全逻辑如果 authorized_keys 文件、甚至.ssh目录本身对组和其他用户可写那么任何能写入该目录的用户都可以把自己的公钥追加进去等于篡改了服务器“认人”的名单。所以 sshd 在认证前会主动检查密钥相关文件的权限只要发现过宽就拒绝使用密钥认证宁可让你连不上也不让认证链路被人动手脚。正规要求是~目录不能是组可写或全局可写.ssh目录必须是 700authorized_keys 必须是 600。这个细节在后面排查“配置正确但就是连不上”时十次有八次是它的问题。1.3 算法选择该用 RSA 还是 ED25519我早期一直用 RSA 4096因为在老系统上兼容性最好。但后来发现 ED25519 更香密钥短、生成快、安全性在当前主流场景下足够OpenSSH 6.5 以上就支持了。如果手头服务器大多是 CentOS 7 以下的老系统可能需要保留 RSA 作为兼容密钥否则新环境一律建议 ED25519。我自己现在的做法是个人电脑与云服务器之间用 ED25519如果遇到只支持 RSA 的老设备或堡垒机再单独生成一把 RSA 4096 密钥备用。两把密钥互不干扰用~/.ssh/config按照 Host 区分就好。给一个直观对比项目RSA 4096ED25519密钥长度4096 位256 位生成速度较慢极快性能开销签名/验证稍重轻量适合频繁连接兼容性极好老设备首选OpenSSH 6.5现代系统无压力推荐场景旧服务器、特殊设备默认首选2. 密钥对的生成与公钥投放一条命令搞定从本地到服务器生成密钥本身没什么技术含量但很多人不知道参数怎么填、公钥怎么安全地送过去、在 Windows 上到底该用 PowerShell 还是 Git Bash。这块我直接把每一步拆开附上踩过坑之后的习惯操作。2.1 Windows 下生成密钥对的关键参数如果你用的是 Windows 10 1809 之后的系统PowerShell 里直接有 OpenSSH 客户端不需要额外安装第三方工具。生成命令ssh-keygen -t ed25519 -C opsexample.com -f $env:USERPROFILE\.ssh\id_ed25519-t ed25519指定算法新版默认也已切换到 ED25519但显式指定更稳妥。-C注释一般写“谁在用这把钥匙”方便在服务器上维护多把公钥时识别归属。-f指定生成的私钥路径。如果不指定默认会写到~/.ssh/id_ed25519Windows 下就是C:\Users\你的用户名\.ssh\。执行后会提示设置 passphrase也就是私钥的使用口令。这一步我强烈建议不要留空。私钥文件本身是静态的如果电脑被拿走、文件被拷走没有 passphrase 的话等于白送加了 passphrase即使文件泄露攻击者也很难使用这把私钥。代价是每次连接时可能要输入一次 passphrase但后面我会讲用 ssh-agent 免掉这个重复操作。如果不想在命令里写死用户名也可以这样ssh-keygen -t ed25519 -C $env:USERNAME$env:COMPUTERNAME2.2 公钥投放到服务器的两种姿势第一种服务器上开了ssh-copy-id的话一条命令完事ssh-copy-id -i $env:USERPROFILE\.ssh\id_ed25519.pub root192.168.1.100第二种服务器上没有ssh-copy-id很多精简系统默认不装手动追加公钥cat $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root192.168.1.100 mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys这条命令做了什么先在远端创建.ssh目录并设置 700 权限然后把本地公钥内容追加到 authorized_keys 末尾并设置 600 权限。为什么必须用而不是因为会覆盖文件如果服务器上已经有其他公钥你这一下就把别人锁在门外了。另外注意第一次执行ssh root...时会提示输入密码那一次是最后需要输入密码的时刻公钥到位之后再连接就不需要密码了。2.3 多台服务器批量部署公钥如果你有十几台服务器要管理一台台输密码不现实。我当时的做法是写一个简单的 PowerShell 循环$servers (192.168.1.10, 192.168.1.11, 192.168.1.12) foreach ($s in $servers) { ssh-copy-id -i $env:USERPROFILE\.ssh\id_ed25519.pub root$s }每台机器会依次提示输入密码输入一次就永久生效。后续再也不用输密码。你要是更懒一点配合plink或者某些批量运维工具也可以做免交互但那些配置复杂容易把自己绕晕普通场景这个循环够用了。3. Windows 下最真实的配置坑位权限、路径与 bad owner or permissionsWindows 上用 SSH 的痛点比 Linux 多得多而且大多数报错都很不直观。最出名的就是这个Bad owner or permissions on C:\Users\thinkpad\.ssh\config。我当年第一次在 VSCode 里连远程服务器命令行明明能连上VSCode 却报这个错搜了半天才知道是 Windows 文件权限模型和 Linux 完全不一样导致的。3.1 为什么 Windows 上会出现“权限过宽”Linux 下的文件权限是 755、600 这种八进制数字一眼就能看出来。Windows 的 ACL 是一套完全不同的体系默认情况下.ssh目录或 config 文件往往继承了 C 盘或用户目录的大量权限条目比如SYSTEM、Administrators、Users组都有访问权。OpenSSH 客户端在 Windows 上检测权限时看到“除了当前用户之外还有别的账户能改这个文件”就会套用 Linux 的安全逻辑认为权限太开放于是拒绝读取。你哪怕把文件设成只读、把用户改成完全控制不改继承关系照样报错。3.2 修复 config 和私钥权限的标准步骤修复方式有两种我推荐记住第二种命令行的因为能直接写进文档之后谁也坑不到你。方式一图形界面。右键C:\Users\thinkpad\.ssh\config选“属性”。切到“安全”标签点“高级”。底部“禁用继承”选择“将已继承的权限转换为显式权限”。在权限条目里把除当前用户以外的SYSTEM、Administrators、Users等全部删除只保留当前用户的“完全控制”。一路确定退出。方式二命令行。icacls C:\Users\thinkpad\.ssh\config /inheritance:r icacls C:\Users\thinkpad\.ssh\config /grant:r $env:USERNAME:F第一条命令把继承权限全部移除第二条命令只给当前用户授予完全控制权。注意config文件要做一遍私钥文件id_ed25519也要做一遍。我自己测试过有时报错指向 config但真正的权限问题出在私钥文件上改了私钥的 ACL 才恢复连接。3.3 Git Bash、PowerShell 和 VSCode 读的 config 不是同一个还有一个隐蔽的坑Windows 上的 OpenSSH 默认是从$env:USERPROFILE\.ssh\config读取配置也就是C:\Users\用户名\.ssh\config。但如果你装了 Git for Windows它自带的 OpenSSH 使用的是同一套路径一般没冲突。真正的问题出在VSCode 的 Remote-SSH 默认调用的是系统 OpenSSH 客户端也就是C:\Windows\System32\OpenSSH\ssh.exe如果你在系统环境变量之前装了别的 SSH 工具并且改了 PATHVSCode 可能连的是“另一套 ssh”读的配置路径也可能不一样。所以当你发现“命令行能连VSCode 不能连”时第一步就去 VSCode 输出面板里看日志开头那行使用的ssh.exe路径。我自己后来直接固定做到两件事第一~/.ssh目录下只维护一份 config不做多份拷贝第二在系统 PATH 里把C:\Windows\System32\OpenSSH提到最前面。做完之后 VSCode、PowerShell、Git 三方读取的配置完全一致之后基本没再遇过“拆家式”的路径混乱。3.4 只读属性不是权限别被误导网上不少旧教程会让你把.ssh目录或 config 文件“去掉只读”这在 Windows 上经常无效因为 Windows 的“只读”属性和 Linux 的写权限不是一回事。OpenSSH 检查的是 ACL不是只读标志。你手动取消了只读日志里可能还是报同样的权限错误。遇到这种情况别纠结直接按上面的 icacls 处理才是正路。4. VSCode 的 Remote-SSH 就是从这份配置读答案私钥登录本身在终端里跑通了接下来就是让 VSCode 也吃这套方案。Remote-SSH 这个插件本质上就是在本地用 SSH 连接到远程主机后在远端部署一个vscode-server服务然后由它承载你的编辑、调试、终端。所以它能不能连上完全取决于它按什么配置发起 SSH 连接。4.1 插件与基础连接在 VSCode 扩展市场搜Remote - SSH装好之后左侧会出现一个远程资源管理器图标。按F1或者CtrlShiftP输入Remote-SSH: Connect to Host它会读取 SSH config 里的 Host 列表。所以你在 config 里怎么定义主机这里就显示什么。如果这里直接找不到你的服务器大概率是 config 路径没读对回到第 3 章去查。我的 config 文件长这样Host web-prod HostName 120.24.88.10 User root Port 22 IdentityFile C:\Users\thinkpad\.ssh\id_ed25519 Host jump-01 HostName 10.0.8.5 User ops ProxyJump bastion IdentityFile C:\Users\thinkpad\.ssh\id_ed25519Host后面那个名字是你自己起的别名HostName才是真实 IP 或域名。VSCode 里连接时选别名连上之后窗口左下角会显示主机名。IdentityFile指定私钥路径Windows 下建议直接写完整路径不要写~/.ssh/id_ed25519因为不同终端对~的展开结果可能不一致在 Git Bash 下是C:\Users\thinkpad在 PowerShell 下也是但万一你切换了默认终端工具可能就不对了。完整路径一劳永逸。4.2 首次连接的 vscode-server 下载问题第一次通过 Remote-SSH 连接时VSCode 会自动在远端下载并解压vscode-server-linux-x64到~/.vscode-server。这个过程依赖远端能访问 VSCode 的下载域名。如果你用的是国内云主机经常会出现一直卡在“正在下载”或者进度条半天不动的情况。此时有两个办法一是配置代理让远端通过代理访问下载地址但很多生产环境没有现成代理。二是手动下载后传到服务器。在本地浏览器访问 VSCode 对应的下载地址拿到vscode-server-linux-x64.tar.gz然后scp vscode-server-linux-x64.tar.gz root120.24.88.10:/tmp/ ssh root120.24.88.10 tar -xzf /tmp/vscode-server-linux-x64.tar.gz -C ~/.vscode-server --strip-components1版本号必须与本机 VSCode 的 commit 完全匹配可以在 VSCode 的关于页面里找到 Commit ID。以前我手动装过一次版本不匹配远程还是不断重新下载折腾了半天才发现问题。后来我都是先在本地命令行用curl把下载地址拼出来确认版本一致再传上去。4.3 VSCode 连接日志到底怎么读连接失败时VSCode 会弹通知提示查看日志。很多人点开日志一脸懵全是debug1、debug2开头的行。其实最该看的是下面几类Trying private key: C:\Users\thinkpad\.ssh\id_ed25519确认它认到了私钥。Authenticated to 120.24.88.10 ([120.24.88.10]:22)出现这行说明认证已经成功。Permission denied (publickey,password)说明认证失败且服务器可用的认证方式里没有能匹配上的私钥。Offending key或者Bad owner or permissions明确指向权限问题回第 3 章处理。日志里还能看到 VSCode 使用的ssh.exe路径和 config 文件路径这两项对排查“为什么 VSCode 和我本地终端配置不一致”很有价值。4.4 一个小技巧为远程主机单独指定别名我强烈建议在 config 里给每台服务器起一个清晰别名而不是直接用 IP 当 Host。比如web-prod、db-slave-01、my-workstation。这样 VSCode 的资源管理器列表里显示的是好记的名字更重要的是你可以给同一台服务器配置多个不同的“入口”比如走代理的入口和直连的入口别名区分开切换方便不会污染真实主机名。5. 从“能连上”到“顺手用”多主机、ssh-agent 与 Git 复用如果你只是为了连一两台服务器前面四章已经够用。但实际工作中我们和 SSH 打交道的场景远不止“登录服务器”这一个动作。Git 提交要 SSH、跳板机要 SSH、甚至自动化脚本也要 SSH。所以最后聊几个进阶用法这些都围绕同一套私钥体系展开。5.1 用 ssh-agent 省去重复输入 passphrase私钥加了 passphrase 之后每次连接都要敲一遍很影响体验。解决办法是把私钥加入 ssh-agent# 启动 ssh-agent 服务Windows 一次性配置 Get-Service ssh-agent | Set-Service -StartupType Manual Start-Service ssh-agent # 添加私钥 ssh-add $env:USERPROFILE\.ssh\id_ed25519添加时会提示输入一次 passphrase之后只要 agent 在运行同一把私钥的后续连接都不需要再输入。VSCode 也会复用系统 ssh-agent 的能力所以你在终端加好之后重启 VSCode 再连接基本不再问 passphrase。Windows 的 OpenSSH 默认会把 agent 注册为手动启动服务不用时不会占资源要用时手动拉起就可以。5.2 跳板机配置ProxyJump 解决“必须从堡垒机跳转”很多公司环境不允许直连内网服务器必须先登录跳板机再跳到目标机器。以前大家喜欢用ProxyCommand加nc配置绕且容易踩引号转义。OpenSSH 7.3 以后有原生ProxyJump相当于直接告诉 ssh先连跳板机再从跳板机连目标机。配置文件里这样写Host bastion HostName 121.40.10.10 User ops IdentityFile C:\Users\thinkpad\.ssh\id_ed25519 Host internal-db HostName 172.16.1.50 User root ProxyJump bastion IdentityFile C:\Users\thinkpad\.ssh\id_ed25519这样ssh internal-db就会自动先连跳板机再通过它建立到目标机的通道。VSCode 同样支持 ProxyJump只要配置写对它也能直接连内网目标。省去手动登跳板机再敲 ssh 的繁琐过程。代理链路里的两把私钥可以相同也可以不同按场景各写各的 IdentityFile 即可。5.3 让 Git 复用同一套私钥体系很多需要 push 代码到 GitHub、GitLab 的场景其实和服务器登录是一套公钥机制。Git 走 SSH 协议时用的是同一套~/.ssh认证流程。如果你有多把私钥可以在 config 里专门给 Git 平台指定一把Host github.com HostName github.com User git IdentityFile C:\Users\thinkpad\.ssh\id_ed25519_github这里要注意GitHub 固定要求 user 是git不写也没关系但写上更明确。然后测试ssh -T gitgithub.com看到Hi 用户名! Youve successfully authenticated就说明 Git 公钥配置对了。这个配置和服务器登录共用一份 config路径管理不会乱而且 VSCode 源码管理里推送代码时也走这套认证。5.4 什么时候可以关闭密码登录当所有常用设备的公钥都部署完毕、并且确认紧急备用通道存在之后可以考虑在服务器上关闭密码登录。这一步既是安全升级也是最容易把自己锁在门外的一步。修改/etc/ssh/sshd_configPasswordAuthentication no PubkeyAuthentication yes改完重启 sshdsudo systemctl restart sshd在做这个操作之前务必开第二个 SSH 会话测试私钥登录是否稳定甚至建议用另一个终端先保持一个已登录的会话不要退出防止配置错误导致所有连接断开。我见过不止一次有人把PasswordAuthentication no一改新连接立刻全部拒之门外只能重启服务器走控制台救援。所以这个操作我永远放在最后一步并且会先在测试机上验证流程再上生产。6. 几个亲测有效的收尾细节最后分享几条我在多次配置中沉淀下来的实际操作体会。第一排错顺序永远先是“权限”再是“路径”最后才怀疑“密钥写错”。权限问题看目录和文件的 ACL路径问题确认三个终端读的是同一个 config密钥问题用ssh -vT看日志里有没有Offering public key。按这个顺序排查大部分问题五分钟内能定位。第二密钥文件从 U 盘、网盘或其他机器拷贝过来后权限大概率会出问题。Windows 上最典型的就是报Bad owner or permissions直接对私钥文件和 config 分别执行icacls ... /inheritance:r和icacls ... /grant:r $env:USERNAME:F不要试图用改只读属性来解决。第三私钥的 passphrase 不要嫌麻烦。配好 ssh-agent 之后每次开机只需要输入一次收益是即使私钥文件被拷走别人也打不开你的密钥。我自己的默认习惯任何环境、任何密钥一律带 passphrase只有 CI/CD 机器上的部署密钥才例外。第四VSCode 连接出问题时先看输出面板里实际调用的ssh.exe路径和 config 路径。如果发现它读的不是你改的那份文件后面的所有配置修改都是白搭。你在本地终端测试通过不代表 VSCode 一定也是同一条链路。这套 SSH 私钥登录方案配完之后我日常的开发流基本变成了打开 VSCodeRemote-SSH 连上云服务器直接在远端改代码跑测试Git 推送走同一把私钥需要从跳板机进内网时 config 自动完成代理衔接。整个过程不会再被“输密码”打断也不会因为密码策略变更而突然失联。配置过程踩过的坑虽然不少但把这些细节理顺之后后续维护几乎是零成本。如果我当时有人把这些权限和路径的坑提前讲清楚至少能省下两个晚上的排查时间希望这篇记录能帮你顺利绕开这些弯路。
返回列表