☰
麒麟V10离线升级OpenSSH:RPM回滚与配置回填
2026/10/28 22:28:58 网站建设 项目流程

简介:针对国产麒麟操作系统与 CentOS 环境下 OpenSSH 版本偏旧、联网升级不便的问题,这份离线升级包提供了开箱即用的解决方案,适合运维人员、系统管理员及需要在隔离内网中完成安全加固的从业者使用。包体共 4 个文件,以 3 个 gz 源码压缩包和 1 个 sh 脚本为主,整体约 20.04MB;源码包分别对应 OpenSSL 加密库、OpenSSH 套件与 zlib 压缩库,脚本则封装了升级流程,执行后即可完成编译、替换与版本更新。目前已有 2095 人次学习下载。读者拿到后即可获得一套不依赖外网的三件套升级素材,省去逐一手动查找兼容版本的环节;脚本把复杂的编译、备份与替换步骤简化为一条命令,降低了误操作风险。对希望理解 OpenSSH 升级链条、梳理依赖关系的读者,源码包本身也可作为本地验证与排错的基础材料。

1. 麒麟 V10 离线升 openssh:一次掉线换来的版本匹配清单

在麒麟操作系统上做 openssh 的离线一键升级,真正容易出问题的地方往往不是升级那几分钟,而是升级完的那三十秒:新 sshd 已经起来,当前会话还连着,你以为成功了,退出重连才发现 22 端口没人监听。我见过一台内网跳板机就这么变成了连不上的机器,最后靠机房现场接显示器才救回来。这类机器的共同点是:不能连外网、没有本地 yum 源、sshd 是唯一入口,升级包只能靠 U 盘拷进去。因此,这套流程真正要解决的是“装的过程中永远留一条能回去的路”,而不只是“装上 9.x”。下面这套流程适合三类人:要给漏洞扫描交差、把 8.2p1 往 9.x 抬的运维;要在离线环境批量刷几十台麒麟的内网管理员;以及第一次碰 rpm 覆盖升级、怕把机器搞失联的开发者。

2. 环境清点与离线包准备:麒麟 V10 上要把哪几个 rpm 凑齐

离线升级失败,十次里有六次是包没凑齐:不是少了openssh-server,就是依赖的libcrypto版本对不上。在线环境一条yum update就解决的事,离线环境得先自己把依赖图展开,再一个个拷进去。这一章先把系统底子量清楚,再确定包清单,最后把目录规划好,后面脚本才有地方落脚。

2.1 先把系统底子量一遍

麒麟 V10 分 SP1、SP2、SP3 几个小版本,底座差别不大,但 glibc、openssl、pam 的小版本会直接影响你能不能直接用社区编好的 rpm。下面这张表是我在现场最常遇到的组合,你务必用命令自己核一遍,不要照抄,尤其是 openssl 那一行。

组件常见版本核对命令为什么必须确认
系统Kylin V10 SP1/SP2/SP3cat /etc/kylin-release决定用 el8 系还是别的包
内核4.19 系列uname -r只影响排查,不影响装包
glibc2.28rpm -q glibc低于 2.28 的包直接装不上
openssl1.1.1 系列openssl version9.x 编译时链接的就是它
zlib1.2.11rpm -q zlibsshd 压缩算法依赖
pam1.3.1rpm -q pam密码认证走 pam 栈
现有 openssh8.2p1 居多ssh -V决定回滚包版本
# 一次性把底子打出来,存成文件方便对比另一台机器 { cat /etc/kylin-release uname -r rpm -q glibc openssl zlib pam ssh -V rpm -qa | grep -E '^openssh' } | tee /tmp/kylin_base_info.txt

这段脚本输出的是“升级前基线”,它有两个用途:一是选包,二是万一升级后某个业务报错,你可以拿这份基线证明“以前是这个版本”。ssh -V的输出走的是 stderr,所以上面用2>&1或者直接不管它都能看到,写进文件时用ssh -V 2>>更保险。rpm -q openssh只会返回主包,openssh-server、openssh-clients、openssh-keycat要单独查,漏了任何一个,升级后都会出现“版本对但功能缺”的怪现象。

