
简介面向国产麒麟操作系统与 CentOS 运维人员聚焦内网、隔离环境难以联网更新安全组件的实际问题提供 OpenSSH 离线升级所需的源码、依赖与一键执行脚本适合具备基础 Linux 操作能力、需要完成系统安全加固的读者。资源以 rar 压缩包交付共 4 个文件包含 3 个 gz 源码压缩包和 1 个 sh 脚本整体约 20.04MB覆盖 OpenSSH、OpenSSL、zlib 等关键组件及自动化升级入口目录结构精简便于按组件快速定位与校验。已有 2095 人学习下载。读者可据此在断网环境中完成依赖组件与 SSH 服务的版本更新降低手工逐包编译带来的操作失误同时梳理 OpenSSL、zlib 与 OpenSSH 之间的升级顺序和兼容关系并借助脚本固化流程为批量维护、安全补丁落地和同类系统迁移提供可复用参考尤其适合无外网条件的机房部署、漏洞修复与版本统一工作。1. 离线升级不是拷个 rpm 就完事先看清这套资源接住哪三个断点一台放在内网机柜里的国产操作系统服务器运维只能带离线介质进去要把 openssh 版本和相关依赖文件一起抬上去还要保证升级失败能退回来——这就是离线一键升级包要接住的活。它把目标操作系统的基线检查、依赖包收集、备份、安装顺序、sshd 配置校验和回滚串成一条可执行链路适合内网交付、批量运维和不能联网的机器。它不是把 rpm 包拷进去执行一条命令就结束真正值钱的是升级前能判定、升级中能中断、升级后能验收。拿到资源后先别急着跑脚本先看清它按哪个操作系统小版本做基线再看包目录和回滚目录是否齐全。2. 版本与依赖先对齐OpenSSH、OpenSSL、PAM 与本地仓库的选型账离线升级最容易翻车的地方不是rpm -Uvh这条命令本身而是包与操作系统基线对不上。openssh 不是一个孤立的二进制它依赖 openssl 的加密库、PAM 的认证模块、zlib 的压缩库还受 glibc、rpm 数据库、systemd 服务单元影响。国产操作系统不同小版本之间包名、依赖 soname、默认配置路径都可能变化拿一个在其他机器上能装的包集合直接套过来轻则依赖报错重则 sshd 起不来。我的做法是先锁基线再读依赖最后把离线包做成可校验的本地仓库而不是一上来就执行安装。2.1 先锁目标操作系统的基线不要跨大版本猜先看/etc/os-release不是为了记名字而是为了确认目标操作系统的版本号、包管理器和架构。同一台机器上uname -m输出x86_64还是aarch64会直接决定资源包里的 rpm 能不能用rpm -q openssh openssh-server openssh-clients openssl-libs则告诉你当前装的是哪些包、包名是否齐全。有些系统把服务端包叫openssh-server有些拆得更细还有些把客户端和服务端合在同一个包集合里不能凭经验猜。下面这段检查脚本我一般放在升级包根目录先跑一遍再决定要不要继续#!/usr/bin/env bash set -euo pipefail OS_RELEASE/etc/os-release ARCH$(uname -m) echo [1] 操作系统基线 grep -E ^(NAME|VERSION_ID|PRETTY_NAME) $OS_RELEASE || true echo ARCH$ARCH echo [2] openssh 与相关包现状 rpm -q openssh openssh-server openssh-clients openssl-libs 2/dev/null || true echo [3] sshd 依赖动态库 ldd /usr/sbin/sshd | sed -n 1,80p echo [4] 包管理器与本地仓库能力 command -v dnf || true command -v yum || true command -v createrepo_c || true这段脚本的逻辑是先采集事实不修改系统。set -euo pipefail让脚本在变量未定义、管道失败时尽早退出rpm -q在包不存在时返回非零所以后面加了|| true避免检查脚本自己先中断ldd /usr/sbin/sshd用来观察当前 sshd 链接了哪些动态库尤其要看有没有not found。command -v输出的是包管理器路径后面决定用dnf、yum还是直接rpm。参数上唯一需要改的是OS_RELEASE路径常规系统都在/etc/os-release如果目标系统做了裁剪就用实际文件替换。判读时重点看三件事操作系统版本是否与资源包说明一致架构是否匹配ldd是否有缺失库。只要有一项对不上就不要跨大版本硬升。常见做法是把目标机器和资源包基线做成一张对照表版本号、架构、包名、依赖库四列对齐后再进入下一步。这里没有捷径跨大版本猜版本号后面往往要用回滚来收场。2.2 依赖链怎么读ldd、rpm -qR、repoquery 三张表ldd看的是运行时动态库rpm -qR看的是包级依赖repoquery或dnf repoquery看的是仓库范围内的提供关系。三张表不能互相替代。比如ldd显示libcrypto缺失说明运行环境少库rpm -qR显示某个包要求特定 capability说明安装阶段就会卡住repoquery --whatprovides则用来确认本地仓库里有没有包能提供这个 capability。离线环境里没有远程源repoquery只能查本地仓库或已安装的 rpmdb所以先把包集合收全再查。我常用的检查表如下检查项命令判读方式包级依赖rpm -qpR ./rpms/openssh-server-*.rpm输出里出现本地没有的包名或 capability就要补包已安装提供者rpm -q --whatprovides capability返回包名说明系统已有提供者返回空要警惕动态库依赖ldd /usr/sbin/sshd出现not found时先补库不要急着装 sshd本地仓库提供关系dnf repoquery --whatprovides capability只对已配置的本地仓库有效远程源要禁用下面这段脚本把包级依赖逐条查一遍缺什么就打印什么#!/usr/bin/env bash set -euo pipefail PKG./rpms/openssh-server-*.rpm rpm -qpR $PKG | sort -u requires.txt while read -r req; do if ! rpm -q --whatprovides $req /dev/null 21; then echo MISSING: $req fi done requires.txtrpm -qpR只查询包元数据不安装sort -u去重避免同一 capability 反复打印while read -r req逐行读取依赖rpm -q --whatprovides查当前 rpmdb 里谁提供它查不到就输出MISSING。这段脚本的局限是只查已安装提供者不查离线包目录里未来会安装的提供者所以正式升级前还要用rpm -Uvh --test或本地仓库的--assumeno做一次闭包测试。参数上PKG要指向实际的包路径如果有多个包建议改成for PKG in ./rpms/openssh-*.rpm; do ...; done。这里有一条血泪经验不要用--nodeps绕过依赖。--nodeps能让 rpm 数据库先写进去但 sshd 启动时动态库缺失照样失败而且失败现场比依赖报错更难排查。依赖冲突时先看包名、架构、版本段再看是不是混入了其他发行版的包。国产操作系统小版本之间包名相同但依赖版本段不同这种情况只能回到资源包基线重新取包不能靠强制参数糊过去。2.3 离线仓库的目录约定与校验离线升级包不能是一堆散装 rpm。我的目录约定是rpms/放待安装包scripts/放检查、备份、升级、回滚脚本backup/或系统外的备份目录放旧包和配置快照logs/放执行日志根目录放SHA256SUMS和一份包清单说明。文件数量和大小由资源简介负责正文不重复真正要确认的是目录职责清楚升级脚本只从固定目录取包不从当前工作目录随便抓文件。校验分两层第一层是文件完整性用sha256sum -c对资源包做逐文件校验第二层是 rpm 签名用rpm --checksig验证包没有被篡改。离线环境没有导入签名密钥时第二层可能验不过这时至少要把第一层做完整并把校验日志留在logs/里。常见做法是先校验再解压不要先解压再补校验否则中间被替换的文件很难发现。cd offline-ssh-upgrade sha256sum -c SHA256SUMS rpm --checksig rpms/*.rpm 21 | tee logs/rpm-checksig.log || truesha256sum -c读取SHA256SUMS里的路径和摘要逐项比对rpm --checksig输出每个包的签名状态tee把结果同时写到日志末尾|| true是为了在签名密钥缺失时不打断后续人工判断但不代表可以忽略签名失败。参数上SHA256SUMS必须与包目录同级路径要写成相对路径否则换一台机器执行时容易找不到文件。如果目标系统支持createrepo_c可以把rpms/做成一个本地仓库再用dnf --disablerepo* --enablerepolocal-ssh安装。这样做的好处是依赖解析交给包管理器安装顺序不需要人工排。注意--disablerepo*是禁用所有远程源防止脚本在离线环境里误连--enablerepolocal-ssh要求本地 repo 文件已经配好。没有本地仓库条件时直接rpm -Uvh也可以但必须先用--test验证闭包。3. 一键脚本拆开看环境检查、备份、包校验与安装顺序“一键”不等于“无脑”。我拆这个资源包里的脚本时只看四段环境检查、备份、包校验、安装与配置迁移。四段都能单独执行也能串起来执行任何一段失败后面的段不启动。这样做的原因是离线升级现场往往没有远程救援脚本必须能在失败时留下可读日志和可回滚快照。下面按段拆开每段都给一段可抄的脚本骨架参数按实际目录替换。3.1 环境检查脚本把“能不能升”变成可判定条件环境检查不是打印一堆信息就完事而是给出明确的通过/不通过。要检查命令是否存在、包目录是否可读、备份目录是否可写、根分区空间是否足够、当前是否已有升级锁。下面这段脚本只做检查不写系统#!/usr/bin/env bash set -euo pipefail : ${PKG_DIR:?请设置 PKG_DIR 指向离线包目录} : ${BACKUP_ROOT:/var/backups/ssh-upgrade} need_cmdsrpm sshd systemctl sha256sum tar for c in $need_cmds; do command -v $c /dev/null 21 || { echo 缺少命令: $c; exit 1; } done [ -d $PKG_DIR ] || { echo 包目录不存在: $PKG_DIR; exit 1; } [ -w $BACKUP_ROOT ] || { echo 备份目录不可写: $BACKUP_ROOT; exit 1; } free_kb$(df -Pk /var | awk NR2{print $4}) [ $free_kb -gt 204800 ] || { echo 空间不足至少留 200MB; exit 1; } echo 环境检查通过: ${PKG_DIR:?...}是参数必填检查没设置就直接报错退出BACKUP_ROOT给了默认值现场可以覆盖need_cmds里的命令缺一个都不行尤其sshd和systemctl缺了后面无法校验服务df -Pk /var取/var所在分区可用块数204800是给备份和日志留的余量实际可按机器调整。注意这里检查的是/var因为备份目录默认放在/var/backups下如果改到其他分区df的路径也要跟着改。检查通过后再看包目录里的文件是否齐全。常见做法是把包清单做成manifest.txt脚本逐行比对文件是否存在、摘要是否一致。不要在检查阶段执行rpm -Uvh也不要在检查阶段重启服务。检查脚本的退出码要能被上层一键脚本捕获失败就停不要用|| true把错误吞掉。3.2 备份与快照配置、二进制、包清单、服务单元离线升级最怕的是升级后 sshd 起不来手上却没有旧版本。备份至少要覆盖四类东西/etc/ssh配置目录、/usr/sbin/sshd和/usr/bin/ssh二进制、rpm -qa包清单、systemd 服务单元。只备份 rpm 包不够因为配置文件可能被手工改过只备份配置也不够因为回滚时需要旧二进制和旧包。下面这段备份脚本按时间戳落目录#!/usr/bin/env bash set -euo pipefail TS$(date %Y%m%d%H%M%S) BK/var/backups/ssh-upgrade/$TS mkdir -p $BK/{etc,bin,rpmdb,unit} cp -a /etc/ssh $BK/etc/ssh cp -a /usr/sbin/sshd $BK/bin/sshd cp -a /usr/bin/ssh $BK/bin/ssh rpm -qa | sort $BK/rpmdb/rpm-qa.before systemctl cat sshd $BK/unit/sshd.service 2/dev/null || systemctl cat ssh $BK/unit/ssh.service tar -C / -czf $BK/etc-ssh.tar.gz etc/ssh sha256sum $BK/bin/* $BK/bin/SHA256SUMS echo $BKTS精确到秒避免多次执行互相覆盖mkdir -p $BK/{etc,bin,rpmdb,unit}一次建好四类目录cp -a保留权限、属主和时间戳/etc/ssh下的 host key 权限很敏感不能用普通复制rpm -qa | sort保存升级前包清单回滚时用来比对systemctl cat保存服务单元服务名可能是sshd也可能是ssh所以用||兜底tar -C /把配置目录再打一层包便于整体恢复最后对二进制做摘要回滚后可以核验文件是否被替换。参数上BK可以改成外挂存储路径但不要放在待升级的同一块小分区上。备份完成后不要只打印路径最好把路径写进升级日志并在回滚脚本里强制要求传入这个路径。回滚不是后悔药前提是备份在升级前已经完成如果备份目录不可写前面的环境检查就应该拦住。3.3 安装顺序与配置迁移先 sshd -t 再 reload安装顺序不要人工拍脑袋。包集合完整时优先让本地仓库的依赖解析决定顺序没有本地仓库时用rpm -Uvh --test先跑一遍闭包测试再执行正式安装。下面这段按包管理器分支处理if command -v dnf /dev/null 21; then dnf --disablerepo* --enablerepolocal-ssh -y install ./rpms/*.rpm elif command -v yum /dev/null 21; then yum --disablerepo* --enablerepolocal-ssh -y localinstall ./rpms/*.rpm else rpm -Uvh ./rpms/*.rpm ficommand -v dnf和command -v yum用来选包管理器--disablerepo*禁用远程源避免离线机器误连--enablerepolocal-ssh指向本地仓库 id这个 id 要和 repo 文件里的[local-ssh]对应-y让自动化流程不交互localinstall是 yum 处理本地 rpm 的常见写法。直接rpm -Uvh只适合包集合已经闭环的场景执行前先用rpm -Uvh --test ./rpms/*.rpm验证不要跳过测试直接上。安装完成后不要立刻重启 sshd。先做配置语法检查再看有效配置最后 reload。下面三条命令的顺序不要乱sshd -t -f /etc/ssh/sshd_config sshd -T -f /etc/ssh/sshd_config /tmp/sshd-effective.conf systemctl reload sshd || systemctl reload sshsshd -t只检查配置语法和关键文件权限失败会直接报错sshd -T输出解析后的有效配置用来确认PermitRootLogin、PasswordAuthentication、UsePAM、AuthorizedKeysFile等项没有被旧配置或 include 文件覆盖systemctl reload让 sshd 重新读取配置不断开已有连接比直接 restart 安全。参数上-f指向实际配置文件如果系统用了/etc/ssh/sshd_config.d/片段sshd -T会把合并后的结果打出来重点看最终值不要只看主文件。配置迁移的常见坑是旧sshd_config里写了新版本不认的选项或者新包默认配置覆盖了旧的认证策略。我的做法是升级前把/etc/ssh/sshd_config和sshd_config.d/一起备份升级后用diff -u对比只合并业务需要的差异不整体覆盖。reload 之后先保留当前会话另开一个新会话验证登录新会话不通就立即回滚不要关掉旧会话。4. 升级执行与回滚把“一键”拆成可中断的四段一键脚本要能被中断也要能重复执行。我的四段是预演、正式安装、配置校验与服务重载、回滚。预演只打印不执行正式安装带锁和日志配置校验失败就停在原地回滚用升级前快照恢复。下面给三段关键骨架最后一段是回滚。每段都可以单独调用不要把所有逻辑塞进一个没有分支的脚本里。4.1 预演模式只打印不执行预演模式解决的是“命令写对了没有”这个问题。很多翻车不是包的问题而是脚本里路径写错、通配符匹配到旧包、服务名写错。下面这个run函数用DRY_RUN控制是否真正执行#!/usr/bin/env bash set -euo pipefail DRY_RUN${DRY_RUN:-1} run() { if [ $DRY_RUN 1 ]; then printf [dry-run] %q $; echo else $ fi } run rpm -Uvh --test ./rpms/*.rpm run systemctl reload sshdDRY本文还有配套的精品资源点击获取