每天在终端里敲ssh user192.168.1.100然后老老实实输密码一天反复十几次偶尔还得处理scp传到一半断掉重来的情况。后来开始写自动化脚本、用 Ansible 批量下发配置密码认证彻底成了瓶颈——脚本一跑起来就卡在交互式输入那里要么用 expect 蛋疼地匹配提示符要么就得把密码硬编码进配置文件。无论哪种体验都很糟糕。SSH 免密登录解决的就是这个问题把每次验证身份变成首次信任、之后畅通同时安全性并不比密码登录差甚至更好。这篇文章我基于自己在 win10 和多家 Linux 发行版上的实操经历把两端配置、常见坑、批量管理的完整思路都铺开讲一遍希望对正在折腾这件事的你有点帮助。1. 折腾免密登录之前先想清楚要解决什么1.1 每天输密码的日子到底输在哪了在 Windows 10 上装个 OpenSSH 客户端非常简单系统从 1809 版本开始就自带了ssh.exe不需要额外安装任何东西。所以很多人的日常工作流是打开 PowerShell 或 Windows Terminal敲 ssh 命令连上 Linux 服务器输入密码开始干活。单次操作也就几秒钟但一天下来这些几秒钟会被无限放大。更麻烦的是当操作对象变成一批服务器时密码认证的短板就极其明显。比如我临时要检查 10 台机器的磁盘空间用脚本循环 ssh 过去执行df -h密码认证就会让脚本卡死。你可能说可以用sshpass或者expect但这类工具需要额外安装而且把密码以明文形式写在脚本或命令行参数里本身就是个安全隐患。免密登录的真正价值不是少打几个字而是让非交互式场景变得可用让自动化工具链Ansible、rsync、crontab 定时任务不再受制于人工输密码。1.2 免密登录不是无脑配置它有自己的适用范围这里要先泼一盆冷水免密登录不是所有场景都该用。它的前提是客户端身份可信。你自己家里的电脑、公司的跳板机、云上的管理机这类你能完全控制物理和系统安全的设备非常适合做免密。但如果你在一台公共电脑上配置免密那等于把进入服务器的钥匙留给了所有能登录这台公共电脑的人风险很高。适用场景包括个人开发机到测试服务器减少日常操作摩擦。跳板机到内网服务器配合 SSH Agent 做转发。CI/CD 流水线中构建机到部署目标机的自动发布。定时脚本、rsync 备份、日志采集等无人值守任务。需要谨慎的场景公网暴露的高权限服务器不建议设置免密或者至少限制来源 IP。多人共用的机器、临时工位电脑、云桌面不建议配置。跨安全域跳转时需要评估私钥落入他人手里的可能性。1.3 我这次配置所采用的环境约定为了后面叙述不乱先交代一下环境。客户端我以 Windows 10 专业版 22H2 为主Linux 发行版覆盖了 Ubuntu 22.04 LTS、Debian 12、CentOS Stream 9、龙蜥 OS 8 这几种。win10 端的 OpenSSH 客户端版本是 8.xLinux 端 sshd 版本有 OpenSSH 8.9p1 和 9.x 等。不同版本之间密钥算法兼容性有些差异后面会专门提到。整体流程在多数主流发行版上通用如果你用的是其他版本操作基本一致只是个别配置文件路径和命令包名会有差别。2. 免密登录背后的公钥认证流程2.1 用锁和钥匙来理解公私钥很多第一次接触免密登录的人容易把公钥和私钥搞混。换个角度讲公钥是一把锁私钥是这把锁唯一的钥匙。你把锁公钥装到服务器的门上钥匙私钥留在自己的客户端手里。别人看到了你的锁也没用因为他没有对应的钥匙钥匙丢了就比较麻烦捡到钥匙的人能打开所有装过这把锁的门。所以私钥文件的权限必须严格控制这就是后文反复强调权限的原因。服务器只保存公钥客户端每次连接时证明自己持有私钥。这个设计的好处是就算服务器被入侵攻击者拿到的是公钥无法反推出私钥就算公钥在传输过程中被人截获也一样没用。所以把公钥内容随便发给别人没问题私钥则必须当作比密码更重要的秘密来保护。2.2 握手过程里发生了什么SSH 公钥认证的完整流程大致是客户端发起连接后服务器发送一个随机挑战值客户端用私钥对这个挑战值做签名服务器拿到签名后用自己保存的公钥去验签验签成功就确认客户端持有对应私钥建立会话。整个过程不传输私钥本身也不需要用密码登录。这也是为什么免密登录叫做公钥认证而不是无认证——它的认证强度通常高于密码。服务器端在验证签名通过后还会检查是否存在~/.ssh/authorized_keys文件中的对应公钥条目以及客户端 IP、用户等信息是否被允许。严格模式下文件属主和权限也会被检查这就为后面的权限坑埋下了伏笔。2.3 免密不等于不安全前提是管好私钥说到底密码认证和公钥认证的区别是密码是你知道什么something you know私钥是你拥有什么something you have。公钥认证更接近物理世界的钥匙不会因为某次通信被监听而泄露。但前提是私钥不能丢。为了提升安全性可以在生成密钥时设置 passphrase口令短语相当于给私钥文件再加一道加密。这样即使别人拷走了你的私钥文件没有 passphrase 也用不了。日常使用中可以用 SSH Agent 把私钥加载进内存这样每次连接时就不需要重复输入 passphrase 了。这个组合既方便又不牺牲安全我强烈建议在生产环境使用。3. win10 作为客户端生成密钥、部署公钥到 Linux 的完整操作3.1 先确认 win10 自带的 OpenSSH 是否可用很多教程一上来就让人下载 Putty 或第三方工具实际上 win10 自带的 OpenSSH 客户端已经很够用了。打开 PowerShell输入ssh -V能看到类似于OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2的输出就说明可用。如果提示找不到命令可以去设置 → 应用 → 可选功能 → 添加可选功能找到 OpenSSH 客户端安装。装了之后重启终端就行。这里我推荐直接用 Windows Terminal 或者 PowerShell 7 来跑 ssh 相关命令老款 conhost 对 SSH 交互式终端支持不是很好遇到全屏程序比如 vim、htop时显示容易出问题。3.2 生成密钥对选 ed25519 还是 RSA在 win10 的 PowerShell 中执行ssh-keygen -t ed25519 -C win10-dev-key -f $env:USERPROFILE\.ssh\id_ed25519参数说明-t ed25519指定密钥算法类型。Ed25519 是目前综合推荐的选择密钥短、速度快、安全性高。但如果你的 Linux 服务器版本较老比如 CentOS 7 早期、Ubuntu 16.04 之前sshd 可能不支持 Ed25519那就改用-t rsa -b 4096。-C win10-dev-key添加注释用于标识这枚密钥的来源或用途。建议写清楚是哪台机器、给谁用的后面管理一堆公钥时会省很多事。-f指定保存路径。Windows 默认位置是C:\Users\你的用户名\.ssh\id_ed25519我一般会显式指定避免密钥放到默认之外的路径后 ssh 找不到。执行后它会提示设置 passphrase。本地开发机建议直接留空按两次回车即可因为日常使用输 passphrase 也很烦生产环境或公司电脑强烈建议设置一个 passphrase然后用 ssh-agent 来记住它。生成完成后.ssh目录下会多出两个文件id_ed25519私钥和id_ed25519.pub公钥。私钥文件在 Windows 资源管理器里可能看不到因为它是隐藏文件。如果想快速查看公钥内容cat $env:USERPROFILE\.ssh\id_ed25519.pub3.3 把公钥部署到 Linux 服务器的几种方式这是整个流程里最容易让人懵的一步。win10 没有ssh-copy-id这个命令不过有更好的替代方案。第一种直接在 PowerShell 里用管道把公钥内容追加到服务器的authorized_keys文件中type $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 先提示你输入一次密码输入正确后它会自动创建.ssh目录并把公钥追加进去。注意替换root192.168.1.100为你的实际用户名和 IP。如果服务器上已经存在authorized_keys文件上面命令会用追加而不是覆盖不会影响已有公钥。第二种如果公钥内容比较多或不喜欢用管道就手动复制。先在本地打印公钥内容然后登录服务器用 vim 或 nano 编辑~/.ssh/authorized_keys把内容粘贴到新的一行。这种方式的缺点是容易漏掉结尾换行导致两行公钥粘在一起后文排查会讲这个坑。第三种只适用于 Linux 客户端但值得提一下如果你手上已经有一台 Linux 管理机也可以把公钥先传到那台机器上然后用ssh-copy-id批量分发过去。win10 客户端本身没有这个命令但可以通过 Git Bash 或 Cygwin 环境间接调用我个人不太推荐为了一个命令去装全套模拟环境。3.4 验证免密登录注意 known_hosts 首次确认部署完公钥后在 win10 的 PowerShell 里执行ssh user192.168.1.100如果没有设置 passphrase正常情况下会直接进入服务器的 shell不再提示密码。如果服务器之前从未被连接过会出现一段 host key 确认提示The authenticity of host 192.168.1.100 cant be established. ED25519 key fingerprint is SHA256:xxx. Are you sure you want to continue connecting (yes/no)?这个提示是 SSH 客户端在确认你连接的是正确的服务器防止中间人攻击。输入yes确认后会写入known_hosts文件以后不会再提示。如果你不确定服务器的指纹是否真实可以用ssh-keyscan或手动登录一次获取指纹进行比对但日常内网环境下第一次连接确认 host key 信息后也没太大问题。如果到这里发现仍然要密码别急着怀疑人生八成问题出在 Linux 服务器端的文件权限上进入下一章。4. Linux 服务器端 authorized_keys权限就是免密登录的命门4.1 .ssh 目录和 authorized_keys 文件的权限要求之前帮同事排查过一次公钥明明已经写进authorized_keys了还是一直提示密码认证。后来登录服务器一看.ssh目录权限是755authorized_keys文件权限是644。这就是典型的权限地狱。OpenSSH 出于安全考虑默认启用了 StrictModes如果~/.ssh或~/.ssh/authorized_keys的权限过于宽松sshd 会认为这些文件可能被其他用户篡改直接拒绝使用它们做公钥认证然后退回到密码认证。严格来说要求的权限是用户家目录本身不能是 group 或 others 可写的通常是755或700。~/.ssh目录权限应为700只有属主能读写执行。~/.ssh/authorized_keys文件权限应为600只有属主能读写。执行以下命令修复chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chown -R $(whoami):$(whoami) ~/.ssh如果是用 root 用户配置没有chown问题但如果你是切换到某个普通用户注意authorized_keys文件的属主必须是你登录时那个用户。比如你从 win10 用ubuntu用户登录公钥就必须写在/home/ubuntu/.ssh/authorized_keys不能写到 root 的目录下。4.2 权限不对时怎么从日志里看出原因如果你配置完免密登录仍然要输密码第一件事不是去改 sshd 配置而是看服务端日志。在 Ubuntu/Debian 上sudo journalctl -u ssh -f在 CentOS/RHEL/龙蜥 OS 8 上sudo tail -f /var/log/secure然后从 win10 那边再发起一次 ssh 连接观察服务端日志输出。如果权限有问题日志里会出现Authentication refused: bad ownership or modes for file /home/ubuntu/.ssh/authorized_keys看到这句话基本就是权限问题按上一节的方法修即可。日志里还可能出现Permission denied (publickey,password)这个表示客户端尝试过公钥但服务端拒绝了常见原因包括公钥内容不对、权限有问题、sshd 配置禁止了 pubkey 认证。4.3 sshd_config 里可能挡路的配置项除了文件权限/etc/ssh/sshd_config中的几个项目也需要确认sudo grep -E PubkeyAuthentication|AuthorizedKeysFile|PasswordAuthentication|PermitRootLogin /etc/ssh/sshd_config常见情况PubkeyAuthentication yes必须启用这是公钥认证的总开关。AuthorizedKeysFile .ssh/authorized_keys保持默认即可不要乱改成绝对路径否则会到意想不到的地方找公钥。PasswordAuthentication如果设置为no则禁止密码登录。这个配置也是很多网站在配置完免密后为了安全而开的但改之前一定要确认公钥认证已经能正常工作否则一旦两边不匹配你可能连服务器都进不去了。PermitRootLogin如果设置为prohibit-password或yesroot 用户才能用公钥直接登录。默认很多发行版是prohibit-password也就是 root 可以用公钥登录但不能用密码登录这其实是个不错的配置。修改完sshd_config后需要重启 sshd 服务sudo systemctl restart sshd注意重启服务不会断开现有连接但如果你改错了配置导致 sshd 起不来需要格外小心。远程操作时建议先执行sudo sshd -t检查配置语法确认无误再重启。4.4 SELinux 和 AppArmor 给免密登录设的额外关卡CentOS、Rocky Linux、龙蜥 OS 这类基于 RHEL 的发行版默认启用了 SELinux。即使文件权限正确SELinux 也可能阻止 sshd 读取用户家目录中的 authorized_keys 文件。特别是当你把.ssh目录从一个地方复制过来或者手动创建了目录但安全上下文不对时会遇到奇奇怪怪的问题。如果权限和配置都检查过了仍然无法免密登录可以在服务器上执行sudo restorecon -Rv ~/.ssh这是恢复目录的 SELinux 安全上下文。如果之前把.ssh放到非正常路径比如自定义 home 目录还需要检查该目录的 SELinux 类型是否为ssh_home_t。用ls -Z ~/.ssh可以查看ls -Z ~/.ssh正常输出应为unconfined_u:object_r:ssh_home_t:s0之类如果显示var_t或其他类型执行restorecon修正。AppArmor 在 Ubuntu/Debian 上默认对 sshd 的限制相对宽松一般不需要额外处理。但如果遇到报错无法读取公钥也可以顺手查一下/etc/apparmor.d/usr.sbin.sshd规则虽然这种情况很少。4.5 一台服务器上放多个公钥怎么管理不混乱服务器上的authorized_keys文件支持一行一个公钥也就是说可以同时放很多人的公钥。没必要给每个人都建单独的账户除非需要区分权限。我个人的管理习惯是公钥内容后面必须带注释标明哪台机器、哪个用户、什么时候加的。定期清理不再使用的公钥。手动编辑 authorized_keys 时使用编辑器写完后确认最后一行有换行。如果一个用户的authorized_keys文件行数越来越多可以考虑把不同用途的公钥按行组织并在上方添加注释行。比如# desktop win10 dev ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... win10-dev-key # jump server ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ... jump-key注意authorized_keys文件里不能识别#开头的注释行实际上 OpenSSH 支持在authorized_keys中用#开头作为注释但要注意别把整行公钥误注释掉。我自己偶尔会见到有人在文件里写了两个公钥但中间没有换行结果两个公钥连成一行导致两个都验证失败。遇到这种情况把文件拆成两行即可。5. 反过来Linux 免密登录 win10 的配置实现双向互通5.1 在 win10 上安装并启动 OpenSSH Server标题既然是在 win10 和 Linux 上配置 SSH 免密登录那么双向互通也是绕不开的需求。比如你有一台 Linux 服务器想直接连到 win10 机器上执行 Windows 命令或者在 win10 上跑自动化脚本就需要在 win10 端装 OpenSSH Server。安装路径设置 → 应用 → 可选功能 → 添加可选功能找到OpenSSH 服务器点击安装。安装完成后用管理员身份打开 PowerShell执行Start-Service sshd Set-Service -Name sshd -StartupType Automatic第一条命令启动 sshd 服务第二条设置开机自启。然后再查一下防火墙规则Get-NetFirewallRule -Name *ssh*正常情况下安装 OpenSSH Server 时会自动创建两条防火墙入站规则允许 22 端口访问。如果没有手动添加New-NetFirewallRule -Name sshd -DisplayName OpenSSH Server (sshd) -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 225.2 把 Linux 的公钥部署到 win10 的 authorized_keyswin10 的 OpenSSH Server 是基于 OpenSSH for Windows 的移植版本认证逻辑和 Linux 基本一致。因此在 Linux 上生成密钥对ssh-keygen -t ed25519 -C linux-manage然后查看公钥cat ~/.ssh/id_ed25519.pub接下来把公钥放到 win10 上。这一步有个大坑win10 的 OpenSSH Server 默认配置中管理员组成员的公钥文件路径不是%USERPROFILE%\.ssh\authorized_keys而是C:\ProgramData\ssh\administrators_authorized_keys。这个文件需要手动创建并且权限必须设置为只允许 Administrators 组和 SYSTEM 访问否则 sshd 会拒绝使用这个文件。我在第一次配置时就踩了这个坑把公钥写到了C:\Users\admin\.ssh\authorized_keys结果 Linux 连过来一直提示Permission denied (publickey)。后来看了官方文档才明白win10 的 sshd_config 里面有这么一段Match Group administrators AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys也就是说管理员用户登录时sshd 不会去用户目录下找 authorized_keys而是去C:\ProgramData\ssh\administrators_authorized_keys找。所以配置步骤是在 win10 上以管理员身份打开 PowerShell创建目录和文件mkdir $env:ProgramData\ssh notepad $env:ProgramData\ssh\administrators_authorized_keys把 Linux 的公钥粘贴进去保存。修正文件权限让 sshd 能读取。注意不要使用普通方式直接赋予 Everyone 权限否则 sshd 也会因为权限过宽拒绝。建议用 icaclsicacls $env:ProgramData\ssh\administrators_authorized_keys /inheritance:r icacls $env:ProgramData\ssh\administrators_authorized_keys /grant SYSTEM:F icacls $env:ProgramData\ssh\administrators_authorized_keys /grant Administrators:F如果你登录 win10 用的是非管理员组的普通用户那么公钥就放到C:\Users\用户名\.ssh\authorized_keys权限要求相对宽松一些但ssh的日志会告诉你具体去哪里找。重启 sshdRestart-Service sshd然后回到 Linux 上测试ssh administrator192.168.1.50如果没提示密码就进去了说明双向免密配置成功。5.3 win10 OpenSSH Server 的默认 Shell 设置连上 win10 之后默认打开的是 Windows CMD不是 PowerShell。如果你更习惯 PowerShell可以在 win10 上修改注册表或者用New-ItemProperty设置默认 shellNew-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell -Value C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -PropertyType String -Force修改后需要重启 sshd。如果不设置默认 CMD 也能用只是部分命令不熟悉的话容易蒙。5.4 Linux 连 win10 时的文件编码与路径坑Linux 下的scp可以直接拷贝文件到 win10但注意 Windows 路径分隔符和权限。比如scp ./test.txt administrator192.168.1.50:C:/Users/administrator/Desktop/路径中尽量使用正斜杠避免反斜杠被 shell 转义。另外win10 的 OpenSSH 默认是 OpenSSH for Windows和原生 OpenSSH 行为有一定区别有些配置文件里的指令不支持比如AuthorizedKeysCommand遇到报错时留意一下版本兼容性。6. 排错实战从一条连接失败到定位根因的完整链路6.1 先判断问题出在客户端还是服务端免密登录失败时先不要一股脑去改配置。我的排查顺序是在客户端执行ssh -vvv userhost看详细输出。观察输出中的关键阶段网络连接是否成功、是否尝试了公钥认证、服务端是否接受公钥、是否退回了密码。根据输出定位是客户端问题还是服务端问题。-vvv输出非常啰嗦但里面藏着关键信息。比如debug1: Authentications that can continue: publickey,password debug1: Next authentication method: publickey debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxx debug1: Server accepts key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxx看到Server accepts key之后仍然让你输密码那大概率是服务端在公钥验证之外还有额外限制比如权限、用户限制、Match 块。如果输出里压根没有Offering public key这一行就说明客户端没有找到合适的私钥需要检查-i参数或默认密钥路径。6.2 服务端日志是最终裁判在服务器上开一个终端实时跟踪日志然后从客户端再试一次连接。Ubuntu/Debian 用sudo journalctl -u ssh -fCentOS/RHEL 用sudo tail -f /var/log/secure日志里如果有Authentication refused: bad ownership or modes这是权限问题如果有Failed publickey for user from 192.168.1.20 port 50000 ssh2: RSA SHA256:xxx说明公钥验证失败可能是公钥内容不匹配如果有Connection closed by authenticating user可能要检查 sshd 是否因为次数过多临时断开了连接。另外很多发行版默认开启了MaxAuthTries 6反复输错或公钥尝试次数过多会被断开。如果客户端配了多个密钥文件可能触发这个限制。6.3 高频问题对照表我在不同环境里遇到并解决过的问题整理如下现象可能原因解决方式连接被拒绝sshd 服务未启动、防火墙拦截、端口不对Windows 上检查自启动Linux 用 systemctl status sshd总是提示密码客户端没提供私钥或服务端拒绝了公钥排查公钥是否部署、权限是否正确、客户端是否用了正确密钥Permission denied (publickey)服务端关闭了密码认证公钥又不过从控制台登录检查公钥和权限Bad owner or permissions.ssh 或 authorized_keys 权限过宽设置 .ssh 为 700、authorized_keys 为 600修正属主no matching key exchange method客户端算法与服务端不匹配升级客户端或修改 sshd_config 添加算法Server accepts key 但还是要密码用户锁了密码或账户被禁止登录检查 /etc/passwd 中 shell 是否正常是否被锁定第一次连接确认 host key 后仍失败known_hosts 记录与实际 host key 冲突用ssh-keygen -R 主机IP清除旧记录win10 管理员无法公钥登录公钥写入位置不对放到 C:\ProgramData\ssh\administrators_authorized_keys6.4 客户端多密钥和 known_hosts 的诡异问题很多人在同一台 win10 上拥有多个 SSH 密钥一个连 GitHub一个连工作服务器一个连云主机。SSH 客户端默认只会尝试按顺序使用默认路径下的私钥如果你的私钥不叫id_rsa、id_ed25519或者不在默认目录客户端可能压根不会去尝试。这时可以显式指定ssh -i C:\Users\me\.ssh\my_custom_key user192.168.1.100或者更优雅的做法是创建 SSH config 文件在C:\Users\me\.ssh\config中配置Host myserver HostName 192.168.1.100 User ubuntu Port 22 IdentityFile C:\Users\me\.ssh\my_custom_key配置之后直接ssh myserver就能连接。SSH config 对多主机管理非常有用后面章节会详细展开。known_hosts的坑也很常见。当你重装了一次服务器系统或者服务器换了 host key客户端会因为 known_hosts 里保存的旧 host key 与当前服务器的 host key 不匹配而拒绝连接提示类似WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!解决方法是清除旧记录ssh-keygen -R 192.168.1.100注意这个命令清除的是 known_hosts 中该主机的记录不会影响其他主机。重新连接后按提示接受新的 host key 即可。7. 免密登录进阶多主机批量管理与自动化7.1 用 SSH config 把免密登录变成一键直达公钥配置好了之后免密登录已经能用但每次都要敲ssh user192.168.1.100 -p 2222这种长命令还是不够爽。在客户端配置 SSH config 可以解决这个问题。win10 上文件位于C:\Users\你的用户名\.ssh\configLinux 上位于~/.ssh/config。一个典型的配置示例Host dev-ubuntu HostName 192.168.1.100 User ubuntu Port 22 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60 Host prod-centos HostName 10.0.0.5 User root Port 2222 IdentityFile ~/.ssh/id_rsa_prod第一列Host后面的别名可以随便取ssh dev-ubuntu就能免密登录到对应主机。ServerAliveInterval是发送心跳包的时间间隔防止远程会话因为长时间无操作被防火墙断开我这个参数基本每次都会配。7.2 批量分发公钥到多台 Linux 服务器当你手里有几十台服务器逐台手动粘贴公钥显然不现实。最常见的方式是在一台 Linux 管理机上写好循环脚本用sshpass提供密码或者先用密码登录一台再借助信任关系扩散。假设你已经在管理机上生成了密钥现在有一个hosts.txt文件每行一个 IP账号统一为root用sshpass批量执行#!/bin/bash while read host; do sshpass -p 你的临时密码 ssh-copy-id -o StrictHostKeyCheckingno root$host done hosts.txt注意sshpass需要单独安装而且临时密码会暴露在脚本中。更好的做法是先把所有服务器都设置成一个临时密码然后批量执行完成后立即修改密码或者配合 CMDB 来传参。如果你已经配好了其中一台服务器的免密也可以通过ProxyJump跳转来间接管理其他内网机器。如果你的环境里有 Ansible那更方便。在 Ansible 的 hosts 清单里配置好所有服务器然后执行一个 ad-hoc 命令分发公钥ansible all -m authorized_key -a userroot key{{ lookup(file, /root/.ssh/id_ed25519.pub) }} -k-k会提示输入 SSH 密码一次性把公钥推送到所有目标机器。这种方式比写循环脚本更规范也便于后续维护。7.3 免密登录和 cron 定时任务配合的注意事项免密登录最常见的一个实际用途就是定时任务。比如每天早上从数据库服务器拉取备份文件到本地30 2 * * * rsync -avz --delete backupuserdb-server:/backup/ /local/backup/因为配置了免密登录rsync 不需要人工干预就能执行。但这里有几个细节定时任务执行时用的用户是谁取决于 crontab 所属用户。不要在 root 的 crontab 里使用某个普通用户的私钥反之亦然。如果私钥设置了 passphrasecron 环境无法输入需要先把私钥添加到ssh-agent再通过SSH_AUTH_SOCK让子进程继承。这个配置比较麻烦建议对自动化用的密钥单独生成一对不设置 passphrase并严格限制其在服务器上的权限。服务器端可以对特定用户做限制比如只允许 rsync 或特定命令防止密钥泄露后造成更大危害。可以在~/.ssh/authorized_keys中给某个公钥前面加command/usr/bin/rsync --server ...前缀这是另一个进阶话题。7.4 撤销免密权限比配置更重要免密登录的生命周期管理最容易被忽视。员工离职、设备重装、密钥泄露都需要及时撤销某个公钥的访问权限。在 Linux 服务器上撤销就是把authorized_keys文件中对应的行删除在 win10 上就是删除administrators_authorized_keys或用户目录下authorized_keys中对应的行。如果你管理的机器多了手动删除太慢可以用脚本批量处理。还是 Ansible 最方便ansible all -m lineinfile -a path/root/.ssh/authorized_keys regexpmy_old_public_key stateabsent此外建议定期审计所有服务器上的authorized_keys文件列出每台机器允许哪些公钥登录。用脚本从所有服务器拉取 authorized_keys 汇总到管理机和 CMDB 里的备案信息比对发现陌生公钥就及时清理。8. 我踩过的一些真实教训写出来帮你避坑8.1 别把公钥追加到错误用户的 authorized_keys有一次我帮同事配置免密他明明粘贴的公钥没问题但始终连不上。后来我发现他把公钥追加到了/home/ubuntu/.ssh/authorized_keys但登录时用的用户名是ec2-user。SSH 认证时使用哪个用户名登录就去哪个用户的家目录下找 authorized_keys这个最基本的逻辑很多人会忽略。排查时先确认登录用户名和公钥所在目录是一一对应的。8.2 换行符和编码问题在 win10 下格外容易踩win10 上记事本默认使用 CRLF 换行和 ANSI 编码。如果你在 win10 上编辑公钥文件然后再手动复制内容到 Linux可能出现公钥末尾带着\r的情况导致服务器验签失败。我在 win10 上查看公钥后复制粘贴到 Linux 的 authorized_keys 时也中过招。解决方法是在 PowerShell 里用cat输出公钥然后复制到 Linux 用 vim 编辑时确认没有多余字符或者直接用上面提到的管道方式让公钥内容原样传入避免手动复制引起的换行符问题。对于authorized_keys文件的末尾换行也要注意。如果上一个公钥后面没有换行下一个公钥紧接着追加两行会连在一起服务端会把它们当做一个无效的公钥导致两个公钥都无法认证。每次追加公钥后用cat -A ~/.ssh/authorized_keys查看文件行尾应显示$确保每行完整独立。8.3 修改 sshd_config 时永远留一条后路这是远程管理服务器最重要的一条建议。编辑sshd_config之前先用sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak做个备份。修改完成之后先执行sudo sshd -t检查语法再systemctl reload sshd或者service sshd reload而不是 restart。虽然两者差异不大但 reload 更平滑不会瞬间断开所有连接。更保险的做法是在修改密码认证和公钥认证相关的配置时先保持当前 SSH 会话不断开另开一个新的终端测试是否能正常连接。如果新连接失败还能从旧会话里改回配置。这个习惯帮我避免过至少三次把自己锁在服务器外面的尴尬局面。8.4 密钥轮换要当成定期任务公钥认证的安全性高度依赖私钥的保密性。长时间不轮换密钥意味着一旦私钥泄露攻击者有足够长的利用时间。建议半年或一年轮换一次尤其在人员变动、设备转交时立即轮换。轮换过程不必太复杂客户端生成新密钥对把新公钥追加到服务器 authorized_keys确认能登录后再删除旧公钥。Ansible 可以同时做这两步降低操作风险。我个人在实际操作中的体会是SSH 免密登录的配置本身并不难难的是把信任边界管理好。凡是配置成免密的机器都意味着你默认信任持有对应私钥的人。所以在服务器端尽量不要把所有机器都向同一把私钥开放可以根据用途拆分成多对密钥工作电脑一把、自动化任务一把、应急备份一把互不干扰。这样即使某一把私钥泄露影响面也可控。希望这篇基于实操的内容能帮你少走一些弯路真正把 win10 和 Linux 之间的连接变成顺手的事。