SSH免密登录实战:密钥生成、公钥部署与权限排查全指南 1. SSH免密登录到底免的是什么做运维和开发的同学几乎每天都要跟远程服务器打交道。登录服务器最烦的事情是什么就是每次都要输密码尤其是现在密码策略越来越严动不动要求十六位混大小写带特殊字符一天要敲几十遍谁受得了。SSH免密登录解决的就是这个问题——生成一对密钥把公钥放到服务器上之后每次ssh过去直接进不用再敲密码。这套机制不仅能让你少打几十遍密码更重要的是它消除了密码被暴力破解的可能性安全性反而更高。我是做服务端开发的平时要管理好几台服务器有测试环境也有生产环境。最早我也是老老实实输密码后来有一次要批量执行几十台机器的命令密码输得我手快断了才下定决心把免密登录一次性搞定。弄完之后回头看整个过程其实不复杂核心就三步生成密钥对、把公钥放到服务器、配置并验证登录。但这三步里藏了不少细节比如说密钥类型选哪种、权限该给多少、为什么明明公钥放上去了还是报错这些坑我全踩过。这篇文章就从零开始把每个环节掰开揉碎了讲清楚保证你看完能一次配通。这套技术适合谁来用方向其实很广。如果你是刚入行的运维或者后端开发配置免密登录是基本功如果你自己买了一台云服务器跑个人项目每天要登上去看日志、重启服务配了免密之后体验会舒服很多哪怕你只在本地虚拟机里折腾Linux环境这套流程也完全一致。不管你的场景是哪种原理和操作都是同一套学会了一次搞定后面全复用。2. 从零创建你的第一对密钥2.1 ssh-keygen的完整参数解读生成密钥用的命令是ssh-keygen这个命令在Linux、macOS、Windows装了OpenSSH客户端上都是标配不需要额外安装。我先说最基础的用法一行命令ssh-keygen -t ed25519 -C my-server-key -f ~/.ssh/myserver乍一看三个参数有点懵挨个解释一下-t指定密钥类型。你可以理解为“加密算法”常见的有ed25519、rsa、ecdsa。我后面会专门讲类型怎么选这里先记住ed25519是当前推荐选项。-C注释。这个参数就是给密钥加一个备注信息通常写自己的邮箱或者用途比如devexample.com或者prod-backup-key。它的作用是让你在管理多把密钥的时候能分清哪把是哪把不影响功能但强烈建议写上。-f指定生成的文件名。如果不加这个参数ssh-keygen会默认生成~/.ssh/id_ed25519或者~/.ssh/id_rsa。我习惯明确指定文件名因为管理多把密钥时一一对应更清晰。执行这条命令之后终端会问你Enter passphrase (empty for no passphrase)这里是在问你给不给私钥加一个额外的保护口令。关于passphrase我多说几句它不等于服务器密码而是对你私钥文件本身的一层加密。如果设置了每次使用这把私钥时都要输入这个口令相当于在免密的基础上加了一道保险。如果留空私钥文件就是裸奔状态谁拿到了文件谁就能用它登录。那到底设不设我个人建议笔记本电脑这种个人设备可以不设图的就是开机即用如果是共享机器或者需要长期保存的备份密钥建议设上配合ssh-agent可以达到“设备上只输入一次、后续自动用”的效果后面我专门讲。2.2 密钥类型与位数怎么选密钥类型这个问题我在踩过坑之后才真正重视起来。很多老教程还在推荐用RSA而且强调位数要4096这个说法不算错但眼下有了更好的选择。先给一个对照表方便你做决策密钥类型算法特点密钥长度安全性兼容性推荐度ed25519现代椭圆曲线算法速度快签名短固定256位高OpenSSH 6.52014年后首选rsa经典算法生态兼容性最广2048/4096位高4096更强几乎所有SSH版本兼容场景首选ecdsa椭圆曲线算法256/384/521位中部分旧设备支持不佳不常用dsa已废弃的老算法1024位低现代系统已默认禁用不要用我的实际建议是全新的机器、全新环境直接用ed25519。原因很简单它的签名速度更快私钥文件更短安全性在同等比特数下比RSA更好。OpenSSH从6.5版本开始就支持了现在你手头的服务器如果系统版本不是那种十年不更新的古董基本都能用。但有一种情况要换成RSA你需要在一些老旧设备上做免密比如某些嵌入式设备、老版本的交换机、特定型号的存储设备它们的SSH实现可能只认RSA。这时候老老实实用RSA 4096别纠结兼容性优先。我之前就遇到过一台老机器只支持RSAed25519的公钥送上去完全不认后来换了RSA才搞定。还有一个小提示不管是哪种类型ssh-keygen默认生成的私钥是没有注释的但公钥文件里那串字符串末尾会带上你指定的-C内容。公钥文件里那串长长的ssh-ed25519 AAAA...就是公钥本体它长这样ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK5xUxVdBQ9r3uR2Pv8RgXmysqlLJbByW8NQ7H1Uuqz my-server-key公钥是可以随便给人看的密钥对的安全性完全取决于私钥的保密程度这两者的关系后面讲原理时会说清楚。2.3 从零实操完整生成流程空谈没用直接上一段完整实操。假设我现在要给一台新服务器配置免密服务器IP是192.0.2.10登录用户是dev端口是默认的22。第一步在本机生成密钥对ssh-keygen -t ed25519 -C devmyhost -f ~/.ssh/dev_server终端提示输入passphrase我这边个人笔记本选留空直接回车两次。此时~/.ssh/目录下会多出两个文件dev_server私钥这个是核心机密绝不能外传。dev_server.pub公钥可以放心部署到服务器上。验证一下文件是否生成ls -l ~/.ssh/dev_server*能看到两个文件权限分别为-rw-------私钥600和-rw-r--r--公钥644。如果私钥权限不对比如是644后面SSH连接时会直接报错所以第一次生成完就养成检查权限的习惯。有人会问我看到的教程标题里常写“SSH免密登录”为什么有些老教程里是“RSA免密登录”因为RSA是当年最主流的算法很多写教程的人沿用了这个说法。算法本身一直在演进你只要理解“创建密钥对”的本质就行具体用什么算法ed25519还是rsa按我上面的表格选。到这里密钥对已经握在手里了。下一步就是怎么把公钥放到服务器上让它认你。3. 把公钥送上服务器3.1 最省事的方式用ssh-copy-id一键推送生成密钥之后你需要把.pub公钥文件的内容追加到服务器上对应用户的~/.ssh/authorized_keys文件里。这是一个专门存放“允许谁免密登录”的清单文件一行一个公钥。最省事的工具是ssh-copy-id它把这个过程封装成了一步命令。以刚才生成的dev_server密钥为例ssh-copy-id -i ~/.ssh/dev_server.pub dev192.0.2.10注意-i后面跟的是公钥文件路径不是私钥。执行后它会提示你输入服务器密码这是最后一次输密码输入正确后它会自动创建~/.ssh目录、写入authorized_keys、设置好权限。整个过程其实做了好几件事我拆开说一下连接服务器的22端口用密码完成身份验证。检查当前用户的家目录下是否存在.ssh目录没有就自动创建。把目标公钥追加到~/.ssh/authorized_keys文件末尾。顺手修正权限.ssh目录设为700authorized_keys文件设为600。如果你刚才没用-f指定文件名而是生成了默认的id_ed25519那可以直接执行ssh-copy-id dev192.0.2.10它默认找~/.ssh/id_*.pub这种形式的公钥文件。如果你的服务器SSH端口不是22比如是2222需要加-p参数ssh-copy-id -i ~/.ssh/dev_server.pub -p 2222 dev192.0.2.10这一步做完理论上免密配置已经完成了。但我不急着测先讲手动方式因为你会遇到各种环境有时候ssh-copy-id不可用比如Windows原生终端没有这个命令或者目标机器被限制了密码登录就得手动来。3.2 手动部署方式与权限校准手动部署的原理和ssh-copy-id完全一样就是自己完成那几步。第一步先看本机公钥的内容用cat查看cat ~/.ssh/dev_server.pub输出是一长串全选复制。第二步登录服务器ssh dev192.0.2.10第三步在服务器上执行以下命令mkdir -p ~/.ssh chmod 700 ~/.ssh echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK5xUxVdBQ9r3uR2Pv8RgXmysqlLJbByW8NQ7H1Uuqz devmyhost ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys这里的关键点有两个一是echo必须用追加而不是覆盖。如果这台服务器上authorized_keys已经存在其他人的公钥会直接清空文件等于把别人的免密配置全毁了。我见过不止一次这种事故多个运维共用一台机器新来的用覆盖结果所有人的钥匙都失效了。二是权限必须精确匹配。服务器上的sshd服务对权限非常敏感它发现~/.ssh目录权限是755、或者authorized_keys是644会直接拒绝用这些公钥完成认证日志里会提示“bad permissions”。原因很直接如果目录和文件能让其他用户写入或修改那公钥文件就可能被篡改认证也就不安全了。3.3 为什么权限一定要用600和700我单独把权限拎出来说是因为这是我踩坑最多的地方。很多人包括当年的我在服务器上把公钥粘贴进authorized_keys后就完事了结果一连接就报Permission denied (publickey)排查半天最后发现就是文件权限多了一个可读权限。SSH的校验逻辑是这样的服务器端的sshd在启动时会读取authorized_keys但它不是无脑信任这个文件而是在读取前检查文件的属主、权限和路径安全性。具体要求如下登录用户的家目录必须是当前用户拥有且不能是组可写或其他人可写。~/.ssh目录的属主必须是当前用户权限一般不能超过700即drwx------。~/.ssh/authorized_keys的属主必须是当前用户权限不能超过600即-rw-------。组可写、其他用户可读都不行。因为SSH安全模型假设同一台机器上的其他用户也可能靠本地漏洞读取文件如果你把授权文件的权限放得太开就相当于给了别人一把可复制的钥匙。实际排查的时候可以一条命令检查关键文件ls -la ~ | grep .ssh ls -la ~/.ssh输出应该长这样drwx------ 2 dev dev 4096 Jan 15 10:00 .ssh -rw------- 1 dev dev 400 Jan 15 10:01 authorized_keys如果你发现权限不对用chmod纠正chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys这两条命令几乎每个SSH免密教程里都会有但其背后的原因很少有人说透。明白原理之后遇到类似问题你会更快地判断根源而不是漫无目的地乱试。4. 免密登录的配置与验证4.1 用~/.ssh/config管理多台服务器公钥部署好之后基础免密登录已经可以用了。直接连一下试试ssh dev192.0.2.10如果一切正常你会直接进入服务器的shell界面没有任何密码提示。如果你的服务器端口不是22需要显式指定ssh -p 2222 dev192.0.2.10到这里“免密登录”已经从目标变成了现实。但实际工作中我们往往不只管理一台服务器如果每次都要敲ssh -p 2222 dev192.0.2.10 -i ~/.ssh/dev_server这种长命令体验依然不好。这时候就要用~/.ssh/config文件给每台服务器配置一个短别名。配置文件在~/.ssh/config没有就新建一个。写入内容如下Host myserver HostName 192.0.2.10 User dev Port 2222 IdentityFile ~/.ssh/dev_server IdentitiesOnly yes逐行解释一下Host你给这台服务器起的别名连的时候直接ssh myserver。HostName真实域名或IP地址。User登录用户名。PortSSH端口默认是22可不写。IdentityFile指向这把密钥对应的私钥文件路径。IdentitiesOnly yes只使用这里指定的密钥文件不默认尝试~/.ssh下的所有私钥。这一行很重要否则本机密钥多了以后SSH会依次尝试所有密钥一旦某把密钥在服务器上匹配失败认证流程会被拉长甚至触发服务器的防护策略。配置好之后连接就变成了ssh myserver一个单词搞定。如果你有几十台服务器这个方法能把你的日常操作简化到极致。而且不用记端口、IP、用户名全部沉淀在配置文件里换电脑的时候拷贝~/.ssh/config和~/.ssh/目录就能无缝迁移。4.2 验证登录并查看调试信息配置完免密后最重要的不是“能连上”而是“稳定地能连上”。我强烈建议你在第一时间用调试模式做一次完整验证ssh -v myserver-v是verbose调试模式它会打印整个连接过程中的日志。你会看到类似这样的关键阶段Offering public key: /home/user/.ssh/dev_server ED25519 SHA256:xxx——客户端在向服务器出示这把公钥。Server accepts key: ed25519 SHA256:xxx——服务器端确认接受这把公钥。Authenticated to 192.0.2.10 ([192.0.2.10]:2222)——认证成功。如果中途卡住了输出停在某个阶段比如一直在Authentications that can continue: publickey附近打转说明密钥对没被服务器接受。这时候就进入下一步的排查流程。一个容易忽略的细节是验证时如果本机~/.ssh/下有多个密钥SSH客户端默认会按顺序尝试所有私钥。如果某个私钥没有对应密码直接跳过输出里会有一段load pubkey之类的记录。用IdentitiesOnly yes配合config文件可以避免这种多余的探索流程因为指定了就用那一把。4.3 从密码到免密的过渡期策略有一种常见的场景你的服务器现在还开着密码认证你也已经配好了免密但不敢直接把密码登录关掉怕哪天密钥出问题把自己锁在外面。我建议采用过渡期策略分两步走。第一步保持现状免密和密码同时可用观察一段时间。这期间你可以确认免密登录在各场景下都稳定比如从公司网络登录、从家里登录、从手机终端登录都没问题。第二步确认无误后再考虑关掉密码登录。具体操作是修改服务器上的SSH配置文件一般路径是/etc/ssh/sshd_config把这几项改掉PasswordAuthentication no ChallengeResponseAuthentication no然后重启SSH服务sudo systemctl restart sshd注意PasswordAuthentication和ChallengeResponseAuthentication都要关掉只关第一个是不够的。有些系统默认开启ChallengeResponseAuthentication它一开服务器依然会提示输入密码。改完之后先用一个新终端窗口测试免密是否正常确认能免密进入后再小心地关闭当前的连接。千万不要全部关完才测试一旦密钥有问题那就真的进不去了。过渡期实在是没把握的话可以再留一个后门比如服务器厂商的网页终端VNC或云控制台。万一密钥失效还能通过网页终端进去修复。很多人吃过“改完配置顺手关掉密码结果密钥对不上彻底锁死”的亏这属于运维界最痛的几个失误之一宁可多留一手不要赌一把。5. 常见问题与排查实录5.1 常见的几种Permission denied免密配置完成后最常遇到的错误提示就那几类。我把它们列成一张速查表方便你直接对照排查错误现象可能原因处理方向Permission denied (publickey,password)公钥未正确写入或私钥未匹配重新核对authorized_keys内容Permission denied (publickey)公钥认证失败且服务器禁用了密码检查密钥文件、权限或先临时开启密码登录Bad owner or permissions on /home/user/.ssh.ssh目录或文件权限过宽chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keysLoad key xxx: incorrect passphrase supplied私钥设置了口令但输入有误重新输入正确的passphrase或改用ssh-agent管理Server refused our key服务器未接受这把公钥检查公钥是否追加到了正确用户的authorized_keysToo many authentication failures本机密钥太多逐一尝试时失败次数过多用IdentitiesOnly yes或-i指定具体密钥我在实际工作中见过最多的情况就是第二种publickey方式失败。原因通常有几个方向一是公钥文件被用覆盖写坏了二是行尾有换行符问题从Windows粘贴到Linux时容易带出\r三是服务器上配置了AuthorizedKeysFile指向了别的文件路径。第三种情况比较隐蔽需要看服务器配置grep -i authorizedkeys /etc/ssh/sshd_config如果输出的是AuthorizedKeysFile .ssh/authorized_keys那是默认值没问题。如果指向了/etc/ssh/authorized_keys或者别的路径说明公钥要写到指定位置而不是用户目录下。5.2 排查步骤一步步定位问题遇到连接失败不要慌按照下面这个顺序排查90%的问题五分钟内能定位第一步确认基线你用的是不是正确用户和IP。很多人以为自己连的是服务器A其实config里写的是服务器B的地址连接失败当然正常。先执行不带简化的原始命令比如ssh -i ~/.ssh/dev_server -p 2222 dev192.0.2.10如果原始命令失败而config方式成功那是config配置有误如果两者都失败才进入下一步。第二步确认密钥文件路径本机检查~/.ssh/下是否有dev_server和dev_server.pub私钥权限是否为600。检查命令我一般这么写ls -la ~/.ssh/ ssh-keygen -y -f ~/.ssh/dev_serverssh-keygen -y -f可以从私钥反推出公钥内容如果执行报错说明私钥文件本身有问题如果输出的内容和你的.pub文件不一致说明私钥和公钥可能不配对。这种“文件内容对不上”的问题很少见但一旦发生了很难想到值得排查。第三步确认服务器端状态用密码登录或从其他已有免密的机器登录服务器检查authorized_keys里有没有你的公钥grep ssh-ed25519 ~/.ssh/authorized_keys不仅要确认存在还要确认内容和你本机.pub文件一致。最容易出错的是复制时漏字或者多了空格严谨比对一下。第四步确认服务配置和日志如果前几步都没问题就看服务器SSH服务的运行状态和日志。日志位置一般是/var/log/auth.logDebian系或/var/log/secureRHEL系搜关键字grep Failed /var/log/secure | tail -20如果你常遇到这台服务器重启后免密失效的情况那要考虑是不是authorized_keys写入的路径和用户实际使用的不一致或者服务器的pam配置里有特殊限制。这种情况一般用sshd -T查看实际生效的配置sudo sshd -T | grep authorizedkeysfile这招能直接看到当前有效路径排查一次少走很多弯路。5.3 踩过几次坑之后的一些真心建议整个流程走完把经验沉淀下来有几点我真觉得值得单独说第一密钥文件要起好名字别全叫id_rsa。当你机器上积攒到三五个密钥之后再回头看那些凭文件名认不出的密钥会非常痛苦。我在命名上吃过亏一开始全是默认id_rsa后来一台服务器对应一个密钥全都混在一起连调试都不知道试的是哪把。现在我的命名规范是用途_环境比如aliyun_prod_key、home_nas_key配合config里的Host别名一目了然。第二多台服务器的密钥尽量分开生成尽量别“一把钥匙开所有锁”。虽然技术上完全可以把同一份公钥复制到十几台服务器一台设备一个密钥的做法的确是管理复杂度更高但安全上要清楚一把密钥的私钥泄露所有服务器都会被攻破。我的做法是测试环境共用的服务器可以共用一把生产环境每台独立密钥方便单独吊销。第三ssh-agent是个好东西但大部分人没用明白。如果你在生成密钥时设置了passphrase可以把它加入agent让这个口令只输一次之后在同一会话内不再询问eval $(ssh-agent -s) ssh-add ~/.ssh/dev_serverssh-add -l可以列出当前加载的所有密钥ssh-add -D清空。注意agent只对当前会话有效重启电脑后重新加载一次。配合~/.ssh/config里的AddKeysToAgent yes可以在每次连接时自动把密钥加进agent体验会顺滑很多。第四多设备同步密钥时要小心。如果你换了新电脑只需要把原来那台设备的~/.ssh目录整体拷贝过去尤其是私钥和config文件。拷贝时注意用scp或者rsync最好用自己的免密通道传输前校验一下源文件权限。切勿通过U盘在公共电脑上拷贝私钥那等于把家门钥匙丢在街上。第五备份私钥并不意味着把它存到网盘或聊天记录里。如果你真的担心丢失最稳妥的办法是本地加密压缩后放进加密盘或者干脆多生成一把备用密钥放在离线存储设备中。凡事多做一手准备别哪天服务器没变、你的电脑先坏了私钥就再也找不回来了。最后再分享一个非常实用的小习惯每次配完一台服务器的免密我都会顺手在本地执行一次ssh -v的完整连接确认输出里的Authenticated to出现后再关闭终端。这个习惯看似多了一步但正因为每次配完都立即验证我很少因为“配完之后不知道到底成没成”而来回折腾。你以后配得多了会发现免密登录这件事真正花时间的不是敲那几行命令而是排查那些藏在权限、路径、复制粘贴里的隐形问题。把这篇文章里的检查逻辑和排查顺序装进脑子里以后再碰到任何SSH连接问题你都不会一头雾水了。