提示:麒麟的部分加固镜像会把/etc/os-release改成自定义内容,判断系统时用/etc/kylin-release更准。

2.2 离线包的组成:四个主包加三个依赖

openssh 在 rpm 层面被拆得很碎,升级时必须整组一起换,不能只换 server。我一般按下面这个清单准备,少一个都会在rpm -Uvh时报依赖错误。

packages/ ├── openssh-9.6p1-1.el8.x86_64.rpm # 主包,含 ssh-keygen 等公共工具 ├── openssh-server-9.6p1-1.el8.x86_64.rpm # sshd 本体,最关键 ├── openssh-clients-9.6p1-1.el8.x86_64.rpm # ssh/scp/sftp 客户端 ├── openssh-keycat-9.6p1-1.el8.x86_64.rpm # 可选,密钥目录服务用得上 ├── openssl-libs-1.1.1*.rpm # 仅当目标包要求更高版本时才带 ├── zlib-1.2.11*.rpm # 同上 └── pam-1.3.1*.rpm # 同上

后三个依赖包的原则是:能不换就不换。openssl-libs是全系统的地基,动它等于动半个操作系统,很多业务程序的.so链接会跟着断。绝大多数为 el8 编的 openssh 9.x 包,对 1.1.1 系列是兼容的,只有当rpm -Uvh明确报libcrypto.so.3 is needed时,才考虑补 openssl 3.x,而且必须新旧 so 并存,不能覆盖。

版本号的选择上,9.3p2 和 9.6p1 是内网里用得最多的两个。9.3p2 偏保守,改动小;9.6p1 修的安全项更多,扫描器更容易过。不要追最新的大版本,新版本对老客户端的算法收得更紧,内网里那些跑了十年的老终端很可能连不上。

2.3 校验与落地目录:/opt/ssh_upgrade 的三层结构

包拷进机器后第一件事是校验,不是解压。U 盘拷贝、跨网段传输都可能出现文件截断,一个几百 KB 的 rpm 少了几十字节,rpm -Uvh会报not an rpm package,你会以为是包本身有问题,白折腾半小时。

cd /opt/ssh_upgrade/packages # 逐个校验 rpm 头部完整性,不通过的直接重拷 for f in *.rpm; do rpm -K "$f" || echo "FAIL: $f" done # 再看一眼每个包到底要什么依赖,提前暴露缺包 rpm -qpR openssh-server-*.rpm | sort -u

rpm -K做的是摘要校验,只验证文件本身没坏,不验证签名(离线环境通常没导入 GPG key,加--nosignature更省事)。rpm -qpR打印的是这个包声明的依赖列表,输出里如果有libcrypto.so.3()(64bit)这种带 so 名字的,就说明必须补 openssl 3.x;如果是openssl >= 1.1.1这种带版本号的,基本现有环境就满足。

目录我固定分成三层:packages放入库的包,backup放每次升级自动生成的备份,logs放日志。脚本里所有路径都从这里取,好处是换一台机器只要把这个目录整体拷过去,脚本一个字不用改。

mkdir -p /opt/ssh_upgrade/{packages,backup,logs} chmod 700 /opt/ssh_upgrade/backup # 备份里有主机密钥,权限必须收紧

backup目录给 700 不是形式主义,/etc/ssh里躺着ssh_host_rsa_key这类私钥,备份目录权限放开等于把主机身份公开了。这一点在等保检查里是会被单独挑出来的。

3. 升级前的后路:备份、旁路和一份 sshd -T 快照

升级 sshd 和升级别的软件有个本质区别:你正在用的这条连接,就是它提供的。所以升级前的准备工作不是“以防万一”,而是流程的必需环节。这一章三件事,缺任何一件我都不会按下回车。

3.1 三份备份,一份都不能省

第一份是配置目录。/etc/ssh整个目录原样拷走,注意用cp -a而不是cp -r,要保留属主和权限位,否则回滚时密钥权限不对,sshd 直接起不来。

BAK=/opt/ssh_upgrade/backup/$(date +%Y%m%d%H%M) mkdir -p "$BAK" cp -a /etc/ssh "$BAK/etc_ssh" ls -l "$BAK/etc_ssh" | head -5

