ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Rocky 9.5 OpenSSH 10.0p1 RPM升级加固实战

Rocky 9.5 OpenSSH 10.0p1 RPM升级加固实战

简介:面向 Rocky Linux 9.5 x86_64 环境的 OpenSSH 升级与安全加固需求,这套离线交付包适合系统管理员、运维工程师及内网安全加固人员使用。它围绕将 OpenSSH 提升至 10.0p1 并配套 OpenSSL 3.5.0 的场景组织,提供可直接安装的 RPM 组件和自动化升级脚本,减少源码编译、依赖查找与外网源不可用带来的处理成本。压缩包共 6 个文件,包含 5 个 rpm 与 1 个 sh 脚本,整体约 10.96MB;其中 rpm 覆盖 OpenSSH 服务端、客户端及相关系统依赖组件,sh 脚本用于串联升级流程,便于在受限网络中离线部署和升级后复核。资源已有 210 人学习下载,可作为 Rocky Linux 9 系列 SSH 版本升级、服务可用性验证与基础加固的参考材料。读者可从中获得组件配套关系、脚本化升级思路以及升级后版本与服务状态核对方法,适合需要快速落地补丁升级、减少外网依赖并完成加固检查的中高级运维场景。

1. 上周接手一台 Rocky Linux 9.5 跳板机,安全基线扫描把 OpenSSH 标成了红项

运维同事的第一反应是dnf update openssh,翻遍 BaseOS 和 AppStream,版本最高只到 8.7p1,离 OpenSSH 10.0p1 差着一大截。转头想源码编译,又怕make install把系统自带的 sshd 冲掉,回滚无门。这套 rockylinux9.5-ssh10.0p1-ssl3.5.0-rpm-x86-64 升级加固脚本走的是第三条路:先把 OpenSSL 3.5.0 和 OpenSSH 10.0p1 编成标准 RPM,再用 dnf 替换,装翻了dnf downgrade就能退回去。手里有 Rocky 9.5、要过安全基线、又不敢在生产机上裸编源码的人,这套流程值得照着走一遍。

2. 环境盘点与版本选型:OpenSSL 3.5.0 和 OpenSSH 10.0p1 的依赖账先算清

动手之前先把盘面摸清。很多人卡在第一步不是因为不会编,是因为不知道系统里到底有几个地方的 OpenSSH 在跑、sshd 到底链着哪个 libssl。这一章不讲原理,只讲怎么在十分钟内把基线、依赖、回滚点三件事确定下来,后面编包和装机才有底。

2.1 系统基线核对:五条命令摸清现状

拿到机器先别急着上传源码包,把下面几条命令跑一遍,输出贴到变更单里,后面出问题时要靠这份快照对照。

# 1. 确认发行版与内核,Rocky 9.5 对应 el9 系列 cat /etc/rocky-release uname -r # 2. 看当前 OpenSSH 相关包的完整清单(别只看 openssh 一个) rpm -qa | grep -E '^(openssh|openssl)' | sort # 3. 看 sshd 二进制实际链接了哪些库,重点盯 libssl / libcrypto 的路径 ldd /usr/sbin/sshd | grep -E 'ssl|crypto' # 4. 看当前 sshd 的编译参数,判断系统包开了哪些特性 sshd -T 2>/dev/null | head -5 strings /usr/sbin/sshd | grep -i 'openSSH_' | head -3 # 5. 看 sshd 监听端口与运行状态 ss -lntp | grep sshd systemctl status sshd --no-pager

第 3 条是关键。Rocky 9.5 自带的 openssh-server 链接的是系统/usr/lib64/libssl.so.3,版本 3.0.x。你要装的 OpenSSH 10.0p1 如果链接到 OpenSSL 3.5.0,那么 libssl 的路径必须能被 sshd 在启动时找到——ldd的输出就是判断这件事有没有做对的唯一证据。第 5 条用来确认端口,后面改配置时如果动了 Port,SELinux 那边还要同步打标签,这里先记下来。

依赖清单我一般整理成一张表,编包前对着装,装完对着查:

