最早管理云服务器那阵子我都是老老实实输密码登录直到某天登录日志里出现了十几个来自不同IP的Failed password记录我才意识到密码认证虽然是默认方案却也是最容易被暴力破解盯上的短板。后来我把所有服务器切到SSH密钥对登录过程不算复杂但中间踩过几个挺隐蔽的坑。这篇文章就围绕SSH密钥对免密安全登录云服务器来聊聊从原理到落地再到排错和加固尽量把该说的细节都说到位适合刚上手云服务器、想摆脱繁琐密码或者对安全有要求的开发者参考。1. SSH密钥对到底是什么为什么比密码可靠1.1 密码登录的两个致命弱点密码登录本质上是个共享秘密模型客户端拿着密码服务端也存着密码的校验信息两边比对通过就算验证成功。问题在于这个共享秘密太脆弱了。第一密码需要传输无论是登录明文传输还是后续框架里加密传输都存在被截获或中间攻击的风险窗口第二密码在服务端有存储副本一旦服务端配置文件泄露或日志被拖走密码本身就可能暴露。更现实的问题是密码的强度和使用便利性天然矛盾。我见过不少人在云服务器上设了个简单密码理由是反正内部测试用结果第二天日志里就多了一堆扫描记录。1.2 密钥对的非对称设计SSH密钥对用的是非对称加密模型核心是公钥加密、私钥签名。你可以把私钥想象成你口袋里唯一的钥匙公钥则是配给服务器的一把特殊锁。登录验证时服务端拿着公钥生成一个挑战信息客户端用私钥签名响应服务端验签通过才放行。私钥自始至终不出本地机器服务端永远拿不到你的私钥所以不存在服务端泄露导致凭据泄露的问题。这个过程有点像你去健身房刷脸系统只校验你的脸和库里存的模板是否匹配但你本人不需要把整张脸的数据交给前台。这个模型的另一重优势在于公钥可以随便分发管理员可以给一台服务器配多个公钥对应不同开发者的私钥。谁的公钥被删了谁就不能登录颗粒度非常细比换密码再分发省事得多。而且暴力破解在密钥对面前几乎没有意义因为私钥本身是2048位甚至更高位数的随机数枚举空间大到让人绝望。2. 开始之前本地密钥对的生成配置2.1 生成算法怎么选现在打开终端执行ssh-keygen就能生成密钥对但算法别瞎选。目前主流推荐是Ed25519它基于椭圆曲线生成的密钥短、速度快、安全性高OpenSSH近几年的版本默认也倾向推荐这个算法。如果你需要兼容那种特别老的服务器或网络设备退一步选RSA位数建议至少4096位。RSA 2048不是不安全只是业界共识里大家都开始往更高位数或更现代算法迁移。这个步骤里我习惯加上注释参数作用是在公钥末尾标注归属信息方便以后在服务器上管理多个公钥时一眼看出哪个是谁的。命令大概是ssh-keygen -t ed25519 -C deployexample-C就是注释建议写成用途使用者的格式不要写机器名之类容易混淆的信息。如果选择RSA命令是ssh-keygen -t rsa -b 4096 -C admininternal生成过程中会问密钥文件保存路径和passphrase保存路径默认是~/.ssh/id_ed25519一般情况下回车用默认值就行。passphrase这一步很多人会直接留空图省事我的建议是如果是个人电脑且你能保证设备不被别人动留空也能接受如果是公司配发设备或多人共用的跳板机建议设置一个passphrase。passphrase相当于给私钥文件再加一道口令保护即使文件被拷走没有passphrase也没法用。2.2 企业邮箱后缀生成子公钥不用纠结有人会把-C参数写成自己的邮箱生成一个绑定邮箱的密钥这个纯属注释作用不影响登录验证。真正影响登录的是公钥内容本身和服务器上存放的授权文件。如果你确实想用多个不同用途的密钥而不是一个密钥走天下可以在生成时指定不同路径并在后续连接时用-i指定私钥。团队协作场景里我更建议一人一密钥而不是一个密钥多人共用否则人员变动时只能全量替换公钥。还有一个点是权限问题。生成完密钥对后默认私钥权限是600公钥是644这是OpenSSH能接受的安全权限。如果你手抖改过权限比如chmod 777 id_ed25519那SSH客户端会直接拒绝加载这个私钥报错信息通常是Permissions too open。这个细节后面排错部分会展开。3. 把公钥部署到云服务器上3.1 一条命令完成初始化部署生成密钥对之后下一步是让云服务器信任你的公钥。最顺手的方法是ssh-copy-id它会自动把本地的公钥追加到服务器的~/.ssh/authorized_keys文件里并处理好目录权限。命令大致是ssh-copy-id -i ~/.ssh/id_ed25519.pub userserver_ip执行过程中会要求输入一次密码这是整个流程里唯一一次需要密码的地方。这也是正常的因为首次部署时服务器还没有你的公钥只能通过密码认证来建立信任。ssh-copy-id还有一个隐含动作是自动创建~/.ssh目录、修正authorized_keys权限省掉很多手工操作。如果你用的是非默认密钥比如路径是~/.ssh/custom_key记得把-i参数改成实际公钥路径。3.2 手动部署的细节有些云服务器的安全组规则比较严格或者你手上只有网页终端没法直接跑ssh-copy-id那就用手动方式。流程是先在本地查看公钥内容cat ~/.ssh/id_ed25519.pub把输出的整行复制下来登录服务器后在家目录下执行mkdir -p ~/.ssh echo 粘贴公钥内容 ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys这里的关键一步是权限~/.ssh目录必须是700authorized_keys必须是600用户可以放宽到644但目录一旦变成755或更高服务器会认为存在其他用户写入风险直接忽略这个authorized_keys文件。SSH对权限的要求很死板宁可多检查一眼。还有个细节authorized_keys文件里可以写注释比如一行公钥后面加一段备注文字不影响识别。如果你要管理大量公钥建议养成给每行加注释的习惯方便后续审计。3.3 密钥登录验证别急着关密码公钥部署完之后我强烈建议先开一个新终端用密钥登录测试一次确认能顺利进系统再考虑后续操作。验证命令很简单ssh -i ~/.ssh/id_ed25519 userserver_ip如果这条命令能直接登录、不要求输入密码说明密钥认证链路已经通了。这时候再回头改服务器的SSH配置关闭密码登录才算稳当。很多人图省事部署完公钥马上把PasswordAuthentication改成no结果密钥认证某个环节有问题把自己锁在门外只能去云控制台重置密码或通过VNC登录特别折腾。4. 服务端配置安全性与可用性的平衡4.1 修改sshd_config的核心参数密钥登录验证通过后就可以动/etc/ssh/sshd_config了。这个文件是服务端SSH的总开关改动后要重启sshd服务才生效不同发行版的重启命令略有差异CentOS系是systemctl restart sshdUbuntu系可能是systemctl restart ssh。有几项配置值得重点关注PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password第一行PubkeyAuthentication yes是打开密钥认证总开关默认就是yes但如果你拷贝过自定义配置最好显式确认。第二行PasswordAuthentication no就是彻底关闭密码登录暴力破解的入口直接堵死。第三行PermitRootLogin prohibit-password的意思是允许root用密钥登录但不允许root用密码登录。这个配置比PermitRootLogin yes安全得多也比完全禁止root登录更灵活适合日常管理需要直接root操作的同学。实际生产环境里我见过有些团队把PermitRootLogin设成no然后所有操作都通过普通用户加sudo来做。这也是一种思路但对单台服务器来说root密钥登录加sudo方案其实没有本质差别重点是密码登录必须关掉。另外如果你所在地区网络环境比较特殊导致改完SSH配置后连接直接超时千万不要反复盲目试错先通过云厂商的控制台VNC进去看日志一般都能发现问题。4.2 重启服务前必须开的逃生窗口修改sshd_config之后重启sshd服务有个隐形风险如果配置写错比如PermitRootLogin拼写有误sshd可能直接拒绝启动或者启动后拒绝所有连接。稳妥的做法是在改配置之前先开一个持久会话用screen或tmux挂着然后在这个会话里改配置、重启服务即使配置有问题导致后续连接全断这个会话依然活着你还有机会把配置改回来。这个习惯我保持到现在每次操作防火墙或SSH配置都尽量遵守。还有一个容易忽略的点sshd -t可以检查配置语法。改完文件后先执行sshd -t没有任何输出就是语法正常有输出就按提示修正。这一步放在重启之前比重启后发现问题再救火高效得多。4.3 关于UsePAM这个坑有个配置项叫UsePAM默认是yes它负责调用系统的PAM模块做账号验证。当你把PasswordAuthentication设成no后PAM的密码验证路径会被绕开但它依旧会影响部分特殊登录场景比如键盘交互登录。如果你在配置中显式设置UsePAM yes又同时开了ChallengeResponseAuthentication某些系统上密码登录可能仍然生效导致你以为关了密码登录实际上没关。这个现象在CentOS 7和部分Ubuntu老版本上比较明显。排查方法也很简单登录服务器后看sshd日志或配置文件里的生效项确认连接用的到底是哪种认证方式。5. 客户端侧的高级玩法与日常体验5.1 用config文件管理多台服务器密钥对登录最舒服的一点就是配合~/.ssh/config文件实现一条别名走天下。比如我有几台云服务器IP记不住端口也各不相同直接在config里写Host webserver HostName 123.123.123.123 User ubuntu Port 22 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30 ServerAliveCountMax 3 Host db-server HostName 223.223.223.223 User admin Port 2222 IdentityFile ~/.ssh/id_ed25519_db写完之后连接只需要ssh webserver不用再敲完整IP和用户名。ServerAliveInterval和ServerAliveCountMax这两个参数对长连接特别有用它们之间配合可以检测网络断线并自动断开避免SSH卡在那里半天没反应。对于需要频繁跳转的服务器还可以在config里配置ProxyJump跳板机链路上省很多事。5.2 私钥安全与本地多设备同步私钥文件的保管是个敏感话题。我个人不建议把私钥直接传到网盘或聊天工具里因为一旦泄露服务器上所有配过公钥的机器都可能失守。如果你确实需要在多台设备上使用同一个密钥对优先考虑导入密码管理器或者用加密压缩包传输。更重要的是养成定期轮换密钥的习惯比如每半年或一年重新生成密钥对替换服务器上的公钥。顺便提一个ssh-agent的用法设置过passphrase的私钥每次SSH连接都要输入一次passphrase很麻烦。解决办法是把私钥加载到ssh-agenteval $(ssh-agent -s) ssh-add ~/.ssh/id_ed25519这样只需要在加载时输入一次passphrase会话期间后续连接都不再需要重复输入。macOS系统还可以用ssh-add --apple-use-keychain把passphrase存进系统钥匙串体验更顺滑。5.3 免密登录带来的连锁效率提升换成密钥登录之后不只是SSH登录更顺畅整个工作流都会变。scp和sftp都是基于SSH协议的密钥通了之后文件传输同样免密。配合脚本做自动化部署时比如用rsync同步代码到服务器、在CI流水线上执行远程命令密钥对几乎是基础中的基础。没有密钥对这些自动化操作全都卡在密码输入这一步根本没法无人值守。我早期手动部署项目的时候每次发布都要输好几遍密码后来改密钥对配合rsync一条命令直接同步上线效率差距非常大。6. 安全管理与登录审计的一些经验6.1 限制登录用户与来源IP密钥对登录也不能一劳永逸为了安全管理最好再配合用户级别和网络级别的限制。比如sshd_config里的AllowUsers选项可以限定哪些Linux用户可以SSH登录写成AllowUsers admin deploy其余用户一律不允许远程登录相当于缩小了攻击面。更细粒度的控制可以使用/etc/ssh/sshd_config.d里的配置文件覆盖全局配置或者用防火墙规则限制入站SSH来源IP。对于个人服务器如果IP是固定的直接在云安全组里限制只允许自己的IP访问22端口比任何软件层面加固都简单粗暴且有效。6.2 日志监控与暴力破解兜底关闭密码登录后暴力破解的尝试会显著减少但日志里仍然可能出现大量扫描。我习惯用Fail2ban这类工具做兜底它监控系统认证日志一旦发现某个IP多次认证失败就自动加入临时黑名单。这里有个小细节如果你已经禁用了密码登录Fail2ban主要拦截的是那些尝试密钥认证失败的人这类流量往往来自批量扫描器拦截逻辑依然有效。当然云厂商自带的安全组和Web应用防火墙也可以配合起来但别过度依赖单一层面安全永远是多层防护。6.3 审计公钥定期检查authorized_keys很多人配置好密钥后就不管了过了一年半载authorized_keys文件里堆了一堆公钥哪些还在用、哪些已离职根本搞不清楚。建议定期审计服务器上的~/.ssh/authorized_keys一行一行确认公钥对应的使用者不需要的立刻删除。如果你有几十台服务器可以写个脚本统一收集各服务器公钥清单做比对。公钥审计这件事虽然基础但很多安全事故就是从一条遗漏的信任关系开始的。7. 常见问题与排错实录7.1 登录时报错Permission denied (publickey)这是最常见的错误意思是没有可用的公钥完成认证。排查路径按顺序走先用ssh -vvv userserver_ip看详细日志重点看客户端加载了哪个私钥、服务器端接受了哪个公钥。如果服务器端日志显示找不到匹配的authorized_keys就去服务器上检查那个用户的.ssh目录权限和authorized_keys文件内容。我遇到过几次都是因为公钥文件里内容被我粘贴时加了个多余换行或空格导致密钥解析失败。另外还要确认你连接时用的用户名对不对。密钥验证是和具体用户绑定的A用户部署了公钥你拿B用户去连自然被拒。7.2 服务端配置无误但仍需密码登录很多时候你以为修改了sshd_config实际生效的还是默认配置。Linux的SSH配置有个include机制有的发行版默认会加载/etc/ssh/sshd_config.d/目录下其他文件如果你在sshd_config里显式改了一个参数又在子配置文件里改同一个参数生效规则可能会和你预期不一致。建议用sshd -T查看当前实际生效的配置值这比肉眼判断文件靠谱得多。7.3 云服务器重启后密钥登录失效这种情况多半不是你配置的问题而是云服务器的cloud-init重新初始化了用户配置。比如某些云镜像会在首次启动时执行初始化脚本重置默认用户的authorized_keys或锁定用户的某些远程登录属性。排查时先看服务器是否还在正常运行再比对authorized_keys文件内容和预期是否一致。如果不是云厂商初始化导致的问题就去查看/var/log/auth.log或/var/log/secure确认SSH服务本身有没有启动失败或监听异常。8. 从密码到密钥一次值得做的迁移把云服务器从密码登录切换到密钥登录这个动作本身不大但它带来的安全收益和操作体验提升是立竿见影的。我现在接手任何一台新服务器第一件事就是把密钥部署好、密码登录关掉之后的所有自动化工具都在这条信任链上跑。换了几十台机器下来最深刻的体会是密钥对登录的关键不在于那几行命令而在于理解信任模型和权限细节。只要把公钥发给服务器、私钥留在本地这个逻辑吃透后续遇到再多变体问题都有排查方向。如果你还没迁移我建议按文章的步骤一步步来不要急着把密码登录一步关死。先部署公钥、验证通过、再关密码这个顺序能帮你避开九成的新手坑。等这套流程走顺了再配合config别名、ssh-agent、Fail2ban这类进阶工具日常管理服务器的体验会比以前舒服很多。
阅读完成 · 觉得有帮助?