$(date +%Y%m%d%H%M)让每次备份独立成目录,多次升级不会互相覆盖。cp -a等价于-dR --preserve=all,属主、权限、时间戳、软链接全都保留,回滚时可以直接cp -a盖回去。

第二份是旧 rpm 包。这一步最容易被跳过,但它是唯一能让你退回原版本的东西。离线环境里旧包往往早就没了,所以要么提前从安装光盘或本地源里find出来,要么在升级前用yumdownloader --resolve在有网的机器上抓好再拷进来。

mkdir -p "$BAK/old_rpms" for p in $(rpm -qa | grep -E '^openssh'); do # 在本地找同名 rpm,找到就复制;找不到会在回滚时抓瞎,所以要提前准备 find / -xdev -name "${p}.rpm" -exec cp -n {} "$BAK/old_rpms/" \; 2>/dev/null done ls "$BAK/old_rpms"

-xdev限定不跨文件系统,避免find扫到挂载的网络盘上卡住。如果这条命令跑完old_rpms是空的,先停下来,去把旧包找来,不要继续。

第三份是当前运行参数快照。sshd -T会把 sshd 实际生效的配置全部打印出来,包括那些写在sshd_config.d/里、你根本没注意到的行。这是升级后比对配置有没有被覆盖的唯一依据。

/usr/sbin/sshd -T > "$BAK/sshd_T_before.txt" 2>&1 wc -l "$BAK/sshd_T_before.txt"

-T是 extended test 模式,只解析配置并输出结果,不真正启动服务,所以可以随时执行。输出里包含port、permitrootlogin、passwordauthentication、pubkeyacceptedalgorithms这些关键项,升级完再跑一遍,diff一下就知道新包把你的配置改成了什么样。

3.2 开一条不依赖 sshd 的旁路

这一步是血泪经验。远程升级 sshd,你必须假设升级后 SSH 会断,并且断了之后还能进去。可行的旁路有三种,按可靠性排序:云主机的 VNC 控制台、机房的物理显示器加键盘、同网段的带外管理口。三种都没有的话,还有第四种土办法:提前开一个screen或tmux会话跑升级脚本,至少脚本输出不会随连接断开而丢失。

# 在旁路终端里先确认能登录,再动 sshd # 顺便确认备用账号可用,不要只用 root 一条路 id sshbak 2>/dev/null || useradd -m -G wheel sshbak passwd sshbak

wheel组在麒麟上默认有 sudo 权限,加进去是为了旁路登录后还能改配置。备用账号必须提前建、提前试登一次,等升级完再想起来建,已经登不进去了。

注意:不要用“把 sshd 端口临时改成 2222 再升级”这种办法当后路,新包如果没起来,两个端口一起没。

3.3 用 sshd -T 把当前运行参数导出来

除了整份快照,我还会单独把几个高风险项拎出来存一份,方便升级后逐条对照。

/usr/sbin/sshd -T 2>/dev/null | grep -Ei \ '^(port|listenaddress|permitrootlogin|passwordauthentication|pubkeyauthentication|authorizedkeysfile|subsystem|usepam|allowusers|allowgroups)' \ | tee "$BAK/key_params.txt"

authorizedkeysfile这一项要特别留意,很多单位会把它改到/etc/ssh/keys/%u这类非默认路径,新包的默认配置文件里是.ssh/authorized_keys,一旦配置被覆盖,所有人的公钥认证全部失效,表现就是“密码能登、密钥登不上”。subsystem那行决定 sftp 能不能用,路径在不同发行版里是/usr/libexec/openssh/sftp-server或/usr/lib/openssh/sftp-server,升级后路径不存在的话,scp好用但sftp直接报错。

usepam yes也值得单独看一眼。关掉 PAM 的机器上,密码策略、登录审计、账号锁定全都不生效,升级后被新配置重置成默认值,等保一查就是问题项。

4. 一键脚本怎么写:从 rpm -Uvh 到配置回填的五个阶段