依赖包作用缺失时的典型报错
zlib-devel压缩传输configure: error: *** zlib missing
openssl-devel头文件与 pkg-configchecking for OpenSSL headers... no
pam-develPAM 认证模块--with-pam编译中断
libselinux-develSELinux 上下文--with-selinux静默跳过
audit-libs-devel审计日志sshd 登录事件不进 audit.log
krb5-develGSSAPI 认证域环境下密码认证异常

装依赖这一步别偷懒用--nodeps,缺一个 dev 包,configure 有时不报错,只是把某个特性静默关掉,等装到生产机上才发现 PAM 用不了,那时候排查成本比装包高十倍。

2.2 为什么这条链路必须走 RPM,而不是 make install

源码编译三分钟,回滚三天——这是血泪经验。make install会把新二进制直接覆盖到/usr/sbin/sshd,但 RPM 数据库里记录的还是 8.7p1,rpm -V openssh-server从此永久报校验失败,dnf后续任何一次系统更新都可能把文件再覆盖回去,或者干脆因为文件冲突拒绝执行。更要命的是 SELinux 上下文:手工 cp 进去的二进制,标签常常是user_home_t或者default_t,enforcing 模式下 sshd 直接起不来,而journalctl里只会甩一句Permission denied,玄学得很。

下面这张表是我在几个项目里对比出来的结论,变更评审时也拿它说服人:

维度make installRPM 打包替换
回滚方式手工恢复备份文件dnf downgrade openssh
包完整性校验rpm -V失效正常
SELinux 上下文需手工 restorecon%post脚本自动处理
多机一致性每台手工操作一套包推所有机器
依赖解析无dnf 自动校验
卸载找得到文件算命大rpm -e干净移除

RPM 唯一多出来的成本是写 spec 和等编译。但这套脚本已经把 spec 模板和构建脚本备好了,你要做的只是改%files里的路径、确认--with-ssl-dir指向正确,剩下的rpmbuild -ba会自动跑完。熟手看到这里会问:那 OpenSSL 3.5.0 要不要也打成 RPM 替换系统库?答案是不替换,理由在 3.2 节展开,简单说就是动系统 libssl 会连带影响 dnf、curl、sudo,牵一发动全身。

2.3 升级前必留的三样东西:备份、包、会话

后悔药要在动手之前吃。下面三条命令在变更窗口开始前十分钟执行,缺一不可。

# 1. 备份整个 ssh 配置目录,带上时间戳,别覆盖旧的 tar -czf /root/ssh-config-backup-$(date +%F-%H%M).tar.gz /etc/ssh /etc/pam.d/sshd # 2. 把当前的 ssh 相关 rpm 全部下载到本地留底,断网也能回滚 mkdir -p /root/ssh-rollback && cd /root/ssh-rollback dnf download --alldeps openssh openssh-server openssh-clients openssh-askpass # 3. 记录当前包版本与文件清单,回滚后逐条比对 rpm -qa | grep -E '^(openssh|openssl)' > /root/ssh-rollback/pkg-list.txt rpm -ql openssh-server > /root/ssh-rollback/openssh-server-files.txt

第 2 条用的是dnf download --alldeps,比yumdownloader --resolve更省事的地方在于它会把所有依赖包一并拉到当前目录,尤其适合内网无外网源的机器。第 3 条的包清单在回滚后拿diff一比,就知道哪些文件是新增的、哪些被改过,比盲目rpm -e靠谱。

最后一样东西不是文件,是会话。操作前开两个 SSH 会话:一个用来改配置,一个全程不碰、只挂在后台当救生艇。改完配置先别关救生艇,等新会话连上、systemctl status稳定三十秒后再退。后面第 6 章讲验证时会再提一次,这里的习惯一旦养成,sshd 相关的翻车概率能降低一大半。

3. 编包实操:OpenSSL 3.5.0 和 OpenSSH 10.0p1 的 RPM 怎么打出来

构建机不需要和生产机同配置,但必须是同一个大版本(el9),glibc 版本对不上,编出来的包在目标机上一跑就报GLIBC_2.34 not found。我一般找一台同版本的 Rocky 9.5 虚拟机当构建机,装完rpm-build和rpmdevtools就开工,编完把~/rpmbuild/RPMS/x86_64/整个目录拷走。

