
简介面向 Rocky Linux 9.5 运维人员与安全加固需求提供一套完整的 OpenSSH 升级加固方案。核心解决系统自带 SSH 版本老旧、OpenSSL 漏洞风险以及升级后服务管理方式切换等问题包内已集成 OpenSSH 10.0p1 与 OpenSSL 3.5.0 对应的 RPM 构建产物并附带一键执行脚本可在离线环境中完成安装、配置加固与 sshd 服务重启。压缩包共 6 个文件整体约 10.96MB包含 5 个 RPM 包和 1 个 shell 脚本。RPM 包覆盖 openssh、openssh-clients、openssh-server 等核心组件以及 chkconfig、initscripts 等依赖工具用于替换旧版二进制并恢复 SysV 式服务管理shell 脚本则负责自动化执行升级、备份配置、调整 sshd 参数并输出日志降低手工操作失误风险。已有 199 人学习下载。按脚本操作可快速将 sshd 切换为 SysV 管理方式同时保留远程会话不中断适合内网批量加固、等保合规整改或系统初始化时使用。1. OpenSSH 加固别只盯着 sshd_configRPM 升级脚本才是治本方案做 OpenSSH 加固的运维十有八九都干过同一件事拿到漏洞扫描报告看到 OpenSSH 版本偏低顺手改几行 sshd_config关掉 root 登录、换个端口然后交差。但真正上过等保或行业合规的都清楚配置项只是表面功夫老版本自带的 CVE 扫描器照样给你标红。我在 Rocky Linux 9.5 上做 OpenSSH 加固时没有逐台源码编译而是把 OpenSSH 升到 10.0p1、OpenSSL 升到 3.5.0全部打成 RPM 包批量分发。这篇把我整理的“rockylinux9.5-ssh10.0p1-ssl3.5.0-rpm-x86-64升级加固脚本”完整拆一遍从为什么选 RPM、编译参数怎么定到翻车之后怎么排错照着这篇能直接复现。2. 为什么选 RPM 而不是源码编译四个致命坑决定打包路线2.1 源码编译的四个坑我第一次做 OpenSSH 升级时也想过直接源码编译毕竟网上教程铺天盖地都是./configure make make install。真在 Rocky Linux 9.5 上实践过一轮才发现这条路对生产环境来说坑太深。第一个坑是库路径冲突。源码编译默认把文件装到/usr/local而系统自带的 OpenSSH 和 OpenSSL 都装在/usr。编译出来的 sshd 启动时去/usr/lib64找libcrypto.so.3找到的是系统旧版或者干脆找不到。我在测试机上编译完systemctl restart sshd直接报libcrypto.so.3: cannot open shared object file整个 ssh 服务起不来。第二个坑是 PAM 认证链路断裂。RHEL 系发行版的密码认证走的是/etc/pam.d/sshd源码包默认 configure 如果没显式加--with-pam编译出来的 sshd 根本不读 PAM 配置密码认证逻辑直接变了。最典型的表现是密码明明正确/var/log/secure里却报pam_authenticate() failedroot 都进不去。第三个坑是 systemd 管理缺失。make install不会帮你生成/usr/lib/systemd/system/sshd.service机器重启后 sshd 不会自动拉起。等保要求里服务开机自启是基本盘机房场景下重启就失联这单谁都不想买。第四个坑是回滚困难。源码安装的文件不记录在 rpmdb 里出了问题想回退只能手动去找老文件覆盖漏一个就埋雷。对比之下RPM 重打包方案的优势就很明显下面这张表是我当时做的选型对比。对比项源码编译方案RPM 重打包方案安装路径/usr/local与系统库隔离/usr、/etc跟随系统目录PAM 集成需手动确认 configure 参数spec 文件里固化配置systemd 注册自己写 unit 文件%post 脚本自动 enable回滚方式手动恢复容易遗漏rpm -Uvh --oldpackage 一步回退批量分发每台重复编译构建一次scp 或内网仓库分发2.2 脚本目录结构与执行入口这份升级加固脚本的目录结构我按 rpmbuild 的规范组织方便直接丢进/root/rpmbuild使用。我一般会把整个目录叫rocky9-ssh-upgrade里面分模块放如下所示。rocky9-ssh-upgrade/ ├── build.sh # 一键构建入口 ├── SPECS/ │ ├── openssl350.spec # OpenSSL 3.5.0 打包定义 │ └── openssh100p1.spec # OpenSSH 10.0p1 打包定义 ├── SOURCES/ │ ├── openssl-3.5.0.tar.gz │ ├── openssh-10.0p1.tar.gz │ ├── sshd_config.r9 # 加固版 sshd_config 模板 │ └── sshd.service # systemd unit 模板 ├── hosts.txt # 待升级机器列表 └── deploy/ └── deploy.sh # 批量分发安装脚本每个文件的作用并不复杂build.sh是总入口先后调用 rpmbuild 构建 OpenSSL 和 OpenSSH 两个包SPECS下是两个 spec 文件定义了版本、依赖和安装后操作SOURCES存放源码包和加固配置模板。需要说明的是rpmbuild 默认的工作目录就是/root/rpmbuild你把SPECS和SOURCES放对位置rpmbuild -ba才能找到它们。如果放在了其他路径记得用--define _topdir /你的路径指过去。2.3 升级前置检查动手之前先花两分钟做前置检查。我见过不少翻车现场都是因为系统版本、架构或者基础依赖没确认编译到一半才报错。cat /etc/redhat-release # 确认是 Rocky Linux 9.5 uname -m # 确认 x86_64 架构 ssh -V # 记录升级前 OpenSSH 版本 openssl version -a # 记录升级前 OpenSSL 版本 rpm -qa | grep -E ^(gcc|make|perl|pam-devel|zlib-devel|openssl-devel)最后一条命令是关键。pam-devel缺失会让 OpenSSH 的 configure 直接禁用 PAMgcc、make缺失连编译都起不来openssl-devel和zlib-devel是编译链接时的硬依赖缺了会在 configure 阶段报 header 文件找不到。如果检查出来缺包先dnf install -y gcc make perl pam-devel zlib-devel openssl-devel tcp_wrappers-devel再往后走别带着缺依赖硬上。3. 先打 OpenSSL 3.5.0 的 RPMConfigure 参数与 spec 脚本陷阱3.1 为什么必须先升 OpenSSL 再升 OpenSSHOpenSSH 10.0p1 的很多加密算法、KEX 协商和证书解析依赖 OpenSSL 提供的底层库。老版本的 OpenSSL 3.0.7 在算法套件和 FIPS 支持上都有缺口直接编译 OpenSSH 时链接符号版本可能不满足更隐蔽的是装完运行时的一些新特性会静默失效。所以打包和部署的顺序必须是先构建并安装 OpenSSL 3.5.0 的 RPM再构建并安装 OpenSSH 10.0p1 的 RPM。这个顺序我写死在deploy.sh里用串联顺序一旦反了RPM 的依赖检查会直接拦截报openssl(x86-64) 3.x不满足。还有一点Rocky Linux 9.5 自带的 OpenSSL 是 3.0.7升级到 3.5.0 属于大版本跨跃需要确认系统的ca-bundle路径仍然在约定位置也就是/etc/pki/tls这个我在 Configure 参数里做了对齐。3.2 OpenSSL Configure 参数逐项拆解构建 OpenSSL 3.5.0 时我的 Configure 命令长这样脚本里核心就是这一段。cd /root/rpmbuild/SOURCES/openssl-3.5.0 ./Configure linux-x86_64 \ --prefix/usr \ --openssldir/etc/pki/tls \ shared \ enable-fips \ -O2 -g make -j$(nproc) make install DESTDIR/root/rpmbuild/BUILDROOT/openssl-3.5.0-1.el9.x86_64参数逐个说清楚linux-x86_64是目标平台标识对应项目标题里的 x86-64。uname -m返回的是x86_64就用这个 target选错的话编译到汇编阶段就会报错。--prefix/usr是最不能省的一个参数它让 OpenSSL 装完直接落在/usr/bin/openssl和/usr/lib64和系统路径保持一致。如果你用默认值/usr/local后续 sshd 启动时动态库路径对不上就是我在第 2 章说的第一个坑。--openssldir/etc/pki/tls把证书目录固定到 RHEL 系约定位置避免ca-bundle找不到。shared生成libcrypto.so.3和libssl.so.3动态库OpenSSH 编译时才能链接。enable-fips开启 FIPS 模块等保测评里很多检查项认这个装完可以用openssl version -a查看 FIPS 状态。-O2 -g是常规编译优化选项生产环境建议保留。3.3 spec 文件里最容易翻车的 %post 与 %preun手动跑 make install 还不够要把它变成可控的 RPM关键在 spec 文件。我贴一个精简但能用的版本。Summary: OpenSSL 3.5.0 for Rocky Linux 9.5 Name: openssl Version: 3.5.0 Release: 1%{?dist} License: Apache-2.0 Source0: openssl-3.5.0.tar.gz BuildRequires: gcc, make, perl, perl-core %description OpenSSL 3.5.0 with FIPS module for Rocky Linux 9.5 x86-64. %prep %setup -q %build ./Configure linux-x86_64 --prefix/usr --openssldir/etc/pki/tls shared enable-fips -O2 -g make -j%{_smp_mflags} %install make install DESTDIR%{buildroot} %post /sbin/ldconfig %preun if [ $1 -eq 0 ]; then /sbin/ldconfig fi %files /usr/bin/openssl /usr/lib64/libcrypto.so.3 /usr/lib64/libssl.so.3 /etc/pki/tls/openssl.cnf重点看%post和%preun两段。%post里执行/sbin/ldconfig是必须的动态库装完不刷新缓存sshd 起来照样报libcrypto.so.3 not found这个问题最玄学因为文件明明就在磁盘上。%preun里的if [ $1 -eq 0 ]表示只有真正卸载时才跑 ldconfig升级场景下$1为 1跳过这段避免把新库的缓存弄丢。很多人在 spec 里漏掉这个条件判断升级时旧包卸载触发 ldconfig把新库缓存清掉直接导致服务起不来。3.4 构建产物与 ssl 检查spec 文件写完执行构建并验证产物。rpmbuild -ba /root/rpmbuild/SPECS/openssl350.spec ls -lh /root/rpmbuild/RPMS/x86_64/openssl-3.5.0-1.el9.x86_64.rpm rpm -qip /root/rpmbuild/RPMS/x86_64/openssl-3.5.0-1.el9.x86_64.rpmrpm -qip用来检查包的元信息重点看版本号和打包日期。装到测试机后跑openssl version -a输出里必须出现OpenSSL 3.5.0同时检查openssl s_client -connect 目标地址:443 -brief能不能正常完成证书链握手。这一步相当于做一次快速 ssl 检查能暴露动态库和证书目录配置是否正常。我习惯在测试机验证通过后再批量分发省得后面所有机器都跟着翻车。4. 打包 OpenSSH 10.0p1 RPMconfigure 参数与加固配置注入4.1 OpenSSH configure 参数清单OpenSSL 的 RPM 落地后开始处理 OpenSSH 10.0p1。这一步的 configure 参数比 OpenSSL 更敏感少一个参数后面就是一个坑。核心命令如下。cd /root/rpmbuild/SOURCES/openssh-10.0p1 ./configure \ --prefix/usr \ --sysconfdir/etc/ssh \ --with-pam \ --with-ssl-dir/usr \ --with-md5-passwords \ --with-privsep-path/var/empty/sshd \ --with-tcp-wrappers \ --with-selinux \ --with-kerberos5/usr/kerberos make -j$(nproc) make install DESTDIR/root/rpmbuild/BUILDROOT/openssh-10.0p1-1.el9.x86_64--with-pam是第一重要的参数没了它密码认证直接不走 PAM第 5 章的翻车现场 5.2 就是它的锅。--with-ssl-dir/usr指向我们刚升级的 OpenSSL 3.5.0让 sshd 链接到新的 libcrypto这个路径要和 OpenSSL 的--prefix对齐。--with-privsep-path/var/empty/sshd是权限分离目录编译前必须mkdir -p /var/empty/sshd属主 root权限 755否则 configure 直接报错。--with-tcp-wrappers依赖tcp_wrappers-devel包Rocky 9.5 最小化安装默认没有先用 dnf 装好。--with-selinux让新 sshd 能兼容 SELinux 的上下文策略RedHat 系默认 enforcing 模式下少了它新二进制可能被拦截。--with-kerberos5是给有跳板机 Kerberos 认证的环境准备的不需要的企业可以去掉但加上不影响。--with-md5-passwords保证老用户密码哈希兼容这个参数经常被忽略一旦漏了部分老账号会登录失败。4.2 sshd_config 加固项注入打包的第二个核心点是把加固后的 sshd_config 作为模板塞进 RPM而不是安装完再去手动改。脚本会在 build 阶段把SOURCES/sshd_config.r9覆盖到目标机器的/etc/ssh/sshd_config。我常用的加固项如下。# SOURCES/sshd_config.r9 里的关键加固项 Port 22 Protocol 2 PermitRootLogin no PubkeyAuthentication yes PasswordAuthentication yes PermitEmptyPasswords no MaxAuthTries 4 LoginGraceTime 30 ClientAliveInterval 300 ClientAliveCountMax 0 X11Forwarding no AllowTcpForwarding no逐项说一下考虑。PermitRootLogin no禁止 root 远程登录这是等保 Linux 检查项里最常见的一条如果你还想用 root 跑批量操作建议先切普通用户再加 sudo。MaxAuthTries 4和LoginGraceTime 30配合起来限制暴力破解登录尝试超过 4 次或 30 秒内没完成认证就断开。ClientAliveInterval 300加ClientAliveCountMax 0的效果是空闲超过 5 分钟服务端主动断开比单纯配置超时更可靠适合内网跳板机场景。X11Forwarding no和AllowTcpForwarding no是金融行业常见的加固要求禁用 X11 和端口转发减少横向暴露面。注意PasswordAuthentication yes我保留开启因为很多业务场景还在用密码登录如果你们全部走证书可以改成 no进一步压缩暴力破解面。4.3 批量部署ssh 密钥与执行顺序资源里配套的deploy.sh解决的是批量问题核心逻辑就是先传包、再安装、再重启。我贴一个去掉了日志审计的简化版本。#!/bin/bash # deploy/deploy.sh 核心循环 for host in $(cat ../hosts.txt); do scp -i ~/.ssh/deploy_key \ ../RPMS/x86_64/openssl-3.5.0-1.el9.x86_64.rpm \ ../RPMS/x86_64/openssh-10.0p1-1.el9.x86_64.rpm \ root$host:/root/ ssh -i ~/.ssh/deploy_key root$host \ rpm -Uvh /root/openssl-3.5.0-1.el9.x86_64.rpm \ rpm -Uvh /root/openssh-10.0p1-1.el9.x86_64.rpm \ systemctl restart sshd done这段脚本有两个关键点。第一批量登录必须提前把deploy_key.pub追加到目标机的authorized_keys里顺序反了你会发现自己被锁在门外。ssh 密钥的权限也要注意.ssh目录 700、authorized_keys600权限过宽会导致服务端拒绝使用。第二串联保证 OpenSSL 先装、OpenSSH 后装、最后重启只有全部成功才执行下一步。如果你管理的主机在 20 台以上我一般会改成先把包放到内网 yum 仓库然后dnf update openssh openssl -y比 scp 循环省事得多这就是另一种 ssh 批量登录的玩法了。另外提醒一句升级后因为主机密钥变化git 或其他走 ssh 协议的工具可能报Host key verification failed不是认证失败是老 known_hosts 里的指纹对不上了删掉对应行重新连接即可。5. 升级加固避坑指南五个翻车现场与处理方案5.1 重启 sshd 后起不来journalctl 报 libcrypto.so.3 not found现象执行systemctl restart sshd失败journalctl -u sshd里看到libcrypto.so.3: cannot open shared object file。用ldd /usr/sbin/sshd检查显示库文件 not found。原因OpenSSL 编译时--prefix没有对齐到/usr动态库装到了/usr/local/lib64或者装完后/sbin/ldconfig没有执行缓存里没记录新路径。解决先看文件实际位置再做软链并刷新缓存。ls -l /usr/lib64/libcrypto.so.3 /sbin/ldconfig ldd /usr/sbin/sshd | grep not found如果发现库文件在/usr/local/lib64建软链指过去再执行 ldconfig。这一步做完ldd输出里所有依赖都必须是found再重启服务。5.2 密码登录被拒/var/log/secure 报 pam_authenticate() failed现象升级后密码登录一律失败日志里反复出现pam_authenticate() failed、pam_unix(sshd:auth): authentication failure密码明明是对的。原因OpenSSH configure 时漏了--with-pam编译出的 sshd 不读/etc/pam.d/sshd。或者机器上/etc/pam.d/sshd文件缺失RHEL 9 默认是带这个文件的但如果做过精简加固被删掉密码认证链条一样断。解决重新编译时带上--with-pam确认/etc/pam.d/sshd存在。检查当前 sshd 运行时用的可执行文件路径如果是/usr/local/sbin/sshd而不是/usr/sbin/sshd说明旧二进制还在服务里占着位置停掉旧进程再重启。5.3 机器重启后 sshd 没起来机房机器直接失联现象升级一切正常但重启机器后 ssh 端口不通控制台进去看到 sshd 没在跑。原因源码编译安装没有注册 systemd unit或者 RPM 包的 spec 里没写%post的 enable 指令。服务没有开机自启这是 RHEL 系升级最常见的疏漏。解决手动执行以下命令并确认 spec 的%post段已经加上 enable。systemctl enable --now sshd systemctl list-unit-files | grep sshdlist-unit-files输出里sshd.service必须是enabled。顺便说一句有些发行版服务名是ssh但 Rocky Linux 9.5 上服务文件叫sshd.service别照搬习惯了sudo systemctl restart ssh在 CentOS 系直接报错。5.4 rpm -Uvh openssh 时报依赖缺失 openssl(x86-64) 不满足现象安装 OpenSSH RPM 时rpm 报错误依赖检测失败openssl(x86-64) 3.0 被 openssh-10.0p1-1.el9.x86_64 需要。原因RPM 依赖检查认的是系统 rpmdb 里记录的主版本而当前还没装 OpenSSL 3.5.0 的包rpmdb 里仍是 3.0.7。这和平时 rpm 安装 MySQL 报缺 libaio 是同一类问题本质都是依赖包没先进系统。解决严格按顺序执行先装 openssl 包再装 openssh 包不要跳步。如果已经用--nodeps强装了 OpenSSH那就先把 OpenSSL 3.5.0 装上然后重新执行rpm -Uvh openssh让它完成正确的依赖解析。--nodeps是最后一招副作用是 sshd 可能能启动但行为怪异能不用就不用。5.5 升级中断线失联现象第一台机器升级时 ssh 连接直接被 kill当前掉线后续机器更不敢动批量升级卡在半途。原因升级 sshd 时当前会话被终止又没有备用通道。很多企业环境的机器只有 ssh 一条路没有带外管理升级动作本身就是断自己后路。解决升级前开一个临时逃生通道机房有带外管理用带外没有的话用 telnet 顶一下注意是明文的只允许内网临时用升级完成后必须马上关。# 升级前开启 telnet 作为临时逃生 systemctl enable --now telnet.socket # 升级完成后立即关闭 systemctl disable --now telnet.socket另外升级后如果 ssh 客户端连不上先分清是服务端问题还是客户端 known_hosts 问题。服务端主机密钥变化会导致客户端报Host key verification failed把旧的 known_hosts 条目删掉再连。这类问题在 git 仓库使用 ssh 协议推送时特别容易遇到记住一个习惯升级完 sshd顺手清理测试客户端和生产客户端的 known_hosts 缓存能省掉一堆误报工单。6. 升级完成后的验证清单与回滚后悔药6.1 五步验证清单升级完不是结束验证过了才算落地。我每次都会按固定清单走一遍用表格列出来方便你对照执行。验证项命令预期结果版本确认ssh -VOpenSSH_10.0p1, OpenSSL 3.5.0配置检查sshd -t无报错输出服务状态systemctl status sshdactive (running)监听端口ss -tlnp | grep :22sshd 监听在 22 端口登录测试ssh 普通用户本机密码和密钥分别测一次注意sshd -t这步是必做的它能在不重启服务的情况下把 sshd_config 的语法错误暴露出来。登录测试不要只测 root普通用户和密钥方式都测一遍。用 mobaxterm 或 FinalShell 这类 ssh 远程工具连一下顺便确认终端会话、端口转发行为正常因为 OpenSSH 大版本升级对终端兼容性有影响。6.2 回滚方案回滚是给升级留的后路我管它叫后悔药。升级前先把旧版本 rpm 备份到/root/backup不要偷懒删掉。mkdir -p /root/backup rpm -qa | grep -E ^(openssh|openssl) /root/backup/rpm_list.txt万一新版本出现兼容性问题用--oldpackage参数降级回滚注意顺序和升级正好相反先回滚 OpenSSH再回滚 OpenSSL。rpm -Uvh --oldpackage /root/backup/openssh-8.7p1-*.el9.x86_64.rpm rpm -Uvh --oldpackage /root/backup/openssl-3.0.7-*.el9.x86_64.rpm systemctl restart sshd--oldpackage是 rpm 允许版本降级的开关没有它rpm 会拒绝安装比当前库中版本更老的包。回滚后同样跑一遍上面的验证清单确认服务正常再离开机器。从那以后我每次做 OpenSSH 加固升级都强制把备份旧包、确认逃生通道、验证清单这三件事走完任何一个条件不满足都不动手。这个习惯救过我两次希望帮到你。本文还有配套的精品资源点击获取