所谓“一键”,不是把所有命令堆进一个文件,而是把“检查、备份、升级、回填、自检”这五段串成一条有失败出口的链。下面这个脚本我在几十台麒麟上跑过,结构比内容重要。

4.1 骨架:检查、备份、升级、回填、自检

#!/bin/bash # ssh_upgrade.sh —— 麒麟 V10 离线升级 openssh set -euo pipefail # 任何一步失败立即退出,未定义变量报错,管道错误不吞 PKG_DIR=/opt/ssh_upgrade/packages BAK=/opt/ssh_upgrade/backup/$(date +%Y%m%d%H%M) LOG=/opt/ssh_upgrade/logs/upgrade_$(date +%Y%m%d%H%M).log mkdir -p "$BAK" "$(dirname "$LOG")" exec > >(tee -a "$LOG") 2>&1 # 屏幕和日志同时留一份 echo "[1/5] 环境检查" grep -qi kylin /etc/kylin-release || { echo "非麒麟系统,退出"; exit 1; } [ "$(id -u)" -eq 0 ] || { echo "必须 root 执行"; exit 1; } ls "$PKG_DIR"/openssh-server-*.rpm >/dev/null || { echo "缺 openssh-server 包"; exit 1; } echo "升级前版本: $(rpm -q --qf '%{VERSION}-%{RELEASE}\n' openssh-server)" echo "[2/5] 备份" cp -a /etc/ssh "$BAK/etc_ssh" /usr/sbin/sshd -T > "$BAK/sshd_T_before.txt" 2>&1 || true mkdir -p "$BAK/old_rpms" for p in $(rpm -qa | grep -E '^openssh'); do find / -xdev -name "${p}.rpm" -exec cp -n {} "$BAK/old_rpms/" \; 2>/dev/null || true done echo "备份目录: $BAK"

前两段的关键是set -euo pipefail。不加-e的话,某个包安装失败脚本还会继续往下走,最后跑完告诉你“升级成功”,而实际上只装了一半,这种半成品状态比彻底失败更难排查。exec > >(tee -a "$LOG") 2>&1把标准输出和标准错误都导向 tee,屏幕能看、日志也有,事后出问题不用靠回忆。

[ "$(id -u)" -eq 0 ]这个判断看着多余,但真有人用普通账号加 sudo 跑,cp -a /etc/ssh时权限保留失败,密钥属主变成普通用户,sshd 起来就报权限错误。

第三段是升级本体,也是最需要克制的地方:

echo "[3/5] 覆盖升级" cd "$PKG_DIR" # 用 -U 覆盖安装,绝不能用 -e 先卸载:卸载 openssh 会连带删掉依赖它的包 if ! rpm -Uvh --nodeps openssh-*.rpm; then echo "升级失败,开始回滚" rpm -Uvh --nodeps --oldpackage "$BAK/old_rpms"/*.rpm exit 1 fi

rpm -U是升级语义,装新版本的同时把旧版本的文件替换掉,配置文件会生成.rpmnew或.rpmsave。--nodeps是离线环境下的常用做法,因为依赖检查常常报“缺 openssl >= 3.0”,但实际运行时 1.1.1 是够的,加上它可以让安装继续。这个参数有风险,用之前一定确认rpm -qpR里没有真正缺的 so。

绝对不要用rpm -e openssh再装新的。卸载的一瞬间,机器上所有依赖 openssh 的包(包括某些监控 agent、自动化工具)都会进入依赖断裂状态,而且卸载过程会把/etc/ssh里的主机密钥一起带走,重装后主机指纹变了,所有客户端的 known_hosts 全部报冲突。

4.2 配置回填与权限、上下文修复

新包装完,/etc/ssh/sshd_config大概率已经被替换成默认版本。这时候不要急着重启,先把备份的配置盖回去,再修权限。

echo "[4/5] 回填配置" if [ -f "$BAK/etc_ssh/sshd_config" ]; then cp -a "$BAK/etc_ssh/sshd_config" /etc/ssh/sshd_config fi # 主机私钥必须是 600,公钥 644,属主 root,否则 sshd 拒绝加载 chown root:root /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*_key.pub # SELinux 开启时,手工 cp 的文件上下文会变成 user_home_t,必须重置 restorecon -Rv /etc/ssh 2>/dev/null || true # 语法校验:只解析不启动,有错会直接打印行号 /usr/sbin/sshd -t