3.1 构建环境:从空目录到能跑 rpmbuild

# 装构建工具链与全部 dev 依赖 dnf install -y rpm-build rpmdevtools gcc make perl-IPC-Cmd \ zlib-devel pam-devel libselinux-devel audit-libs-devel \ krb5-devel openssl-devel perl-generators systemd-rpm-macros # 生成标准构建目录树:BUILD / RPMS / SOURCES / SPECS / SRPMS rpmdev-setuptree # 确认目录落地 ls -1 ~/rpmbuild

rpmdev-setuptree生成的五个目录各有分工:SOURCES放源码 tarball,SPECS放 spec 文件,BUILD是解压后的编译现场,RPMS是最终产物,SRPMS是源码包。源码包放进 SOURCES 时注意名字要和 spec 里Source0:完全一致,openssh-10.0p1.tar.gz少一个p都会让 rpmbuild 报Bad file。

systemd-rpm-macros这个包容易被忽略。Rocky 9 的 spec 里用%systemd_post sshd.service这类宏来注册 systemd 单元,缺了它,%post段会报macro undefined,包能编出来但装上去服务不生效。

3.2 OpenSSL 3.5.0 的编包取舍:装到 /opt,不动系统库

这是整套流程里最关键的一个决定。Rocky 9.5 的系统 libssl 是 3.0.x,dnf、curl、sudo、systemd-resolved 全都链着它。你要是把 3.5.0 覆盖进/usr/lib64,最快当天下午 dnf 就会报libssl.so.3: undefined symbol,整台机器变砖。

常见做法是单独编一份装到/opt/openssl-3.5.0,只让 OpenSSH 这一个消费者去链接它。spec 的%build段核心参数如下:

# openssl.spec 的 %build 段:装到独立前缀,不碰系统 /usr ./Configure \ --prefix=/opt/openssl-3.5.0 \ --openssldir=/opt/openssl-3.5.0/ssl \ --libdir=lib64 \ shared \ zlib-dynamic \ enable-ktls \ -Wl,-rpath,/opt/openssl-3.5.0/lib64 make -j"$(nproc)"

--libdir=lib64不能省。x86_64 上 OpenSSL 默认往lib/放共享库,而 Rocky 的库搜索路径是lib64,装完ldconfig也找不到,后面 sshd 启动就报libcrypto.so.3 not found。-Wl,-rpath,...是把运行时库路径直接焊进二进制的头文件里,这样即使 systemd 环境里没有LD_LIBRARY_PATH,sshd 也能定位到 3.5.0 的库。enable-ktls是内核 TLS 卸载,可选,编不上也不影响 SSH 功能。

%files段要把/opt/openssl-3.5.0整体收进去,并在%post里加一句/sbin/ldconfig。另外提醒一句:如果同一台机器上后面还要装别的需要 OpenSSL 3.5 的软件,这份 /opt 前缀包可以直接复用,不用重复编。

3.3 OpenSSH 10.0p1 的 configure 开关:--with-ssl-dir是命门

OpenSSH 这边参数多,但真正决定成败的就三个:--with-ssl-dir、--with-privsep-path、--sysconfdir。其余都是功能取舍。

# openssh.spec 的 %build 段核心 configure 参数 ./configure \ --prefix=/usr \ --sysconfdir=/etc/ssh \ --libdir=/usr/lib64/openssh \ --with-ssl-dir=/opt/openssl-3.5.0 \ --with-pam \ --with-selinux \ --with-audit=linux \ --with-zlib=/usr \ --with-kerberos5=/usr \ --with-privsep-path=/var/empty/sshd \ --disable-strip make -j"$(nproc)"

--with-ssl-dir=/opt/openssl-3.5.0告诉 configure 去这个目录的include/找头文件、lib64/找库。写错成/opt/openssl-3.5.0/lib64会报找不到opensslv.h,因为 configure 期望的是安装根目录。--with-privsep-path=/var/empty/sshd指定权限分离用的空目录,这个目录必须是 root 属主、755 权限、且不能有写权限,%post段里要用install -d -m 0755 -o root -g root /var/empty/sshd建好,否则 sshd 启动直接退出,日志里只有一句Privilege separation user sshd does not exist或者bad ownership or modes for chroot directory。

