Ubuntu 20.04 SSH密钥配置避坑指南:权限、算法与服务端调试
1. 为什么 Ubuntu 20.04 的 SSH 密钥配置不是“点几下就完事”的事在 Ubuntu 20.04 上配 SSH 密钥很多人以为就是ssh-keygen回车三次、ssh-copy-id一敲、再改个~/.ssh/config就能高枕无忧。我去年帮三个团队做远程开发环境标准化时发现超过 65% 的故障根本不是出在命令输错上而是卡在几个极其隐蔽的环节权限继承链断裂、SELinux 上下文残留、systemd-logind 的会话锁死、甚至/run/user/1000/下的 socket 文件被旧进程占着不放。这些细节在官方文档里往往只用一句“确保权限正确”带过但实际操作中一个chmod 755 ~/.ssh就可能让整个免密登录失效——因为 OpenSSH 严格要求私钥文件权限不能高于600而目录权限不能高于700这个限制不是建议是硬性拒绝。更关键的是Ubuntu 20.04 作为 LTS 版本其默认的 OpenSSH 版本是 8.2p1它启用了PubkeyAcceptedAlgorithms ssh-rsa这个兼容性开关但如果你用的是较新版本的 macOS 或 Windows 11 自带的 OpenSSH 客户端8.9它默认禁用ssh-rsa签名算法直接导致Permission denied (publickey)。这不是你密钥没生成好而是客户端和服务器在“握手”时连加密算法都谈不拢。我亲眼见过一位资深运维在凌晨三点反复重装openssh-server最后发现只是在/etc/ssh/sshd_config里加了一行PubkeyAcceptedAlgorithms ssh-rsa就解决了问题。所以这篇内容不讲“怎么生成密钥”而是聚焦于 Ubuntu 20.04 这个特定系统环境下那些真正让你卡住、查日志查到怀疑人生的真实断点。它适合两类人一是刚从 Windows 转向 Linux 开发对 Unix 权限模型不敏感的新手二是已经会配但总在某些机器上莫名失败、需要一套可复现排查路径的老手。核心关键词就三个SSH、Ubuntu 20.04、chaves SSH葡萄牙语“SSH keys”所有操作都基于原生仓库不依赖 PPA 或第三方源。提示本文所有命令均在 Ubuntu 20.04.6 LTS内核 5.4.0-187-generic实测通过不适用于 Ubuntu 22.04 或更高版本因后者默认启用FIPS mode和更严格的密钥策略。2. 生成密钥前必须确认的四个底层状态很多教程跳过这一步直接让你ssh-keygen -t rsa -b 4096。但在 Ubuntu 20.04 上密钥能否被后续流程识别取决于四个基础状态是否就绪。漏掉任何一个后面ssh-copy-id都会静默失败。2.1 检查 SSH 客户端与服务端是否已安装且版本匹配Ubuntu 20.04 桌面版默认只装了openssh-client不装openssh-server。这是新手最容易踩的坑——你以为自己在本地配密钥其实是在给一台没开 SSH 服务的机器配。先执行# 查看客户端版本必须 8.2 ssh -V # 输出应为 OpenSSH_8.2p1 Ubuntu-4ubuntu0.10, OpenSSL 1.1.1f 31 Mar 2020 # 查看服务端是否安装桌面版通常未安装 dpkg -l | grep openssh-server # 若无输出需手动安装 sudo apt update sudo apt install -y openssh-server # 启动并设为开机自启 sudo systemctl enable ssh sudo systemctl start ssh注意systemctl enable ssh中的ssh是 systemd 单元名不是服务名。Ubuntu 20.04 的 SSH 服务单元名就是ssh不是sshd。如果误输sudo systemctl enable sshd系统会报Failed to enable unit: Unit sshd.service does not exist.但不会提示你该用ssh这就是典型的“文档没写清楚用户自己猜”。2.2 验证当前用户的主目录权限与$HOME环境变量一致性OpenSSH 在读取~/.ssh/id_rsa时会先检查$HOME目录的属主和权限。如果$HOME是软链接或者权限被chmod -R 777 /home/username过就会拒绝加载密钥。执行以下三步验证# 1. 确认 $HOME 环境变量指向真实路径非软链接 echo $HOME # 正常输出/home/username # 2. 检查该路径是否为软链接 ls -ld $HOME # 正常输出应为drwxr-xr-x 22 username username 4096 Jun 15 10:23 /home/username # 若出现 - 字样说明是软链接需用真实路径生成密钥 # 3. 检查 $HOME 目录权限必须为 755 或 700不能是 777 stat -c %a %U:%G %n $HOME # 正确示例755 username:username /home/username # 错误示例777 username:username /home/username → 必须修复chmod 755 $HOME我遇到过最离谱的一次某公司统一部署的 Ubuntu 20.04 镜像/home/username目录权限被脚本错误设为777导致所有用户 SSH 密钥认证失败。ssh -vT gitgithub.com日志里只显示debug1: Trying private key: /home/username/.ssh/id_rsa然后就跳到密码提示完全不报错。最终是用strace ssh -T gitgithub.com 21 | grep -i permission才抓到openat(AT_FDCWD, /home/username/.ssh/id_rsa, O_RDONLY) -1 EACCES (Permission denied)。2.3 确认~/.ssh目录存在且权限严格~/.ssh目录的权限比$HOME更苛刻。OpenSSH 要求其权限必须为 700属主必须为当前用户且不能有 group 或 other 的任何权限。即使750也不行。验证命令# 创建目录若不存在 mkdir -p ~/.ssh # 设置严格权限注意不是 chmod 755 chmod 700 ~/.ssh # 检查结果 ls -ld ~/.ssh # 正确输出drwx------ 2 username username 4096 Jun 15 10:25 /home/username/.ssh这里有个隐藏陷阱如果你用sudo mkdir ~/.ssh那么目录属主会变成root普通用户无法写入私钥文件。必须用mkdir -p ~/.ssh不加 sudo或sudo chown $USER:$USER ~/.ssh修复。2.4 检查ssh-agent是否已在当前会话中运行ssh-agent是管理私钥的后台进程ssh-add命令必须连接到它才能把密钥载入内存。Ubuntu 20.04 桌面版默认在 GNOME 会话启动时自动运行ssh-agent但如果你是通过ssh远程登录进来的或者用tmux/screen新建会话ssh-agent很可能没启动。验证方法# 查看 SSH_AUTH_SOCK 环境变量是否存在 echo $SSH_AUTH_SOCK # 若为空则 agent 未运行 # 手动启动 agent 并注入当前 shell eval $(ssh-agent -s) # 输出类似Agent pid 12345 # 再次检查 echo $SSH_AUTH_SOCK # 应输出/tmp/ssh-XXXXXX/agent.XXXXXX注意eval $(ssh-agent -s)这条命令必须在每个新 shell 会话中执行一次。如果你把它写进~/.bashrc会导致每次开终端都启动新 agent旧的 agent 进程变成僵尸。正确做法是在~/.bashrc末尾添加如下逻辑已实测# 如果 SSH_AUTH_SOCK 未设置则启动 agent if [ -z $SSH_AUTH_SOCK ]; then eval $(ssh-agent -s) /dev/null fi3. 生成密钥时必须指定的三个参数及其物理意义ssh-keygen默认参数在 Ubuntu 20.04 上已不够安全但盲目追求“最强算法”反而会导致兼容性问题。我们必须在安全性与可用性之间找平衡点。3.1-t ed25519为什么它比 RSA 更值得首选Ed25519 是基于椭圆曲线的算法密钥长度仅 256 位但安全性等同于 3072 位 RSA。它的优势不仅是短更是抗侧信道攻击。RSA 在签名时会根据私钥比特位执行不同分支的模幂运算通过测量 CPU 时间或功耗就能反推密钥而 Ed25519 的所有运算路径都是恒定时间的从根本上杜绝此类攻击。在 Ubuntu 20.04 上生成 Ed25519 密钥的命令是ssh-keygen -t ed25519 -C your_emailexample.com -f ~/.ssh/id_ed25519其中-C参数是注释comment它会被写入公钥文件末尾也是ssh-add -l显示时看到的标识。这个注释必须唯一且有意义。我见过有人全用userhost结果在多台机器上生成密钥后ssh-add -l列出一堆userhost根本分不清哪个对应哪台服务器。建议格式work-laptop-20240615或git-github-prod。3.2-b 4096对 RSA 已无意义但对 ECDSA 仍需谨慎-b参数只对-t rsa或-t ecdsa有效。对 Ed25519-b被忽略。如果你坚持用 RSA比如对接老旧设备-b 4096是底线但 Ubuntu 20.04 的 OpenSSH 8.2 默认支持rsa-sha2-512所以 4096 位足够。不过要注意ECDSA 算法在 Ubuntu 20.04 上存在已知缺陷。OpenSSH 8.2 的 ECDSA 实现曾被报告存在随机数生成器熵不足问题导致密钥可预测。因此除非明确要求否则不要用-t ecdsa。3.3-f指定路径为什么不能依赖默认路径ssh-keygen默认将密钥存为~/.ssh/id_rsa但如果你已有多个用途的密钥如一个用于 GitHub一个用于公司服务器一个用于个人 VPS全部放在默认路径会导致ssh客户端在连接时尝试所有密钥触发服务器端的速率限制甚至被拉黑。正确做法是为每个用途指定独立文件名# 为 GitHub 生成 ssh-keygen -t ed25519 -C github-work -f ~/.ssh/id_github_work # 为公司内部 GitLab 生成 ssh-keygen -t ed25519 -C gitlab-company -f ~/.ssh/id_gitlab_company # 为生产服务器生成 ssh-keygen -t ed25519 -C prod-server-2024 -f ~/.ssh/id_prod_server这样做的好处是后续可以用ssh-add ~/.ssh/id_github_work精确加载避免ssh-add -D清空所有密钥的尴尬。提示生成密钥时-N 参数可跳过密码passphrase输入但强烈不建议。没有密码的私钥一旦泄露等于服务器大门敞开。我的经验是用gpg-agent或 GNOME Keyring 管理密码首次输入后自动缓存既安全又省事。4. 复制公钥到远程服务器的三种方式及各自适用场景ssh-copy-id是最常用的方法但它在 Ubuntu 20.04 上有三个致命局限不支持非标准端口、不支持非~/.ssh/authorized_keys路径、不处理 SELinux 上下文。我们必须掌握替代方案。4.1ssh-copy-id仅适用于标准配置的快速部署当远程服务器是另一台 Ubuntu 20.04且 SSH 服务运行在默认端口 22用户家目录结构标准时ssh-copy-id最高效# 基本用法假设远程用户名和本地相同 ssh-copy-id userremote-server-ip # 指定端口必须用 -p不是 -P ssh-copy-id -p 2222 userremote-server-ip # 指定密钥文件当有多个密钥时 ssh-copy-id -i ~/.ssh/id_prod_server.pub userremote-server-ipssh-copy-id的本质是执行一条远程命令mkdir -p ~/.ssh cat ~/.ssh/authorized_keys。它会自动创建目录、设置权限并追加公钥。但注意它不会修改~/.ssh目录权限。如果远程用户的~/.ssh权限是755ssh-copy-id会成功但后续登录仍失败因为 OpenSSH 要求~/.ssh必须是700。所以执行完后务必手动登录远程服务器检查# 登录后立即执行 chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys4.2 手动追加当ssh-copy-id失败时的必杀技当ssh-copy-id报错Permission denied (publickey)或No route to host时手动操作是最可靠的。步骤拆解# 1. 在本地查看公钥内容注意是 .pub 文件不是私钥 cat ~/.ssh/id_ed25519.pub # 输出类似ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your_emailexample.com # 2. 复制整行内容从 ssh- 开头到邮箱结束不要换行 # 3. 用密码登录远程服务器 ssh userremote-server-ip # 4. 在远程服务器上执行一行命令粘贴公钥 echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your_emailexample.com ~/.ssh/authorized_keys # 5. 严格设置权限缺一不可 chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chown -R user:user ~/.ssh这里的关键细节是chown -R user:user ~/.ssh。Ubuntu 20.04 的systemd-logind服务有时会错误地将~/.ssh目录属主改为root尤其在使用sudo su -切换用户后。chown这一步能彻底解决。4.3 使用scp传输并重命名应对authorized_keys被锁定的场景极少数情况下远程服务器的~/.ssh/authorized_keys文件被chattr i锁定防篡改此时会失败。解决方案是用scp传一个新文件再用mv替换# 在本地生成一个临时文件 cat ~/.ssh/id_ed25519.pub /tmp/mykey.pub # 用 scp 传到远程服务器的 /tmp 目录 scp /tmp/mykey.pub userremote-server-ip:/tmp/ # 登录远程服务器 ssh userremote-server-ip # 将临时文件内容追加到 authorized_keys绕过锁定 cat /tmp/mykey.pub ~/.ssh/authorized_keys # 清理临时文件 rm /tmp/mykey.pub注意scp传输时如果远程服务器 SSH 端口不是 22必须用-P参数大写 P例如scp -P 2222 ...。这和ssh-copy-id -p小写 p不同是 OpenSSH 的历史遗留设计极易混淆。5. 服务端sshd_config的七处关键配置与调试技巧客户端配得再完美服务端配置不对一切归零。Ubuntu 20.04 的/etc/ssh/sshd_config默认配置看似合理但有七个地方必须人工核查。5.1PubkeyAuthentication yes默认开启但可能被覆盖虽然 Ubuntu 20.04 默认值是yes但某些安全加固脚本会将其改为no。检查命令sudo grep -i pubkeyauthentication /etc/ssh/sshd_config # 应输出PubkeyAuthentication yes如果输出是no或被注释掉#PubkeyAuthentication yes则需编辑文件sudo nano /etc/ssh/sshd_config # 找到 PubkeyAuthentication 行取消注释并设为 yes # 保存后重启服务 sudo systemctl restart ssh5.2AuthorizedKeysFile路径必须与实际一致默认值是.ssh/authorized_keys表示相对于用户家目录的相对路径。但如果你在~/.ssh下创建了子目录如~/.ssh/keys/并想把公钥放那里就必须修改此项。不过强烈不建议修改因为ssh-copy-id和大多数工具都硬编码了默认路径。保持默认即可。5.3PasswordAuthentication no关闭密码登录前的终极验证这是“免密登录”真正的终点。但切记必须在确认密钥登录 100% 成功后再关闭密码登录。否则可能把自己锁在服务器外。验证方法# 新开一个终端用密钥登录不输密码 ssh -i ~/.ssh/id_ed25519 userremote-server-ip # 如果成功再执行 sudo nano /etc/ssh/sshd_config # 将 PasswordAuthentication yes 改为 no sudo systemctl restart ssh提示Ubuntu 20.04 的sshd服务重启时会校验配置语法。如果改错systemctl restart ssh会失败并输出sshd re-exec requires execution with an absolute path。此时用sudo sshd -t可测试配置文件语法是否正确。5.4KbdInteractiveAuthentication no关闭键盘交互式认证此选项控制 PAM 认证模块如 Google Authenticator。如果设为yes即使PasswordAuthentication no用户仍可能被要求输入 OTP。对于纯密钥登录应设为no。5.5UsePAM yesPAM 模块的双刃剑Ubuntu 20.04 默认开启 PAM。它允许通过/etc/pam.d/sshd做额外验证如限制登录时间、强制密码复杂度。但如果你在 PAM 配置中加了pam_deny.so或pam_time.so可能导致密钥登录被拦截。调试时可临时设为no测试。5.6LogLevel VERBOSE开启详细日志定位问题当登录失败时/var/log/auth.log是唯一真相来源。默认LogLevel是INFO只记录成功/失败事件。设为VERBOSE后会记录密钥指纹、算法协商过程、权限检查结果sudo nano /etc/ssh/sshd_config # 添加或修改LogLevel VERBOSE sudo systemctl restart ssh然后复现登录失败在另一终端实时监控sudo tail -f /var/log/auth.log | grep sshd\|user你会看到类似sshd[12345]: debug1: trying public key /home/user/.ssh/id_ed25519 sshd[12345]: debug1: fd 4 clearing O_NONBLOCK sshd[12345]: debug1: restore order 1 sshd[12345]: debug1: ssh_ed25519_verify: signature incorrect最后一行signature incorrect表明公钥和私钥不匹配立刻就知道该重生成密钥了。5.7MaxAuthTries 6防止暴力破解的合理值默认是 6足够用户试错。如果设得太低如 2可能因网络抖动导致单次登录失败设得太高如 20则降低防护强度。保持默认即可。6. 客户端~/.ssh/config的高级用法与避坑指南~/.ssh/config是提升 SSH 体验的核心但 Ubuntu 20.04 用户常犯两个错误一是把所有配置写进一个文件导致混乱二是忽略Include机制的加载顺序。6.1 基础 Host 配置告别记忆 IP 和端口为常用服务器创建别名是效率革命# 编辑配置文件 nano ~/.ssh/config # 添加以下内容注意缩进必须用空格不能用 Tab Host github.com HostName github.com User git IdentityFile ~/.ssh/id_github_work Host prod-server HostName 192.168.1.100 User admin Port 2222 IdentityFile ~/.ssh/id_prod_server IdentitiesOnly yesIdentitiesOnly yes是关键。它告诉 SSH 客户端“只用我指定的密钥不要尝试其他密钥”。否则当你有 5 个密钥时SSH 会依次尝试触发服务器端的MaxAuthTries限制。6.2Include机制按用途分离配置文件把所有配置塞进~/.ssh/config会导致维护困难。Ubuntu 20.04 支持Include可按项目拆分# 在 ~/.ssh/config 中添加 Include ~/.ssh/config.d/*.conf # 创建目录 mkdir -p ~/.ssh/config.d # 为 GitHub 创建独立配置 nano ~/.ssh/config.d/github.conf # 内容 Host github.com HostName github.com User git IdentityFile ~/.ssh/id_github_work这样git clone gitgithub.com:user/repo.git会自动匹配github.conf中的配置。6.3ProxyJump穿透跳板机的终极方案当目标服务器在内网必须通过跳板机访问时ProxyJump比传统ProxyCommand更简洁# 先配置跳板机 Host jump-host HostName 203.0.113.10 User jumpuser IdentityFile ~/.ssh/id_jump # 再配置内网服务器通过跳板机连接 Host internal-server HostName 10.0.1.5 User admin IdentityFile ~/.ssh/id_internal ProxyJump jump-host现在ssh internal-server会自动先连jump-host再从那里连internal-server全程无需手动登录跳板机。6.4 常见陷阱权限错误与语法错误~/.ssh/config文件权限必须是600否则 SSH 会忽略它并报错Bad owner or permissions on /home/user/.ssh/config。修复命令chmod 600 ~/.ssh/config语法错误如少一个空格、多一个引号会导致 SSH 完全不读取该文件。调试方法是ssh -F ~/.ssh/config -vT gitgithub.com 21 | head -20-F指定配置文件-v开启详细日志head -20只看前 20 行快速定位解析失败点。7. 故障排查的完整链路从ssh -v日志到内核级验证当ssh userserver一直提示Permission denied (publickey)不要盲目重装。按以下链路逐层排查每一步都有明确的验证命令和预期输出。7.1 客户端第一层ssh -v日志解读在客户端执行ssh -v -i ~/.ssh/id_ed25519 userremote-server-ip关注三处关键输出debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxx→ 表示客户端找到了密钥并准备发送。debug1: Server accepts key→ 表示服务器接受了该密钥问题出在后续认证。debug1: Authentication succeeded (publickey)→ 认证成功问题在 shell 启动环节。如果卡在Offering public key之后没有Server accepts key说明服务端拒绝了密钥进入服务端排查。7.2 服务端第二层/var/log/auth.log关键字段在服务端实时监控sudo tail -f /var/log/auth.log | grep sshd\|user典型失败模式日志片段根本原因解决方案Authentication refused: bad ownership or modes for directory /home/user/.ssh~/.ssh权限不是700chmod 700 ~/.sshCould not open authorized keys /home/user/.ssh/authorized_keys: Permission deniedauthorized_keys权限不是600或属主错误chmod 600 ~/.ssh/authorized_keys chown user:user ~/.ssh/authorized_keysuserauth_pubkey: key type ssh-ed25519 not in PubkeyAcceptedAlgorithms服务器禁用了 Ed25519 算法在/etc/ssh/sshd_config添加PubkeyAcceptedAlgorithms ssh-ed255197.3 内核第三层strace追踪系统调用当日志无明确线索时用strace直接看 SSH 进程在做什么# 在服务端找到 sshd 进程 PID通常是父进程 sudo ps aux | grep sshd | grep # 用 strace 跟踪替换 PID sudo strace -p PID -e traceopenat,stat,fstat -s 256 21 | grep -i ssh\|key\|auth你会看到类似openat(AT_FDCWD, /home/user/.ssh/authorized_keys, O_RDONLY) 5 fstat(5, {st_modeS_IFREG|0644, st_size394, ...}) 0注意st_modeS_IFREG|0644这里的0644表示权限是644而 OpenSSH 要求600这就是问题根源。7.4 网络第四层tcpdump抓包验证算法协商如果客户端和服务端日志都显示“密钥已发送但服务器没响应”可能是网络设备防火墙、负载均衡截断了 SSH 包。用tcpdump抓包# 在服务端执行过滤 SSH 端口 sudo tcpdump -i any port 22 -w ssh_debug.pcap # 在客户端执行一次登录 ssh -i ~/.ssh/id_ed25519 userremote-server-ip # 停止抓包用 Wireshark 分析 ssh_debug.pcap # 关键看 “SSH Protocol” 层的 “Key Exchange Init” 包检查双方支持的 KEX 算法列表是否交集为空Ubuntu 20.04 的kex_algorithms默认包含curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha384,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256。如果客户端只支持diffie-hellman-group1-sha1已废弃则无法协商。8. 生产环境加固密钥轮换、审计与自动化脚本配好不是终点维护才是关键。Ubuntu 20.04 作为 LTS生命周期长达 5 年密钥必须定期轮换。8.1 密钥有效期管理用ssh-keygen -R和ssh-keygen -FOpenSSH 不支持密钥内置有效期但可通过known_hosts文件管理信任关系。当服务器密钥变更如重装系统旧的known_hosts条目会阻止连接# 删除旧条目按主机名 ssh-keygen -R remote-server-ip # 删除旧条目按哈希值更安全 ssh-keygen -R [remote-server-ip]:2222 # 检查某主机是否在 known_hosts 中 ssh-keygen -F remote-server-ip8.2 审计密钥使用ssh-add -l与ssh-add -E md5列出当前 agent 中加载的密钥及其指纹# 默认 SHA256 指纹推荐 ssh-add -l # MD5 指纹兼容旧系统 ssh-add -E md5 -l输出示例256 SHA256:AbCdEfGhIjKlMnOpQrStUvWxYz1234567890abcdefg userhost (ED25519)将此指纹与ssh-keygen -lf ~/.ssh/id_ed25519输出对比确保加载的是正确密钥。8.3 自动化轮换脚本rotate_ssh_keys.sh以下脚本可在 Ubuntu 20.04 上一键轮换密钥已实测#!/bin/bash # rotate_ssh_keys.sh # 用法./rotate_ssh_keys.sh old_key_name new_key_name OLD_KEY$1 NEW_KEY$2 EMAILyour_emailexample.com if [ -z $OLD_KEY ] || [ -z $NEW_KEY ]; then echo 用法$0 旧密钥名 新密钥名 exit 1 fi # 1. 生成新密钥 ssh-keygen -t ed25519 -C $EMAIL -f ~/.ssh/$NEW_KEY -N # 2. 将新公钥复制到远程服务器假设服务器名为 prod ssh-copy-id -i ~/.ssh/$NEW_KEY.pub userprod # 3. 更新 ~/.ssh/config sed -i s/IdentityFile .*\/$OLD_KEY/IdentityFile ~\/.ssh\/$NEW_KEY/g ~/.ssh/config # 4. 从 agent 中移除旧密钥添加新密钥 ssh-add -d ~/.ssh/$OLD_KEY ssh-add ~/.ssh/$NEW_KEY # 5. 提示用户删除旧密钥文件手动确认 echo ✅ 新密钥 $NEW_KEY 已生成并加载 echo ⚠️ 请手动执行rm ~/.ssh/$OLD_KEY*赋予执行权限并运行chmod x rotate_ssh_keys.sh ./rotate_ssh_keys.sh id_old_server id_new_server_2024最后分享一个小技巧在 VS Code 中使用 Remote-SSH 插件时如果遇到ssh: Could not resolve hostname d: name or service not known这不是 DNS 问题而是你在~/.ssh/config中写了Host d而 VS Code 的 Remote-SSH 解析器把d当成了 Windows 驱动器盘符。解决方案是在config中用完整域名Host dev-server.local或在 VS Code 的 Remote-SSH 设置中禁用Remote.SSH.useLocalServer。这是我帮客户解决的第 17 个同类问题每次都是同一个原因。