
用了大半年的密码登录我最终还是把所有RedHat服务器的认证方式换成了密钥免密。原因很现实机器多、密码复杂、定期轮换需求强每天在MobaXterm里输密码这件事本身就成了最大的效率瓶颈和维护隐患。可一旦换成公钥加私钥这套认证机制登录过程干净利落安全级别也比密码高一个维度。这篇文章把我实际操作的完整流程写出来从原理到落地从MobaXterm生成密钥到RedHat侧授权再到失败后的排查套路全部覆盖。跟着走一遍你也能把密码登录这件事彻底忘掉。1. 为什么密码登录又麻烦又不安全公钥/私钥免密的关键思路是什么1.1 密码登录的隐性成本很多人觉得“密码登录也没什么不好”那是没经历过大规模管理Linux服务器的场景。我先列几个实际痛点密码复杂到一定程度手输特别容易错。RedHat的账号密码如果要求大小写加特殊字符在MobaXterm终端里手输几次打错就会被系统锁定触发pam_tally2或者fail2ban进而影响正常登录。密码要定期改。等保要求三个月换一次密码你在十台RedHat上维护十个不同的复杂密码基本是在考验记忆力。密码会经过网络传输。即使SSH是加密通道密码依然是可被中间人截获后离线爆破的资产。密码一旦泄露对方可以直接登录而密钥认证不存在“把私钥发给服务器”这个过程。说白了一句话密码是一个“可被转交的秘密”而私钥是一个“不可被冒用的身份凭证”。理解了这一点你就能明白为什么密钥免密登录在运维圈里几乎是必备技能。1.2 公钥和私钥的工作原理用一句话讲清楚公钥用来加密和验证签名私钥用来解密和生成签名两者成对出现谁也离不开谁。具体到SSH登录过程你的Windows机器上保存着私钥文件RedHat服务器的~/.ssh/authorized_keys文件里保存着你的公钥。发起SSH连接时服务器会基于你的公钥生成一个随机挑战challenge发送给客户端。客户端用私钥对这个挑战做签名把签名结果返回。服务器用公钥验证签名。签名匹配就证明你确实持有与该公钥配对的私钥认证通过。这个流程里私钥始终没有离开你的本机。所以你不必担心“密钥在网络上传输被截获”因为传输的只是签名结果而不是私钥本身。用个生活化的比喻公钥是一把锁私钥是唯一能打开这把锁的钥匙。你把锁复制成千上万把分发给所有服务器它们看到有人上门就把锁拿出来考你。有钥匙的人轻松开锁进门没钥匙的人就算把锁研究一百年也进不来。1.3 为什么SSH选非对称加密而不靠对称加密对称加密比如AES速度快但有个死穴通信双方必须共享同一个密钥。问题是怎么安全地把这个密钥传给另一方如果通过网络直接传那就存在被截获的风险这就变成了“先有鸡还是先有蛋”的循环。非对称加密解决了这个循环公钥随便分发私钥永久保留。就算公钥在网络传输中被截获攻击者也无法用它推导出私钥。因此SSH的认证阶段确认“你是谁”用非对称加密而在后续建立加密通道时协商出会话密钥再用对称加密保证传输速度。你不需要精通密码学但理解“公钥可以公开、私钥必须保密”这个核心原则后面配置和排错的时候就特别有用。2. 环境准备MobaXterm版本与RedHat侧OpenSSH的配置确认2.1 MobaXterm要用对版本和正确的工具入口MobaXterm是Windows上非常好用的终端软件支持SSH、SFTP、RDP等多种协议。它在“Tools”菜单里自带了一个密钥生成器叫MobaKeygen这是生成密钥对最顺手的方式不需要额外去装OpenSSH客户端。版本方面建议用20.x以上的版本对现代SSH算法支持更全面。如果你还在用很老的版本可能不支持ssh-ed25519这类新公钥格式。RedHat这边不同小版本自带的OpenSSH版本差异较大后面会有兼容性处理说明。有一个非常容易踩的坑在MobaXterm的“本地终端”Local Terminal里执行ssh-keygen它实际使用的是MobaXterm自带的Unix工具环境默认家目录通常不在C:\Users\你的用户名而是在C:\Users\你的用户名\Documents\MobaXterm\home这类位置。很多人配完发现私钥文件“找不到”就是因为路径和预期不一致。用MobaKeygen直接生成并手动选择保存路径可以完全避开这个问题。2.2 确认RedHat的sshd服务正常运行拿到一台RedHat首先要确认SSH服务端是正常的。执行systemctl status sshd或者对于RedHat 6及更早版本service sshd status如果服务没启动先启动并设置开机自启systemctl enable --now sshd然后检查SSH主配置文件/etc/ssh/sshd_config里几个关键参数# 是否允许公钥认证 PubkeyAuthentication yes # 公钥文件路径默认即可 AuthorizedKeysFile .ssh/authorized_keys # 是否允许root直接登录如果用root账号直连 PermitRootLogin yes # 是否允许密码登录建议在密钥验证成功后再改成no PasswordAuthentication yes修改配置文件后先用sshd -t校验语法再重载服务sshd -t systemctl reload sshd2.3 老版本RedHat的兼容性问题RedHat 6.5、7.x这类老版本自带的OpenSSH较旧如果MobaXterm端默认启用了较高版本的密钥交换算法连接时可能报类似no matching key exchange method found这类的错误。解决思路有两种第一在MobaXterm的SSH会话“Advanced SSH settings”里勾选兼容老设备的算法选项。第二在RedHat服务端/etc/ssh/sshd_config末尾追加兼容配置KexAlgorithms diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256 Ciphers aes128-ctr,aes192-ctr,aes256-ctr然后重载sshd服务。这里要提醒一点修改加密算法是在降低安全面请只在确实需要连接老系统时临时使用不要全局永久保留。2.4 生成密钥前的基础信息确认在生成密钥之前先确认你要登录的RedHat账号是哪个root还是普通用户因为公钥要放进该用户的~/.ssh/authorized_keys不同用户之间不能共用。如果想实现免密登录多个账号每个账号都需要单独部署一次公钥。还需要确认RedHat服务器的IP、SSH端口默认22。如果你在公司网络环境下可能还要确认防火墙是否放行22端口否则服务端一切配置正确但连接请求到达不了服务器。3. 生成密钥对并把公钥部署到RedHat我试过三种可靠方法3.1 用MobaKeygen生成密钥对的具体步骤打开MobaXterm点击菜单栏的Tools-MobaKeygen。密钥类型选择推荐RSA位数选3072或4096。如果只是内网运维2048也够用但4096更稳妥缺点是生成和加解密稍慢不过SSH场景下感知不到。点击Generate然后按提示在窗口内移动鼠标收集随机熵。看到生成的公钥字符串后建议在Key comment里填一个备注比如rootansible-host方便以后识别这条公钥是哪台客户端的。设置Key passphrase。这是私钥文件本身的保护口令不是系统登录密码。我建议一定要设哪怕是个简单的短口令。因为直接不设口令的话私钥文件一旦被别人拷走对方就是零门槛登录。设置口令后即使文件泄露对方也要破解口令才能用。点击Save private key保存私钥点击Save public key保存公钥。保存路径建议放在一个独立目录比如C:\ssh-keys\方便管理。不建议把私钥放桌面或临时下载目录容易被误删或泄露。3.2 方法一用scp/ssh-copy-id从本地终端推送公钥如果你不太想手动编辑服务器上的文件最省力的方式是ssh-copy-id。这个命令会自动创建~/.ssh目录、写入公钥并设置权限。在MobaXterm的本地终端或自带的Unix工具环境执行ssh-copy-id -i /c/ssh-keys/id_rsa.pub root192.168.10.10执行时会提示输入一次RedHat上root的密码。验证通过后公钥就被自动追加到/root/.ssh/authorized_keys里。不过ssh-copy-id在老版本RedHat上并不一定自带如果命令不存在就用下面两种手工方法。3.3 方法二手工命令创建authorized_keys文件这是最通用、最可控的方法适合任何版本的RedHat。先让MobaXterm用密码登录到目标主机然后执行mkdir -p ~/.ssh chmod 700 ~/.ssh echo 在这里粘贴你的公钥内容 ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys restorecon -R -v ~/.ssh 2/dev/null解释一下每一条命令的作用mkdir -p ~/.ssh如果.ssh目录不存在就创建。chmod 700 ~/.ssh目录权限必须是700只有属主本人能读写进入。如果权限过于开放OpenSSH会拒绝使用这个目录下的公钥文件。echo 公钥内容 ~/.ssh/authorized_keys把公钥字串追加到授权文件注意是追加而不是覆盖防止多次操作把已有公钥全清掉。chmod 600 authorized_keys授权文件权限必须是600。restorecon这是给SELinux环境用的。RedHat默认开着SELinux如果公钥文件在复制过程中安全上下文不对sshd可能拒绝读取。执行restorecon可以把.ssh目录下的文件上下文恢复正确。3.4 方法三通过SFTP上传公钥文件再追加不想在终端里复制粘贴长字符串的时候我更喜欢用MobaXterm自带的SFTP面板。先用密码登录RedHat左侧的SFTP浏览窗口会显示服务器文件系统。直接把本地的id_rsa.pub文件拖到/root目录下然后执行mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/id_rsa.pub ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys rm -f ~/id_rsa.pub restorecon -R -v ~/.ssh 2/dev/null这个方法的优势在于不用选中终端里那段超长公钥内容避免CtrlC/CtrlV过程中把内容截断或误复制多余字符。3.5 用ssh-keygen做本地公钥匹配验证部署完成后我强烈建议在本地做一次公钥与私钥的匹配验证避免因为保存错文件导致配置半天才发现问题。在MobaXterm本地终端执行ssh-keygen -y -f /c/ssh-keys/id_rsa这条命令会读取私钥内容输出对应的公钥。把这个输出与你在服务器authorized_keys里的内容对比如果一致说明配对没问题如果不一致说明你服务器上放的是另一对密钥里的公钥登录肯定失败。4. MobaXterm会话里配置私钥完成免密登录验证4.1 在SSH会话设置中指定私钥公钥部署到RedHat后接下来就是让MobaXterm在发起SSH连接时自动使用私钥。步骤很简单点击Session-SSH。Host name填RedHat的IPPort填22用户名填RedHat上的账号比如root。展开Advanced SSH settings勾选Use private key点击右侧文件选择按钮选中你保存的私钥文件。点击OK保存会话设置然后双击这个会话。连接过程中MobaXterm会自动加载私钥尝试公钥认证。如果私钥设置了Key passphrase会弹出输入口令的窗口。输入正确后只要MobaXterm进程不退出后续连接同一台服务器就不再要求重复输口令。4.2 让MobaXterm记住私钥口令每次打开MobaXterm第一次连接都要输一次私钥口令有些人会觉得麻烦。MobaXterm提供了口令保存功能在SSH会话配置里有一个Remote environment和SSH keepalive相关选项区域另外弹出输入passphrase时窗口里通常有记住口令的选项勾上即可。但这里有个安全提示如果这台Windows机器有多个人能登录勾选记住口令等于把私钥的最后一道防线也拆了。我个人是“机器私密性高就记住共享机器坚决不记”。4.3 验证登录是否成功连接成功后MobaXterm右侧窗口会显示RedHat的命令行提示符。看连接日志时会看到类似Authentication methods succeeded (publickey)或者Authenticated with publickey的字样这就是密钥认证成功的直接证据。验证完成后我建议在RedHat服务端修改配置把密码登录关掉PasswordAuthentication no然后sshd -t systemctl reload sshd请注意关闭密码登录之前一定要保证至少有一个已建立的SSH会话还开着并且密钥认证已经验证成功。否则一旦密码认证被关而密钥认证又因为某些问题失效你就只能去物理控制台或带外管理卡上救人了这是新手最容易犯的“把自己锁在门外”的错误。4.4 保持会话活跃的细节RedHat服务器默认的TCP连接超时时间可能几分钟就会断开闲置会话。MobaXterm里可以在Settings-Configuration-SSH-SSH keepalive中设置一个保活周期比如每30秒或60秒发送一次心跳包。这个功能对运维场景特别实用不然本地思考或看日志太久回头终端已经断开输入的内容全白费。5. 免密登录失败时怎么办从权限到日志的完整排查链路排查思路比记忆几个命令更重要。我把自己的排查顺序写出来你照着做绝大多数问题都能定位。5.1 先看MobaXterm本地的详细日志不要去猜问题而是让客户端把详细握手过程打出来。在MobaXterm本地终端执行ssh -vvv -i /c/ssh-keys/id_rsa root192.168.10.10关键看输出中Offering public key、Server accepts key、Authentications that can continue这几行。如果出现Permission denied (publickey)说明服务端拒绝了公钥。5.2 在RedHat侧看sshd日志RedHat 7及以后版本SSH日志一般在/var/log/securetail -f /var/log/secure | grep sshdRedHat 6部分版本在/var/log/messages。日志里的关键词Accepted publickey for root from IP公钥认证成功问题解决了。Failed publickey for root from IP公钥认证失败通常是公钥内容不对或私钥不匹配。Authentication refused: bad ownership or modes for file权限配置错误。Connection closed by authenticating user可能是配置了错误的passphrase或密钥被agent后端拒绝。5.3 权限问题是最常踩的坑OpenSSH对密钥相关文件的权限要求近乎苛刻因为sshd以root身份运行但它读取用户文件时如果发现这些文件被其他用户可写就会怀疑有人篡改过从而拒绝使用。这是我整理的最低权限清单路径要求权限说明/root700家目录不能对其他用户可写/root/.ssh700密钥目录/root/.ssh/authorized_keys600授权公钥文件私钥文件本地不强制Windows上无所谓Linux客户端才需要限制如果家目录是755.ssh目录本身没问题也会触发bad ownership or modes。赶紧执行chmod 700 /root chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys修改后不用重启sshd直接重试连接即可。5.4 一个真实翻车案例我把authorized_keys内容贴错位置我之前配置一台RedHat 8时明明所有权限都正确、日志也显示尝试了公钥认证但就是不成功。最后看了半天发现是我在粘贴公钥时带着对端的ssh-rsa行首但少了一行注释更关键的是我在粘贴时顺手把公钥旁边的换行符弄丢了导致整行内容被截断。用cat -A /root/.ssh/authorized_keys查看发现这一行在中间就出现一个^M字符。这就是Windows文本编辑器的\r\n导致的。解决办法很简单sed -i s/\r$// /root/.ssh/authorized_keys另外检查一下公钥是否被粘贴成多行。公钥内容是单行如果被自动折行也会认证失败。判断标准是看开头是不是ssh-rsa或ecdsa-sha2-*结尾是否是或用户名注释。在vim里查看时如果一行很长建议用set nowrap关闭自动换行。5.5 SELinux对authorized_keys的影响RedHat默认开启SELinux如果公钥文件是用SFTP或者cp命令复制到系统里的文件的SELinux上下文可能不匹配sshd作为受限服务读取时会报Permission denied。此时执行restorecon -R -v /root/.ssh如果是在家目录下复制文件SELinux会自动给新文件设置正确的上下文但最好还是手动执行一次确保万无一失。如果你对SELinux不熟尽量不要用setenforce 0这种粗暴关法简单restorecon就够了。5.6 常见问题排查速查表现象可能原因解决办法Permission denied (publickey)公钥没写入或写错位置检查authorized_keys内容和权限bad ownership or modes.ssh或authorized_keys权限太开放chmod 700和chmod 600Server accepts key但被拒绝私钥不匹配用ssh-keygen -y验证no matching key exchange methodRedHat 6老版本与新版客户端算法不兼容服务端追加KexAlgorithmsSELinux拒绝读取文件上下文错误执行restorecon输入passphrase后仍失败私钥口令输入错误或私钥文件损坏重新保存私钥登录后立即断线sshd配置里AllowUsers限制检查/etc/ssh/sshd_config5.7 批量部署多台RedHat的经验如果你要管理的不止一台RedHat手动一台台配置肯定不现实。我的做法是先在跳板机上生成密钥对然后把公钥通过循环分发到各台机器。示例脚本用MobaXterm本地终端执行for host in 192.168.10.11 192.168.10.12 192.168.10.13; do echo $host sshpass -p 临时密码 ssh-copy-id -i /c/ssh-keys/id_rsa.pub -o StrictHostKeyCheckingno root$host done如果系统没有sshpass也可以用expect或直接用MobaXterm的MultiExec功能同时打开多个SSH会话在会话输入框里执行相同命令效率同样很高。批量操作后的所有服务器建议用一条命令统一检查密钥认证是否生效for host in 192.168.10.11 192.168.10.12 192.168.10.13; do ssh -o BatchModeyes -o ConnectTimeout5 root$host echo $(hostname) ok doneBatchModeyes的作用是如果密钥认证失败直接返回错误不会提示输入密码。这在批量验证场景里很实用避免脚本卡在密码输入界面。5.8 安全加固的收尾动作全部机器验证密钥认证成功后把密码登录关掉sed -i s/^#*PasswordAuthentication.*/PasswordAuthentication no/ /etc/ssh/sshd_config sshd -t systemctl reload sshd备份私钥并妥善保管建议放在加密压缩包里或U盘中。公钥可以随便分享但私钥绝对不能进Git仓库、不能放在共享目录、不能通过未加密的聊天工具传输。我个人的习惯是每台客户端生成的密钥对都会追加一条不同的comment这样即使将来约10台机器都免密了我依然能从服务端的authorized_keys里看出每个公钥对应哪台客户端排查问题时省了很多时间。这个细节用上之后基本就没再被“哪台机器对应哪个公钥”这种问题绊倒过。