--libdir=/usr/lib64/openssh是给 ssh 内部辅助程序(sftp-server、sshd-session)找位置用的,和系统路径错开,避免和旧包的文件冲突。--with-audit=linux编进 audit 支持后,登录失败事件能进/var/log/audit/audit.log,等保测评时这条是加分项。

OpenSSH 10.0p1 相比 8.7 有几个算法层面的硬变化,必须先知道,否则配完连不上还以为是配置写错了:DSA 算法被彻底移除,ssh-dss主机密钥和ssh-keygen -t dsa都不再可用;基于 SHA-1 的ssh-rsa签名默认不再协商,RSA 密钥只能用rsa-sha2-256或rsa-sha2-512;ChallengeResponseAuthentication这个配置项已经彻底删掉,只认KbdInteractiveAuthentication。这三条在第 5 章会对应三个真实故障。

3.4 产物自检:包编出来了不等于能用

rpmbuild 跑完不报错,只代表语法对了,不代表包能装。RPMS/x86_64/下面应该出现四个包:openssh、openssh-server、openssh-clients、openssh-askpass,以及 OpenSSL 的一个openssl35前缀包。装之前挨个过一遍下面三条命令。

cd ~/rpmbuild/RPMS/x86_64/ # 1. 看包信息与依赖声明,确认 Requires 里没有意外的系统库版本要求 rpm -qpi openssh-server-10.0p1-1.el9.x86_64.rpm rpm -qpR openssh-server-10.0p1-1.el9.x86_64.rpm # 2. 看包内文件清单,确认没有和系统旧包重叠的路径 rpm -qpl openssh-server-10.0p1-1.el9.x86_64.rpm | head -30 # 3. 看 sshd 的链接情况(把二进制从包里抽出来验) rpm2cpio openssh-server-10.0p1-1.el9.x86_64.rpm | cpio -idmv ./usr/sbin/sshd ldd ./usr/sbin/sshd | grep -E 'ssl|crypto'

第 2 条是防冲突的关键。如果清单里出现/usr/bin/ssh,说明你的--libdir或者%files写岔了,装的时候必然和系统的 openssh-clients 打架。第 3 条期望看到libssl.so.3 => /opt/openssl-3.5.0/lib64/libssl.so.3,如果指向/usr/lib64/libssl.so.3,说明--with-ssl-dir没生效,回去重编,别抱侥幸心理往下装。

4. 安装与加固:把新包落进 Rocky 9.5 且不打断现有会话

到这一步包已经在手上了,真正的心跳时刻才开始。装包顺序错了会触发文件冲突,配置改猛了会把自己关在门外,SELinux 忘了打标签会让服务起不来。这一章按顺序做,每一步都有回退点。

4.1 安装顺序:先 OpenSSL 前缀包,再 OpenSSH 四件套

# 1. 先装 OpenSSL 3.5.0 前缀包,它不冲突,可以放心装 dnf install -y ./openssl35-3.5.0-1.el9.x86_64.rpm # 2. 确认库路径已注册 ldconfig -p | grep openssl-3.5.0 # 3. 再装 OpenSSH 四件套,--allowerasing 允许替换同名旧包 dnf install -y --allowerasing \ ./openssh-10.0p1-1.el9.x86_64.rpm \ ./openssh-clients-10.0p1-1.el9.x86_64.rpm \ ./openssh-server-10.0p1-1.el9.x86_64.rpm # 4. 立刻校验版本与链接 rpm -q openssh openssh-server openssh-clients ldd /usr/sbin/sshd | grep ssl

--allowerasing的作用是允许 dnf 移除与新包文件冲突的旧包,不加这个参数会直接报file /usr/bin/ssh from install of openssh-clients-10.0p1 conflicts with file from package openssh-clients-8.7p1。这个参数有风险——它可能在你不注意的时候顺带删掉别的包——所以装完立刻执行第 4 条,把版本和链接都确认一遍。四个包要一次性装完,分批装会在中间状态产生sshd和ssh版本不一致的情况,sshd -t会报协议不匹配。

