简介:面向 Rocky Linux 9.5(Blue Onyx)服务器的 OpenSSH 升级与安全加固方案,适合运维工程师、系统管理员在旧版 sshd 整改、等保合规加固等场景中直接使用。整套材料围绕 OpenSSH 10.0p1 与 OpenSSL 3.5.0 的替换链路组织,附带升级前后的版本核对、服务状态确认等实操依据,可帮助读者规避升级中常见的依赖断裂、服务无法启动等问题。压缩包共 6 个文件、约 10.96MB,含 5 个 rpm 与 1 个 sh 脚本:rpm 覆盖 openssh 服务端、客户端及 chkconfig、initscripts 等配套依赖,脚本负责串联升级与加固动作,降低手工逐条执行的出错概率。目前已有 210 人学习下载。读者可据此掌握版本升级的完整流程、依赖处理顺序与加固项配置思路,并对照自身环境完成 sshd 的平滑替换与验证。
1. 重启 sshd 之后那三分钟:我为什么坚持把升级做成 RPM
把 Rocky Linux 9.5 上自带的 openssh-8.7p1 换成自己编的 openssh-10.0p1、再链上 OpenSSL 3.5.0,打成 x86_64 的 rpm,最后用脚本做 OpenSSH 加固——这套流程我做了三遍才敢上带业务的机器。第一遍是最土的./configure && make install,跑通了,也埋了雷:三个月后想回退,系统里已经找不回原来那份二进制,只能翻 shell history 猜当时改过什么。第二遍我开始写 spec,把 OpenSSL 3.5.0 关进 /opt,把升级和回滚都塞进脚本。第三遍补上加固基线和验证闭环,才敢一次推给二十几台机器。
这篇写的就是这条路上的判断依据和能直接抄的片段:三条升级路线怎么选、3.5.0 为什么不能覆盖系统那份 libssl.so.3、spec 里哪几行是保命的、加固清单怎么定、以及五个我亲身踩过、能让你少熬一宿的坑。写给准备做版本跨越的运维和安全同学,也写给已经被 sshd 重启坑过一次、想要一套可复现流程的人。
2. 三条路摆在一起比:源码编译、等仓库、自建 RPM 该选谁
2.1 先明确一个前提:版本号低不等于不安全
很多人做 OpenSSH 加固的起点是一张漏洞扫描报告,上面写着 openssh 版本过低。这里有个常被忽略的事实:发行版维护方会把上游的安全补丁回移到旧版本号上,8.7p1 后面跟着一长串.el9_5之类的 release 号,其中相当一部分就是回移的修复。所以判断"要不要升"的依据不是版本号大小,而是三件事:扫描器能不能认、功能上有没有必须要新版本的特性、以及你们的安全基线是不是硬性要求上游版本号。
把这三件事想清楚,路线选择才有意义。如果只是为了让扫描器闭嘴,回移补丁的 8.7p1 其实够用;如果确实需要 10.0p1 这条线上的东西,比如默认启用抗量子的密钥交换、彻底移除 DSA 支持、以及新的算法默认值,那就得自己动手。我当时属于后者:加固基线里明确要求禁用 SHA-1 系签名、DSA 主机密钥不进配置文件,同时希望密钥交换首选走混合后量子算法。
2.2 三条路线的逐项对比
我把三条路线按实际会遇到的维度摊开比了一遍,结论在表后面。
| 维度 | 源码 make install | 等发行版仓库 | 自建 RPM + 升级脚本 |
|---|---|---|---|
| 版本可控性 | 想装哪个装哪个 | 被仓库版本锁死 | 想装哪个装哪个 |
| 回滚成本 | 基本靠重装或手工还原 | dnf history undo一条命令 | rpm -Uvh --oldpackage一条命令 |
| 批量下发 | 每台手敲或写一堆 ssh | 仓库统一推送 | 一个 rpm 推过去 |
| 配置文件归属 | 无记录,容易被覆盖 | 由 rpm 管理 | 自己用%config(noreplace)定规则 |
| 审计留痕 | 只有 shell history | rpm -q可查 | rpm -q+ 包签名可查 |
| SELinux 处理 | 经常要手工 restorecon | 策略自带 | 需要写进%post |
| 首次投入 | 半小时 | 零 | 两三小时 |
| 出问题概率 | 高 | 低 | 中 |
| 出事后代价 | 高 | 低 | 低 |
表格里最扎眼的是最后两行。源码编译不是"装不上"的风险高,而是"装上了以后出问题没法查"的风险高:/usr/local/etc/sshd_config和/etc/ssh/sshd_config同时存在、/usr/local/sbin/sshd和/usr/sbin/sshd同时存在,systemd 起的是哪个、PAM 读的是哪个、SELinux 认的是哪个,全靠记忆。这种状态在单机上无所谓,在二十台机器上就是血泪经验。
等仓库这条路最稳,代价是版本天花板。而且 Rocky Linux 9.5 自带的 OpenSSL 是 3.2 分支,想上 3.5.0 这条 LTS 线,光换 OpenSSH 是不够的,最终还是得自己编。所以真正要选的是"编完怎么落地":直接覆盖安装,还是打成包。
2.3 自建 RPM 到底买到了什么
多花两三个小时,买到的是四样东西。
第一是可回滚。rpm -Uvh --oldpackage能把整包退回上一个版本,配置文件的 rpmnew/rpmsave 机制还会把改过的配置单独留一份。这一点在 sshd 这种"改错就进不去"的服务上价值极高。
第二是可审计。rpm -q openssh --qf '%{VERSION}-%{RELEASE}.%{ARCH}\n'能直接回答"这台机器现在跑的是哪个版本、什么时候装的、包有没有被篡改"。源码编译给不了这个答案。
第三是可批量。包推到内网源,剩下的交给配置管理工具,每台机器执行的动作完全一致,不存在"这台当时多打了个参数"的玄学差异。
第四是能跟 SELinux 和 PAM 对上。自建包在%post里跑一次restorecon、把/etc/pam.d/sshd一起打进包里,就避免了"编译装完 SELinux 拒绝、PAM 找不到配置"这类只在源码安装时出现的毛病。
注意:自建包不等于自建 OpenSSL 系统包。把系统的 openssl-libs 换掉是另一回事,
dnf、python3、curl一大串东西都挂在它上面,那条路我不建议碰。
3. 构建环境与 OpenSSL 3.5.0:让它待在 /opt 别碰系统的 libssl.so.3
3.1 构建机该装什么、刻意不装什么
构建机用一台和线上同版本的 Rocky Linux 9.5 最小化安装就够,先补构建工具链和几个开发包。
# 基础工具链与打包工具 dnf groupinstall -y "Development Tools" dnf install -y rpm-build rpmdevtools systemd-rpm-macros perl-interpreter wget tar # 对应 OpenSSH 的几个可选特性,缺一个就少一个功能 dnf install -y pam-devel zlib-devel libselinux-devel krb5-devel audit-libs-devel libfido2-devel每个包都对应 configure 的一个开关,不是随手装的:pam-devel对应--with-pam,没有它编出来的 sshd 根本不走 PAM,密码和二次验证会一起失效;libselinux-devel对应--with-selinux,没有它 SELinux 下的上下文切换会出问题;audit-libs-devel对应--with-audit=linux,登录事件才会进 audit 日志;krb5-devel对应--with-kerberos5;libfido2-devel是给 FIDO/U2F 硬件密钥认证用的,如果你的环境用 sk-ssh-ed25519 这类密钥,这个必须有。
刻意不装的是openssl-devel。装了它以后,configure 有可能通过 pkg-config 找到系统那份 3.2 的头文件和库,你以为链上了 3.5.0,实际上ssh -V一看还是旧的。这种"以为改了其实没改"的翻车最难查,因为编译过程一句报错都没有。
3.2 OpenSSL 3.5.0 装到 /opt:为什么不能覆盖系统那份
OpenSSL 3.5 是 LTS 分支,soname 依然是libcrypto.so.3/libssl.so.3,也就是说直接覆盖系统的/usr/lib64/libssl.so.3不会立刻炸——它会以"某天 dnf 突然报错"、"某个 python 模块导入失败"的方式延迟爆炸,排查成本极高。
先看一眼系统上到底有多少东西依赖它:
# 看看谁在用系统的 openssl-libs,输出会很长 rpm -q --whatrequires openssl-libs | head -30所以正确姿势是隔离安装:
wget https://www.openssl.org/source/openssl-3.5.0.tar.gz tar xf openssl-3.5.0.tar.gz && cd openssl-3.5.0 # --prefix 决定安装位置,--openssldir 决定 openssl.cnf 的位置 # --libdir=lib64 保证 64 位库落在 lib64 下,跟 rpath 对得上 ./Configure --prefix=/opt/openssl-3.5.0 \ --openssldir=/opt/openssl-3.5.0/ssl \ --libdir=lib64 \ shared zlib -fPIC make -j"$(nproc)" # 只装库和头文件,不装 man 和文档,减少体积也减少冲突面 make install_sw几个参数值得单独说。shared是必须的,我们要的是动态库,静态链进去以后 OpenSSL 发安全更新就得重编 OpenSSH,等于把两个东西绑死了。zlib打开压缩支持。--openssldir指向 /opt 下面,是为了让这份 OpenSSL 有自己独立的配置文件,不去读系统的/etc/pki/tls/openssl.cnf。
关键的一条:不要把/opt/openssl-3.5.0/lib64写进/etc/ld.so.conf.d/再跑ldconfig。那样做等于让全系统所有程序优先加载 3.5,跟直接覆盖只差一个"看起来没那么暴力"。我们只在 OpenSSH 这套二进制上通过 rpath 指定路径,其他程序不受影响。
3.3 configure 参数:一条一条对着看
OpenSSH 这步的坑主要在路径上。整个 configure 我按下面这样写,LDFLAGS一定要在./configure之前 export 出去,不能当成 configure 的参数传。
cd openssh-10.0p1 # rpath 把 3.5.0 的库路径写死进二进制,relro/now 顺手加上加固 export LDFLAGS="-Wl,-rpath,/opt/openssl-3.5.0/lib64 -Wl,-z,relro -Wl,-z,now" ./configure \ --prefix=/usr \ --sysconfdir=/etc/ssh \ --libdir=/usr/lib64 \ --with-ssl-dir=/opt/openssl-3.5.0 \ --with-pam \ --with-selinux \ --with-audit=linux \ --with-kerberos5 \ --with-privsep-path=/var/empty/sshd \ --with-privsep-user=sshd \ --with-pid-dir=/run \ --with-xauth=/usr/bin/xauth \ --with-default-path=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin \ --disable-strip--prefix=/usr是为了让二进制落到/usr/sbin、配置落到/etc/ssh,跟发行版原来的布局一致,systemd 的 unit 文件不用改。--with-ssl-dir同时影响头文件和库的搜索路径。--with-privsep-path=/var/empty/sshd这个目录必须存在、属主 root、权限 0755,缺了它 sshd 启动直接失败,后面打包时要显式创建。--with-pid-dir=/run跟 Rocky 9 的 systemd unit 对得上。--disable-strip是给自己留调试符号的余地,出问题时gdb还能看出点东西。
还有一点值得提前说清楚:Rocky Linux 9.5 的sshd.service里写的是Type=notify和KillMode=process。KillMode=process意味着systemctl restart sshd只杀主进程,已经建立的连接各自有独立的子进程,不会被一起干掉——这是"重启 sshd 不会踢掉当前会话"的底层原因,也是敢在业务机上重启的底气。但这个保护只对已建立的连接有效,新连接能不能进来,取决于你改完的配置对不对。
3.4 用 ldd 和 objdump 验链接关系
编完先别急着打包,把链接关系验一遍,这一步能省掉后面 80% 的"为什么版本没变"。
# 看 sshd 实际加载的是哪份 libssl / libcrypto ldd /usr/sbin/sshd | grep -E 'libssl|libcrypto' # 看 rpath 有没有真的写进去 objdump -p /usr/sbin/sshd | grep -E 'RPATH|RUNPATH' # 最直接的一条:sshd -V 会把 OpenSSL 版本打出来 /usr/sbin/sshd -V判定标准很明确:ldd输出必须是libssl.so.3 => /opt/openssl-3.5.0/lib64/libssl.so.3,如果指向/lib64/libssl.so.3就说明链错了,八成是LDFLAGS没生效或者系统上装了 openssl-devel 导致路径被抢。objdump的 RUNPATH 里必须能看到/opt/openssl-3.5.0/lib64。最后sshd -V那行输出形如OpenSSH_10.0p1, OpenSSL 3.5.0,两个版本号都对上才算过关。
注意:
ssh -V和sshd -V都要看,客户端和服务端是两套二进制,有时候只编对了一半。
4. spec 与升级脚本:能一键回滚的 OpenSSH 加固才算升级
4.1 spec 骨架:%prep 到 %install 逐段拆
spec 不用从零写,rpmdev-newspec生成骨架再改就行。真正有判断的是下面这几段。
Name: openssh Version: 10.0p1 Release: 1%{?dist} Summary: OpenSSH 10.0p1 built against OpenSSL 3.5.0 License: BSD-ISC URL: https://www.openssh.com/ Source0: https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz # 顶替仓库里的三个子包,让 dnf 安装时自动替换而不是报冲突 Provides: openssh-server = %{version}-%{release} Provides: openssh-clients = %{version}-%{release} Obsoletes: openssh-server < %{version}-%{release} Obsoletes: openssh-clients < %{version}-%{release} BuildRequires: gcc make pam-devel zlib-devel libselinux-devel BuildRequires: krb5-devel audit-libs-devel libfido2-devel Requires: pam, zlib %description OpenSSH portable release built in-tree against a private OpenSSL 3.5.0. %prep %setup -q -n openssh-%{version} %build export LDFLAGS="-Wl,-rpath,/opt/openssl-3.5.0/lib64 -Wl,-z,relro -Wl,-z,now" %configure \ --prefix=/usr --sysconfdir=/etc/ssh --libdir=/usr/lib64 \ --with-ssl-dir=/opt/openssl-3.5.0 \ --with-pam --with-selinux --with-audit=linux --with-kerberos5 \ --with-privsep-path=/var/empty/sshd --with-privsep-user=sshd \ --with-pid-dir=/run --disable-strip make %{?_smp_mflags} %install rm -rf %{buildroot} make DESTDIR=%{buildroot} install # 这两个目录 make install 不会建,缺了 sshd 起不来 install -d -m 0755 %{buildroot}/var/empty/sshd install -d -m 0755 %{buildroot}/etc/ssh/sshd_config.dProvides/Obsoletes这两组是整个 spec 里最容易漏、后果又最直接的部分。发行版把 OpenSSH 拆成了 openssh、openssh-clients、openssh-server 三个包,你自建一个单包直接装,dnf 会因为文件冲突和依赖冲突拒绝;加上这三行,安装时它会自动把仓库版本替换掉,`dnf install ./openssh-10.0p1-1.el9.x
本文还有配套的精品资源,点击获取