
前阵子帮一位圈里的朋友排查远程登录问题他手上有几台云服务器天天靠输入密码登录密码还五花八门每次都是先翻记事本再复制粘贴偶尔碰上临时换端口、改密码一折腾就是半个上午。我问他为什么不配置SSH密钥做免密登录他犹豫了半天“听说过但总感觉那东西很难怕把服务器搞坏。”后来我坐在他电脑前从生成密钥到彻底关掉密码登录前后不到十分钟他当场直呼“怎么早没弄”。其实SSH密钥免密登录本质就是把“你知不知道密码”变成了“你有没有匹配的钥匙”。这个思路一旦转过来剩下的全是机械操作。这篇博文就围绕“使用SSH密钥实现免密登录远程服务器”这件事把原理、实操、踩坑和加固全部分享出来适合刚接触服务器的同学、被密码折磨的运维新人以及想把手头一堆机器统一管理的老手。1. 为什么不用密码认证思路与密钥登录的本质优势1.1 从“密码认证”到“密钥认证”到底变了什么传统登录方式是密码认证。你输入用户名和密码远程服务器比对数据库里存的哈希值一致就放你进。这种方式有个天然缺陷密码会被猜测、会被钓鱼、会被撞库而且只要复杂度一提高人脑就记不住最后都落到各种笔记软件里等于变相增加泄露面。密钥认证走的是另一套逻辑。免密登录靠的是一对公私钥私钥留在你自己的电脑里公钥提前部署到服务器上。每次连接时服务器给你出一个随机挑战你用私钥签名服务器拿之前存好的公钥验签。验证通过直接建立会话。这里有个关键点整个过程私钥从不离开你的电脑也不在网络中传输。所以它比密码更抗截获、抗暴力破解。你不需要记住任何字符串只需要保证私钥文件安全这就够了。从密码到密钥改变的不仅仅是安全强度更是工作流的自由度。密码登录天然是“人肉操作”没法优雅地塞进定时任务、自动化脚本和持续集成工具链。而密钥登录一旦配好脚本SCP、rsync、Git推送都能一条命令跑完不需要任何人工干预。这也是我每次搭建服务器第一个做的配置。1.2 用生活类比理解公私钥体系我经常用一个比喻公钥就像一把挂锁你可以把这把锁发给任何想找你的人私钥就是那把开锁的钥匙全世界只有你自己有。别人给你发一个保险柜上面挂着你那把锁他需要确认你是不是钥匙持有人于是让你当场把锁打开你掏出私钥一旋锁开了对方就知道“对钥匙你真拿着。”而这个开锁动作不像密码那样可以被“重复回答”利用因为每次保险柜都是新的挑战内容都不同。这就是为什么即使有人拿到了公钥他也没法逆推出私钥更没法假装是你。因为公钥加密的内容用私钥才能解开私钥签名的内容也只有匹配的公钥能验证。1.3 选择什么算法Ed25519还是RSA很多教程默认让你ssh-keygen一路回车生成的就是RSA 3072位密钥。这个选择不算错但放到今天我更推荐Ed25519。用我自己的经验对比一下对比项RSAEd25519密钥长度3072/4096位长生成慢256位短生成极快签名速度慢快抗暴力破解能力很长密钥下依然强设计上即高性能且安全兼容性所有平台、所有老版本都认识OpenSSH 6.5几乎全覆盖私钥文件大小较大很小好备份实操中我的建议是新环境一律用Ed25519。如果你管理的服务器上有比较老的SSH版本或者某些内部平台只认RSA再单独生成一把RSA 4096备用。现在教程满天飞但没人告诉你兼容性这层坑等你遇到旧设备连不上才想到配置早已散落各处回头改起来更费劲。2. 动手前的准备环境检查与基础配置2.1 先检查这三样东西配置SSH免密之前我不建议直接甩命令先花两分钟确认环境和前提能避免后面一半的问题。第一确认你是用OpenSSH客户端。Linux和macOS自带Windows 10以上的系统自带OpenSSH无需额外安装。但老系统或者某些精简版Windows可能需要开启“可选功能”里的OpenSSH客户端。第二确认远端服务器SSH服务运行正常且端口可达。命令很简单# 示例某台测试服务器端口2222 ssh -p 2222 userexample-server.com如果能正常输入密码登录说明网络链路和服务都没问题后面专注配置就好。如果这一步都连不上先排查防火墙、安全组和SSH服务状态别急着配密钥。第三确认本地用户目录的权限是否合理。很多Windows和macOS用户之前手动改过用户目录权限这会干扰后续ssh-keygen和ssh-copy-id的正常工作。最简单的办法先备份重要东西然后保证~/.ssh目录存在且权限可控就行。2.2 不同客户端环境下的坑位提醒Windows原生PowerShell里的OpenSSH配置文件和Linux/macOS完全一致但有几个小地方不同一是路径默认是C:\Users\你的用户名.ssh不要跟在Linux上看到的/root/.ssh搞混二是PowerShell里的ssh-copy-id不一定自带需要手写命令或者用scp复制三是如果你的Windows用户名为中文某些老版本会出编码问题极少数情况会导致连接异常重命名用户目录太折腾不如直接用WSL。macOS和Linux则顺畅很多。macOS上注意如果开启了FileVault和iCloud钥匙串私钥文件的权限偶尔会被同步搞得怪怪的后面排查权限问题时多留个心眼。我个人的经验是如果你日常工作本身依赖Linux环境直接装个WSL再操作后续脚本、自动化、免密管理都顺手得多。WSL里生成的密钥可以被Windows下的VSCode Remote引用也能被PowerShell访问同名文件的绝对路径跨工具使用会很方便。2.3 服务器端环境准备要点服务器端需要确认sshd_config允许公钥认证。绝大多数发行版默认是允许的但有些安全加固过的模板会主动关闭它。你现在不用记所有配置项只需要知道一个命令# 在服务器上执行 grep -E ^PubkeyAuthentication|^PasswordAuthentication /etc/ssh/sshd_config如果看到PubkeyAuthentication后面跟的是no就需要改成yes然后重启sshd服务。这一步很多人漏掉导致本地配了一堆密钥服务器根本不认账。另外注意如果服务器开了SELinux常见于CentOS/RHEL系新生成的~/.ssh目录如果没有正确的上下文标签SSH服务会拒绝读取authorized_keys。我用过的办法是在你的家目录下正常执行mkdir和chmodSELinux一般会自动继承家目录的user_home_t类型但如果你手动用root绕过某些环节就可能把目录上下文弄成default_t那种情况建议用restorecon -R -v ~/.ssh修复。3. 生成密钥与部署公钥半小时跑通免密登录3.1 生成一对新密钥在本地客户端执行ssh-keygen -t ed25519 -C my-main-work-laptop-C参数是你的备注建议写清楚用途和设备名。以后万一你积累了一堆公钥在服务器上管理时看注释就知道是哪台机器的钥匙清理起来不费劲。执行之后提示你选择保存位置我习惯保持默认也就是~/.ssh/id_ed25519。如果你已经有同名文件回车前要确认是不是自己常用的别把现有密钥覆盖了。接下来它会问你Enter passphrase这一段很多人直接留空。留空意味着免密登录过程中不需要再输入任何密码方便是方便但私钥文件一旦被拷走等于把门钥匙丢在别人家门口。我更推荐一个折中方案设置一个短语密码passphrase然后配合ssh-agent或系统钥匙串来记住它。这样每天第一次登录输一次后面全程免密安全性还上了一个台阶。一句话说清楚就是密码登录时钥匙本身就锁在保险柜里但保险柜本身还可以再加一道锁设置passphrase等于给私钥文件加个密码保护。3.2 将公钥部署到服务器生成之后本地会同时出现两个文件id_ed25519私钥和id_ed25519.pub公钥。私钥自己藏着公钥可以放手印发。分发公钥的最快捷方式是用命令行工具ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 userexample-server.com这一步会让你输入一次密码因为服务器还不认识你的公钥只能靠密码来验证你有没有登录权限。执行成功之后它会自动把公钥内容追加到服务器的~/.ssh/authorized_keys文件里。如果你的电脑没有ssh-copy-id常见于某些精简Windows环境可以用另一种办法cat ~/.ssh/id_ed25519.pub | ssh -p 2222 userexample-server.com mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys我看到很多人卡在这一步不是没mkdir就是权限不对。这个命令之所以要把chmod和mkdir绑在一起是因为authorized_keys文件权限太宽松时SSH服务会拒绝信任它这是安全机制而不是bug。3.3 测试免密登录与初次失败自救部署好公钥之后断开当前连接重新登录ssh -p 2222 userexample-server.com这时候你会发现自己直接进去了没有任何密码提示。顺利的情况下整段配置就算跑通了。但如果还是提示输入密码不要急着怀疑人生。先按顺序检查三件事ls -la ~/.ssh/ cat ~/.ssh/authorized_keys ls -ld ~/.ssh第一眼看目录是否存在第二眼看一下公钥是不是真的写进去了第三眼看权限。常见的情况是~/.ssh权限是775authorized_keys是664这种我见过太多次。直接把权限拨正chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys然后再连一次。大多数情况下权限修正之后立刻就通了。剩下少部分问题多半是SELinux上下文或服务器端配置开关后面常见问题部分我会专门写。4. 免密登录的进阶玩法多服务器、自动化与安全加固4.1 用config文件统一管理多台服务器当你手里的机器达到两台以上再用完整命令去连就很烦。我习惯在~/.ssh/config文件里给每一台服务器起个“外号”Host deploy-server HostName example-server.com User deploy Port 2222 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30 Host dev-box HostName 192.168.1.50 User dev IdentityFile ~/.ssh/id_rsa_dev配置好之后连接命令直接变成ssh deploy-server域名、端口、用户名、密钥文件全部自动匹配。这个配置文件还有一个隐藏优势你可以给同一台机器准备多个别名用于不同身份登录不同账号避免频繁手动指定参数。我在实际项目中一台跳板机经常要转发到内网很多节点这时候配合ProxyJump的配置会很舒服Host bastion HostName bastion.example.com User admin Host app-* HostName %h User app ProxyJump bastion这样往内网任何一台机器SSH都自动先过跳板机免密逐层穿透效率高且不暴露内网地址。这已经算是密钥免密登录在真实工作中的高阶形态了。4.2 免密登录解锁的自动化场景密钥配好之后最大的收益不是省了密码而是把许多“人肉操作”变成脚本自动运行。比如我最常用的备份脚本片段rsync -avz --delete /data/web/ backup-server:/backup/web/没有密钥之前这个命令会停在密码输入界面上完全没法无人值守。配上免密之后cron定时执行、跌倒重跑、断点续传全部顺理成章。Git部署也是同理。很多团队把代码从裸仓库拉到服务器时需要ssh访问另一台机器如果每次都要输密码CI系统根本跑不起来。用部署专用密钥配好之后git用户、CI机器人、容器容器里读代码都畅通无阻。这里我补充一个建议给自动化任务配置一套独立的密钥对不要复用个人日常登录的密钥。我见过有人把开发电脑的私钥直接传给Jenkins服务器后来电脑丢了还得连夜全军换钥非常痛苦。独立密钥的另一个好处是撤销某个任务时只需要从目标机上删掉对应公钥不影响其他使用场景。4.3 安全加固彻底关闭密码登录当所有人和所有自动化场景都切到密钥方式后最后一个关键动作是关闭服务器的密码登录。编辑/etc/ssh/sshd_configPasswordAuthentication no ChallengeResponseAuthentication no UsePAM no一句一句说清楚PasswordAuthentication no不再接受普通密码登录。ChallengeResponseAuthentication no禁用密码类的挑战响应认证。UsePAM no有时候不关掉它密码认证仍会通过PAM模块生效导致上面两个配置失效所以一并关掉。修改之前务必确保你当前会话用的就是密钥登录并且已经把本机公钥放到了服务器的root或你常用账号里。否则配置完一重启你自己也会被挡在外面。稳妥的做法是开一个新终端测试用密钥登录正常后再动手改配置。这是我强烈建议遵守的保命顺序。改完保存重启SSH服务sudo systemctl restart sshd之后再用密码登录任意账号都会被直接拒绝从源头上断掉暴力破解和弱口令风险。日志里那些循环扫描、不断尝试密码的机器人自然就不再有入局可能。5. 常见问题与排查技巧实录5.1 高频问题的快速定位思路我总结了一下自己带过的人踩过最多的一批坑基本逃不开下面这六类第一权限问题。不是这个文件600、那个目录700就是家目录本身被写成了777这类错误占了大头。排查命令就是层层ls -la看一眼就能定位。第二authorized_keys写错了。例如用vim手工编辑时带了换行符、粘贴过程中把多个公钥揉成一行或者复制时漏了最后的注释信息。检查手段就是wc -l ~/.ssh/authorized_keys对比你发过多少把密钥行数对不上就是有问题。第三SELinux上下文错误。CentOS系常见。用ls -dZ ~/.ssh查看上下文正常应该显示类似unconfined_u:object_r:user_home_t:s0如果显示var_t或default_t执行restorecon -R -v ~/.ssh修复。第四sshd_config关闭了公钥认证。有些云平台市场镜像默认只允许密钥有些则相反完全关闭了PubkeyAuthentication。用grep查一下便知。第五连接端口或用户名写错。测试时用ssh -v或ssh -vvv打印调试信息看到“Connection refused”是端口或地址问题看到“Permission denied (publickey,password)”是认证问题两者方向完全不同。第六服务器时间偏差。公钥认证有防重放机制如果客户端和服务器时间差太远签名验证会失败即使密钥完全正确也登录不上。用date对比两端的系统时间差异超过几分钟就值得警惕。下面给一个排查顺序表我称之为“黄金30秒”流程现象第一排查动作如何修复连接被拒绝检查端口和防火墙安全组放行对应端口仍提示输入密码ls -la ~/.ssh、cat authorized_keys修正权限、恢复公钥提示Permission denied (publickey)ssh -v看日志修正sshd_config重启sshd日志中有failed to open authorized_keysrestorecon -R -v ~/.sshSELinux标签修复日志显示Connection reset查fail2ban或IPTables临时放行来源IP测试5.2 从日志中找出真实原因遇到卡住的时候别在客户端瞎猜服务器端日志才有最终答案。sudo tail -f /var/log/auth.logDebian/Ubuntu家族用auth.logCentOS/RHEL家族一般在/var/log/secure。重开一个连接的同时观察日志尾部看到sshd[12345]: Authentication refused: bad ownership or modes for directory /home/user/.ssh说明权限问题去chmod。看到sshd[12345]: Failed publickey for user from 1.2.3.4 port 54321说明公钥没匹配上去查authorized_keys内容。看到sshd[12345]: Connection closed by authenticating user则可能是客户端侧私钥或代理配置出问题。日志能精确定位到百分九十的问题方向。我强烈建议所有想用好SSH免密的人都养成熟练看auth日志的习惯这一招能省下大量时间。5.3 顺手记下这几个避坑技巧最后分享几个我个人踩过之后总结的实用技巧。一个是用agent管理多个私钥。当你电脑上有多个密钥对时如果不启用ssh-agent每次连接可能都要手动指定密钥文件。启动并添加密钥eval $(ssh-agent -s) ssh-add ~/.ssh/id_ed25519 ssh-add -lmacOS用户还可以把私钥加入系统钥匙串ssh-add --apple-use-keychain ~/.ssh/id_ed25519重启之后依然有效Windows和Linux则可以用密钥文件权限和agent转发解决临时需要。有一点要注意连接跳板机再转发到内网时尽量使用AgentForwarding而不是把私钥拷贝到跳板机方法是在config里对跳板机会话开启ForwardAgent yes。把私钥直接拷到中间机本质上就是扩大泄露面这属于及时止损。第二个技巧是给服务器管理账号单独配置。我尽量不使用root账户直接部署密钥而是用deploy或ops等专属账号然后用sudo提权。好处是审计日志清晰哪些操作是谁做的都能对应上同时因为deploy账号无法直接登录shell攻击面更小。第三个技巧是定期轮换。市面上很多人配完密钥就再也没动过直到私钥泄露才含泪重置。我的做法是每半年或是每次成员离职后重新生成一批密钥严格走“生成新钥—部署新钥—验证新钥—移除旧钥”四步。配置不复杂但坚持下来你会发现“管理密钥”和“管理密码”一样需要体系。整个流程跑下来从生成密钥、分发公钥到验证免密登录再到关闭密码登录最多半小时就能完成。这套方案的直接收益是安全度大幅提升、日常操作效率翻倍间接收益则是一个更规范更自动化的基础设施起点。如果你手头也正被密码登录困扰或者想把手头的服务器管理收敛到一个可控体系里照着这篇文章操作一遍亲测下来会很稳。