如果dnf install因为依赖环卡住,退一步用rpm -Uvh --replacefiles --replacepkgs手工装,但装完要跑一次dnf check确认依赖树完整。

4.2 sshd_config 加固:openssh加固 最值得先动的十一个参数

配置文件不建议整份覆盖,用sshd_config.d/目录分片管理,出问题好定位。Rocky 9.5 的/etc/ssh/sshd_config首行有Include /etc/ssh/sshd_config.d/*.conf,把加固项单独写一个文件:

# /etc/ssh/sshd_config.d/99-hardening.conf # 认证策略:禁密码、禁交互式、只留公钥 PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no UsePAM yes MaxAuthTries 3 LoginGraceTime 30 # 会话控制:断连回收与转发限制 ClientAliveInterval 300 ClientAliveCountMax 2 X11Forwarding no AllowTcpForwarding no AllowAgentForwarding no # 算法白名单:只留 10.0p1 支持且安全的套件 KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,ecdh-sha2-nistp256 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256 PubkeyAcceptedAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

PermitRootLogin prohibit-password和PasswordAuthentication no这两条是必须成对出现的。只写前者,root 还能用密码登录;只写后者,普通用户禁了密码但 root 没禁。KbdInteractiveAuthentication no在 10.0p1 里尤其要写,因为旧的ChallengeResponseAuthentication已经不存在了,配置文件里如果还留着这一行,sshd 会直接报Bad configuration option拒绝启动。

算法白名单这一组是最容易出事的地方。Ciphers只留 AEAD 套件、MACs只留 ETM 套件,安全性上没问题,但如果你的环境里有老旧的网络设备、堡垒机、自动化脚本客户端,它们可能只支持aes128-cbc或hmac-sha1,配完就连不上。稳妥做法是第一次先不加三行算法限制,等新版本跑稳一周,再按客户端能力逐个收紧。真要在生产上收紧,提前用ssh -Q cipher、ssh -Q mac、ssh -Q kex在客户端侧确认支持列表。

4.3 预检与重载:sshd -t不通过就别 reload

# 1. 语法检查,这一步必须 0 退出码才继续 /usr/sbin/sshd -t -f /etc/ssh/sshd_config # 2. 打印最终生效配置,确认分片文件被正确 Include /usr/sbin/sshd -T | grep -E 'passwordauth|permitrootlogin|ciphers|kexalgorithms' # 3. 平滑重载,不中断现有连接 systemctl reload sshd # 4. 看状态与最近日志 systemctl status sshd --no-pager journalctl -u sshd -n 30 --no-pager

第 1 条是黑匣子。sshd -t会把所有配置项过一遍,包括 Include 进来的分片、算法名是否合法、主机密钥文件是否存在、权限是否合规。它报错的行号有时不准(尤其是 Include 场景),但错误关键词很明确,照着搜基本能定位。

第 3 条用reload而不是restart。reload给 sshd 发 SIGHUP,让它重新读配置,已经在线的连接不受影响;restart会把 sshd 主进程停掉再起,如果新配置有隐藏问题,你就彻底断了。真的需要restart时,先确认救生艇会话还在,并且那台机器有带外管理(IPMI、云控制台 VNC)可用。

4.4 PAM、SELinux 与主机密钥:三个装完必查的细节

# 1. 主机密钥:确认 ed25519 和 rsa 都在,DSA 的旧文件删掉 ls -l /etc/ssh/ssh_host_* rm -f /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub # 2. 权限:私钥必须 600,目录 755,属主 root chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*_key.pub chown root:root /etc/ssh/ssh_host_* # 3. SELinux 上下文恢复 + 端口标签(如果改了非 22 端口) restorecon -Rv /etc/ssh semanage port -l | grep ssh semanage port -a -t ssh_port_t -p tcp 2222 # 4. PAM 配置确认:sshd 的 PAM 栈要能读到 grep -E '^(auth|account|session)' /etc/pam.d/sshd | head -8

第 3 条的restorecon是装完 RPM 之后必跑的一步。RPM 的%post脚本里一般会带,但如果你手工覆盖过配置文件或者从别处拷过密钥,上下文就乱了,ls -Z一看是unconfined_u:object_r:user_home_t,sshd 立刻拒绝使用该主机密钥。semanage port -a改端口标签这条命令,只在真的改了 Port 时才需要执行,重复执行会报ValueError: Port tcp/2222 already defined,改用-m修改。

第 4 条容易被跳过。UsePAM yes打开后,sshd 会走/etc/pam.d/sshd这个栈,如果升级过程中这个文件被旧包的%post覆盖或者被新包改坏,认证会静默失败——密码输对了也进不去,日志里只有Failed password,看着像密码错了,其实是 PAM 栈断链。装完比对一下备份里的版本,diff /root/ssh-config-backup-*/etc/pam.d/sshd /etc/pam.d/sshd一眼就能看出来。