chmod 600这两行是升级后最常见故障的解药。原因很直白:sshd 启动时检查私钥权限,只要 group 或 other 有读位就拒绝加载,日志里写的是bad permissions,但很多人只看到“服务起不来”就去翻别的。restorecon也不能省,SELinux enforcing 状态下,文件权限对了、上下文不对,sshd 一样被拒,报错是Permission denied,看着像权限问题其实是标签问题。

sshd -t是最后一道闸,返回非 0 就说明配置有语法错误,脚本应该在这里停下或者回滚,绝不能带着错误配置去systemctl restart。老版本里有些配置项在新版本被废弃(比如RhostsRSAAuthentication、UsePrivilegeSeparation),-t会直接告诉你哪一行不认识,照着注释掉就行。

4.3 重启与失败自动回滚

最后一段负责让新 sshd 接管 22 端口,并在失败时自动退回。

echo "[5/5] 重启与自检" systemctl daemon-reload systemctl restart sshd || { echo "重启失败,回滚配置与包" cp -a "$BAK/etc_ssh/." /etc/ssh/ rpm -Uvh --nodeps --oldpackage "$BAK/old_rpms"/*.rpm || true systemctl restart sshd || echo "回滚后仍失败,请走旁路" exit 1 } sleep 2 ss -lntp | grep -q ':22' || { echo "22 端口未监听"; exit 1; } echo "升级后版本: $(rpm -q --qf '%{VERSION}-%{RELEASE}\n' openssh-server)" echo "客户端版本: $(ssh -V 2>&1)"

systemctl daemon-reload在替换了 unit 文件对应的二进制后有必要跑一次,尤其是从 7.x 跨到 9.x 这种跨度。sleep 2是给 sshd 一点时间完成监听,紧接着ss -lntp | grep ':22'确认端口真的在,这一步比看systemctl status更实在——服务状态是 active 但端口没起来的“假活”情况我遇到过不止一次,通常是配置里ListenAddress写错了地址。

回滚段落里cp -a "$BAK/etc_ssh/." /etc/ssh/结尾的/.是个细节,它拷贝的是目录内容而不是目录本身,避免出现/etc/ssh/etc_ssh这种套娃路径。

脚本跑完不要急着关会话,先按第 6 章的办法验证一遍。

5. 升级翻车排查:sshd 起不来和 root 登不上的六种现场

升级失败的症状就那么几种,但每一种背后的原因可能完全不同。下面五条是我这些年真实遇到并且记录下来的,按“现象、原因、解决”整理,照着症状对就能定位。

5.1 服务端起不来:端口、密钥、配置语法

第一条:no hostkeys available -- exiting

现象:systemctl restart sshd失败,journalctl -u sshd -n 30里最后一行是sshd: no hostkeys available -- exiting。

原因:主机密钥文件权限被改宽或者属主变了。rpm -Uvh覆盖安装时会重新打包/etc/ssh下的文件,权限位可能从 600 变成 644,也可能是你手工cp时用了-r丢了属主,sshd 出于安全考虑直接拒绝使用。

解决:把权限收回去,并重置 SELinux 上下文。

chown root:root /etc/ssh/ssh_host_* chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*_key.pub restorecon -Rv /etc/ssh /usr/sbin/sshd -t && systemctl restart sshd

第二条:Bind to port 22 on 0.0.0.0 failed: Address already in use

现象:服务起不来,日志明说 22 端口被占。

原因:老的 sshd 进程还在跑。rpm -Uvh只替换磁盘上的二进制文件,不会杀正在运行的进程,旧进程继续持有 22 端口;另一种情况是系统里同时存在sshd.service和ssh.service两个 unit,systemctl restart sshd启了一个,另一个的开机自启又把端口抢回去。

解决:先看谁占着端口,再统一服务名。

ss -lntp | grep ':22' ps -ef | grep '[s]shd' systemctl stop ssh 2>/dev/null || true systemctl disable ssh 2>/dev/null || true # 只保留 sshd 一个入口 systemctl restart sshd

第三条:Job for sshd.service failed because the control process exited with error code

现象:只有这一句笼统的报错,没有任何细节。

原因:systemctl把 sshd 自己的报错吞了,真正的错误在 journald 里或者sshd -t的输出里。

解决:绕开 systemd 直接跑一次前台启动,报错会完整打出来。

/usr/sbin/sshd -t # 先验配置语法 /usr/sbin/sshd -D -ddd # 前台调试模式,日志级别拉满,Ctrl+C 退出 journalctl -u sshd -n 50 --no-pager

-D是不 fork 到后台,-ddd是最高调试级别,会打印密钥加载、配置解析、认证过程的每一步。这一步能看到的问题,比翻十遍sshd_config都管用。

5.2 客户端连不上:算法不兼容与认证方式

第四条:Permission denied (publickey),以前明明能登

现象:升级前密钥登录正常,升级后所有用户公钥认证全部失败,密码登录也不收。

原因有两个层面。一是sshd_config被新包的默认文件覆盖了,PubkeyAuthentication、AuthorizedKeysFile、PermitRootLogin全部回到默认值,而默认的AuthorizedKeysFile是.ssh/authorized_keys,如果原来改到了别的路径,公钥自然找不到。二是 openssh 8.8 之后默认不再接受ssh-rsa(RSA-SHA1)签名,内网里那些用老ssh-keygen生成的 RSA 密钥、老版本客户端,全部被拒。

解决:先回填配置,再按需放行老算法。

# 比对升级前后的生效配置,找出被改掉的项 /usr/sbin/sshd -T | grep -Ei 'pubkey|authorizedkeys|permitroot' > /tmp/after.txt diff /opt/ssh_upgrade/backup/*/sshd_T_before.txt /tmp/after.txt

确认是算法问题的话,在/etc/ssh/sshd_config里临时补两行,注意是临时:

# 兼容老客户端,等终端全部换成 ed25519 密钥后删掉这两行 PubkeyAcceptedAlgorithms +ssh-rsa HostKeyAlgorithms +ssh-rsa

改完sshd -t校验再重启。长期方案是推动客户端换 ed25519 密钥,ssh-keygen -t ed25519生成一对,替换掉authorized_keys里的老 RSA 条目,这两行就能删了。

第五条:no matching key exchange method found/no matching host key type found

现象:客户端根本连不上,连密码提示都出不来,直接报算法不匹配。

原因:服务端 9.x 默认砍掉了diffie-hellman-group1-sha1、ssh-dss这类弱算法,而内网里跑着的老终端工具、老版本 ssh 客户端只会这几样。

解决:优先升级客户端;客户端动不了,就在服务端补回来,但要限定范围。

# 只对特定来源网段放行弱算法,用 Match 块收敛影响面 Match Address 10.0.0.0/8 KexAlgorithms +diffie-hellman-group14-sha1 HostKeyAlgorithms +ssh-rsa

Match块必须放在配置文件末尾,因为它之后的所有配置都只对匹配的地址生效。这样公网侧仍然是严格算法,只有内网老设备能连,把风险面压到最小。

5.3 升级把别的工具带崩了

第六条:升级后yum、监控 agent 或某个业务程序报libcrypto.so.1.1: cannot open shared object file

现象:openssh 本身好用,但机器上别的程序起不来了,或者ssh-keygen报符号找不到。

原因:升级过程中顺手把openssl-libs也升到了 3.x,覆盖掉了libcrypto.so.1.1。系统里大量程序是链接 1.1 编的,so 一换全部失效。

解决:这是最麻烦的一种,因为影响面远超 ssh。我的做法是回滚 openssl,把 openssh 单独用--nodeps装回去。

rpm -Uvh --nodeps --oldpackage /opt/ssh_upgrade/backup/*/old_rpms/openssl-libs-*.rpm ldconfig # 验证关键程序 ssh -V; yum --version >/dev/null && echo "yum ok"

教训很直接:为了升 ssh 去动 openssl,是把一个可控的小变更换成了一个不可控的大变更。除非目标包明确要求libcrypto.so.3,否则不要碰它。

6. 装完怎么验证:三条命令加上一次真实重连

升级脚本返回 0 不等于升级成功,只等于“命令都跑完了”。真正的验证要分三层:版本层、配置层、连接层,缺一层都可能留下隐患。

6.1 版本与进程层面的核对

先把最硬的事实确认掉:装的包是什么版本,跑起来的进程是哪个二进制。

rpm -q openssh openssh-server openssh-clients # 三个包的版本必须一致 ssh -V # 客户端版本,输出走 stderr ss -lntp | grep ':22' # 端口在监听,进程是 sshd ps -o pid,lstart,cmd -C sshd # 进程启动时间应是刚才重启的时间

四个输出要互相印证。rpm -q显示 9.6p1,但ssh -V还是 8.2p1,说明/usr/bin/ssh被别的路径覆盖了,which -a ssh查一下是不是/usr/local/bin下还有一份源码编的。ps里进程启动时间如果是几天前,说明systemctl restart实际没生效,旧进程还在跑,这时候所有验证都是假的。

核对项期望结果不符时的排查方向
三个包版本完全一致有包没装上,重跑rpm -Uvh
ssh -V与 rpm 版本一致PATH 里有旧二进制
22 端口LISTEN,进程名 sshd旧进程未退或配置端口变了
进程启动时间本次重启时间服务没真正重启
主机密钥与升级前指纹一致密钥被重新生成,客户端会告警

主机密钥那一行最容易被忽略。rpm -Uvh正常情况下会保留已有密钥,但如果之前用过rpm -e,密钥会被重新生成,所有客户端的known_hosts都会报指纹冲突,用户以为是攻击,实际是升级导致的。

6.2 算法与配置层面的核对

第二层看生效配置有没有跑偏,特别是几个安全项。

/usr/sbin/sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication|pubkeyauthentication|x11forwarding|maxauthtries)' ssh -Q key | head -10 # 服务端支持的密钥类型 ssh -Q cipher | wc -l # 支持的加密算法数量

sshd -T输出的permitrootlogin如果是prohibit-password而你们要求禁止 root 登录,说明配置没回填成功,要回去 diff。ssh -Q key里如果还看到ssh-rsa,说明兼容项生效了,等老客户端换完记得删掉。

还有一项值得单独确认:Subsystem sftp指向的路径在磁盘上是否真实存在。

grep -i '^subsystem' /etc/ssh/sshd_config ls -l /usr/libexec/openssh/sftp-server 2>/dev/null || ls -l /usr/lib/openssh/sftp-server

路径不存在的话,ssh登录完全正常,只有sftp和部分scp会失败,这种“部分功能坏”最难被发现,往往是一个月后有人传文件才报上来。

6.3 真实重连与重启复验

最后一层,也是唯一能证明“真能用”的一层:新开一个终端窗口,从另一台机器连过来。

# 在另一台机器上执行,不要在当前会话里断开 ssh -v 用户名@目标IP 'echo ok; ssh -V' # 密钥、密码、sftp 三种方式各试一次 sftp 用户名@目标IP <<< 'pwd'

ssh -v会打印协商过程,能看到实际用了哪个 KEX、哪个 host key 算法、认证走了 publickey 还是 password。这三样都通了,再考虑关掉原来那个会话。

全部验证通过之后,还有一步别忘:重启一次机器,确认 sshd 开机自启没问题。有些机器升级后手工重启服务正常,重启系统后起不来,原因是新的 unit 文件依赖顺序变了,或者sshd-keygen.service在启动时把密钥权限又改回去了。

systemctl is-enabled sshd # 必须是 enabled reboot # 起来后重新走一遍 6.1 的四条命令

那次之后,我每次升完 sshd 都强制走一遍“另开终端重连、sftp 传一个文件、重启一次机器”这三步,一次都不省。跳过这步省下的十分钟,往往会变成半夜被叫起来处理一台连不上的机器。这套目录结构和脚本我一直放在/opt/ssh_upgrade里没删过,下次换机器直接整包拷过去就能用,希望帮到你。

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

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

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

立即咨询