☰
RedHat服务器SSH密钥免密登录实战:从MobaXterm生成到排查
2026/10/1 12:18:34 网站建设 项目流程

用了大半年的密码登录,我最终还是把所有RedHat服务器的认证方式换成了密钥免密。原因很现实:机器多、密码复杂、定期轮换需求强,每天在MobaXterm里输密码这件事本身就成了最大的效率瓶颈和维护隐患。可一旦换成公钥加私钥这套认证机制,登录过程干净利落,安全级别也比密码高一个维度。这篇文章把我实际操作的完整流程写出来,从原理到落地,从MobaXterm生成密钥到RedHat侧授权,再到失败后的排查套路,全部覆盖。跟着走一遍,你也能把密码登录这件事彻底忘掉。

1. 为什么密码登录又麻烦又不安全,公钥/私钥免密的关键思路是什么

1.1 密码登录的隐性成本

很多人觉得“密码登录也没什么不好”,那是没经历过大规模管理Linux服务器的场景。我先列几个实际痛点:

  • 密码复杂到一定程度,手输特别容易错。RedHat的账号密码如果要求大小写加特殊字符,在MobaXterm终端里手输,几次打错就会被系统锁定,触发pam_tally2或者fail2ban,进而影响正常登录。
  • 密码要定期改。等保要求三个月换一次密码,你在十台RedHat上维护十个不同的复杂密码,基本是在考验记忆力。
  • 密码会经过网络传输。即使SSH是加密通道,密码依然是可被中间人截获后离线爆破的资产。密码一旦泄露,对方可以直接登录;而密钥认证不存在“把私钥发给服务器”这个过程。

说白了一句话:密码是一个“可被转交的秘密”,而私钥是一个“不可被冒用的身份凭证”。理解了这一点,你就能明白为什么密钥免密登录在运维圈里几乎是必备技能。

1.2 公钥和私钥的工作原理

用一句话讲清楚:公钥用来加密和验证签名,私钥用来解密和生成签名,两者成对出现,谁也离不开谁。

具体到SSH登录过程:

  1. 你的Windows机器上保存着私钥文件,RedHat服务器的~/.ssh/authorized_keys文件里保存着你的公钥。
  2. 发起SSH连接时,服务器会基于你的公钥生成一个随机挑战(challenge),发送给客户端。
  3. 客户端用私钥对这个挑战做签名,把签名结果返回。
  4. 服务器用公钥验证签名。签名匹配,就证明你确实持有与该公钥配对的私钥,认证通过。

这个流程里,私钥始终没有离开你的本机。所以你不必担心“密钥在网络上传输被截获”,因为传输的只是签名结果,而不是私钥本身。

用个生活化的比喻:公钥是一把锁,私钥是唯一能打开这把锁的钥匙。你把锁复制成千上万把,分发给所有服务器,它们看到有人上门,就把锁拿出来考你。有钥匙的人轻松开锁进门,没钥匙的人就算把锁研究一百年,也进不来。

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 sshd

2.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里填一个备注,比如root@ansible-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 root@192.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

这个方法的优势在于:不用选中终端里那段超长公钥内容,避免Ctrl+C/Ctrl+V过程中把内容截断或误复制多余字符。

3.5 用ssh-keygen做本地公钥匹配验证

部署完成后,我强烈建议在本地做一次公钥与私钥的匹配验证,避免因为保存错文件导致配置半天才发现问题。

在MobaXterm本地终端执行:

ssh-keygen -y -f /c/ssh-keys/id_rsa

这条命令会读取私钥内容,输出对应的公钥。把这个输出与你在服务器authorized_keys里的内容对比,如果一致,说明配对没问题;如果不一致,说明你服务器上放的是另一对密钥里的公钥,登录肯定失败。

4. MobaXterm会话里配置私钥,完成免密登录验证

4.1 在SSH会话设置中指定私钥

公钥部署到RedHat后,接下来就是让MobaXterm在发起SSH连接时自动使用私钥。

步骤很简单:

  1. 点击Session->SSH。
  2. Host name填RedHat的IP,Port填22,用户名填RedHat上的账号,比如root。
  3. 展开Advanced SSH settings,勾选Use private key,点击右侧文件选择按钮,选中你保存的私钥文件。
  4. 点击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 root@192.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/secure:

tail -f /var/log/secure | grep sshd

RedHat 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 600
Server accepts key,但被拒绝私钥不匹配用ssh-keygen -y验证
no matching key exchange methodRedHat 6老版本与新版客户端算法不兼容服务端追加KexAlgorithms
SELinux拒绝读取文件上下文错误执行restorecon
输入passphrase后仍失败私钥口令输入错误或私钥文件损坏重新保存私钥
登录后立即断线sshd配置里AllowUsers限制检查/etc/ssh/sshd_config

5.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 StrictHostKeyChecking=no 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 BatchMode=yes -o ConnectTimeout=5 root@$host "echo $(hostname) ok" done

BatchMode=yes的作用是:如果密钥认证失败,直接返回错误,不会提示输入密码。这在批量验证场景里很实用,避免脚本卡在密码输入界面。

5.8 安全加固的收尾动作

全部机器验证密钥认证成功后,把密码登录关掉:

sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config sshd -t systemctl reload sshd

备份私钥并妥善保管,建议放在加密压缩包里或U盘中。公钥可以随便分享,但私钥绝对不能进Git仓库、不能放在共享目录、不能通过未加密的聊天工具传输。

我个人的习惯是,每台客户端生成的密钥对都会追加一条不同的comment,这样即使将来约10台机器都免密了,我依然能从服务端的authorized_keys里看出每个公钥对应哪台客户端,排查问题时省了很多时间。这个细节用上之后,基本就没再被“哪台机器对应哪个公钥”这种问题绊倒过。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询