5. 避坑与排查:升级 OpenSSH 后连不上、起不来的五类现场

下面五条是我在几个环境里真实碰到过的,按现象、原因、解决三段写。碰到问题时先别乱改配置,对着这五条找最像的那条,能省掉大半排查时间。

5.1 现象:客户端报no matching host key type found,服务端日志Unable to negotiate

原因:OpenSSH 10.0p1 默认不再协商基于 SHA-1 的ssh-rsa签名,而老客户端(旧版 PuTTY、某些网络设备的 SSH 客户端、写死算法的自动化脚本)还在用这一套。同时 DSA 主机密钥已经被彻底移除,如果客户端指定ssh-dss,直接没有交集。

解决:不要试图在服务端把ssh-rsa加回HostKeyAlgorithms,10.0p1 已经不接受这个值,加了会报配置错误。正确做法是让客户端改用rsa-sha2-256或升级客户端。临时缓解可以在服务端保留 RSA 主机密钥(它仍然能用 SHA-2 签名),同时确保 ED25519 主机密钥存在,客户端优先走 ED25519。实在没法升级的老设备,单独开一个Match Address段降级算法,别全局放开。

5.2 现象:systemctl start sshd失败,journalctl报error while loading shared libraries: libcrypto.so.3: cannot open shared object file

原因:sshd 编译时链接了/opt/openssl-3.5.0/lib64下的库,但 rpm 包里的二进制没有写死 rpath,或者%post里忘了跑ldconfig,systemd 启动时环境里没有LD_LIBRARY_PATH,找不到库。

解决:ldd /usr/sbin/sshd | grep crypto先确认路径,如果是not found,在/etc/ld.so.conf.d/openssl35.conf里写一行/opt/openssl-3.5.0/lib64,然后ldconfig,再systemctl daemon-reload && systemctl restart sshd。根治办法是回 3.2 节给 configure 加-Wl,-rpath,重编一次。不要用systemctl edit塞Environment=LD_LIBRARY_PATH=...,这对 sshd 这种特权进程不可靠,子进程还会丢环境。

5.3 现象:dnf install报file /usr/bin/ssh from install of openssh-clients-10.0p1 conflicts with file from package openssh-clients-8.7p1

原因:新旧包的文件路径完全重叠,dnf 的默认事务策略拒绝覆盖。这是 RPM 打包的正常现象,不是包坏了。

解决:加--allowerasing让 dnf 移除旧包再装新的,或者先dnf remove openssh-clients再单独装 —— 但后者有风险,中间状态可能连 ssh 客户端都没有,回滚都做不了。稳妥做法是四件套一次性dnf install --allowerasing ./openssh*.rpm,事务里一起替换。执行前把/root/ssh-rollback里的旧包确认一遍,出事直接dnf install /root/ssh-rollback/*.rpm退回去。

5.4 现象:密码输对了也登不进,日志里只有Failed password,/var/log/secure没有更详细的线索

原因:UsePAM yes打开了,但/etc/pam.d/sshd这个 PAM 栈在升级过程中被改坏或缺失模块,认证阶段直接失败。也可能是KbdInteractiveAuthentication被设为yes而PasswordAuthentication no,两条路径打架。

解决:先diff备份里的 pam 配置和当前文件,缺什么补什么;再sshd -T | grep -E 'passwordauth|kbdinteractive|usepam'看最终生效值,确认三条互斥关系没写反。排查阶段可以临时sshd -ddd -p 2222在前台跑一个调试实例,把客户端连到 2222 端口,日志会打到终端上,比翻 journalctl 清楚得多。调完记得 kill 掉,别留在后台。

5.5 现象:装完扫描器仍然报 OpenSSH 版本低,或者rpm -q openssh显示的版本没变

原因:只装了openssh-server,openssh和openssh-clients还是旧版;或者装完之后dnf做了一次自动更新,旧包被重新拉了回来覆盖掉。

解决:rpm -qa | grep openssh一条条对,四个包版本必须一致。再跑dnf versionlock add openssh openssh-server openssh-clients openssh-askpass把版本锁住,防止后续更新回退。最后用strings /usr/sbin/sshd | grep -i 'openssh_10'确认二进制里真的是 10.0p1,别只看 rpm 数据库——数据库和文件不一致的情况在手工覆盖过的机器上并不罕见。

6. 验证跑通:三个终端交叉确认新版本真的生效

版本号对了不等于服务能用。我的验证习惯是开三个终端:终端 A 全程挂着旧连接不动,终端 B 用来执行命令和新开连接,终端 C 从另一台机器发起连接。三边交叉确认,任何一边异常都能立刻定位是哪一层的问题。

先做本地自证,这几条命令的输出要逐条对:

# 1. 二进制版本与算法能力 /usr/sbin/sshd -V 2>&1 | head -1 ssh -V ssh -Q kex | head -5 ssh -Q cipher | grep -c gcm # 2. 运行时链接确认(最关键的一条) ldd /usr/sbin/sshd | grep -E 'ssl|crypto' # 期望:libssl.so.3 => /opt/openssl-3.5.0/lib64/libssl.so.3 # 3. 配置最终生效值 /usr/sbin/sshd -T | grep -E 'permitrootlogin|passwordauthentication|ciphers' # 4. 新建密钥对并走本机回环,验证密钥认证链路 ssh-keygen -t ed25519 -f /tmp/verify_key -N '' ssh-copy-id -i /tmp/verify_key.pub localhost ssh -i /tmp/verify_key -o BatchMode=yes localhost 'echo OK; ssh -V'

第 4 条是一次端到端自检:用 ED25519 密钥、强制非交互模式登录本机,如果返回OK加版本号,说明密钥认证、PAM、会话建立、算法协商全部走通。BatchMode=yes的作用是禁止任何交互提示,密码提示、主机指纹确认都会被当成失败,这样测出来的结果才是干净的。

然后从终端 C 发起真实连接,重点看协商细节:

# 客户端侧:-vvv 看协商过程,重点抓 kex / host key / cipher 三行 ssh -vvv -p 22 -i ~/.ssh/id_ed25519 user@target 2>&1 | \ grep -E 'kex:|host key algorithm|server->client cipher|Authenticated'

期望输出类似kex: algorithm: sntrup761x25519-sha512@openssh.com、host key algorithm: ssh-ed25519、Authenticated to target。如果kex那行显示的是curve25519-sha256而不是 sntrup 那一条,说明客户端版本稍老,不影响使用,只是没走最强的 KEM 套件。

最后把验证项整理成一张表,变更单里附上,比写文字描述省事:

验证项命令期望结果
版本一致rpm -qa | grep openssh四包均为 10.0p1
库链接ldd /usr/sbin/sshd | grep ssl指向 /opt/openssl-3.5.0
配置合法/usr/sbin/sshd -t退出码 0,无输出
服务状态systemctl is-active sshdactive
密钥认证ssh -i /tmp/verify_key localhost免密登录成功
算法协商ssh -vvv ... | grep kex非 ssh-rsa,非 DSA
旧会话保留终端 A 执行who am i仍在线,未断开
SELinuxls -Z /etc/ssh/ssh_host_*_keysshd_key_t

这张表跑完,才算真的装完了。别嫌麻烦,我见过太多"装完了"其实是只在测试机装完了。

从那以后我每次动 sshd,都强制走一遍固定动作:先开两个会话、sshd -t过一遍、reload而不是restart、新会话验证通过之后才关救生艇。这套动作花不了五分钟,但救过我三次——一次是算法白名单写死了老设备连不上,一次是 rpath 没生效库找不到,一次是 PAM 文件在升级里被覆盖。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进