离线环境下给 OpenSSH 做版本升级,是运维圈里一个绕不开的经典场景。很多生产环境出于安全策略,服务器压根不接外网,甚至连内网 yum 源都没有,所有软件包只能靠 U 盘或者跳板机拷进去。而 OpenSSH 作为系统里最核心的远程登录组件,一旦被扫描出低版本漏洞,安全部门就会追着你要整改。标题里提到的 Linux、Centos、Anolis 离线升级 OpenSSH 9.6p1,说的就是这么一件事:在不能联网的 CentOS 或 Anolis 系统上,把老版本 OpenSSH 换成 9.6p1。这件事听起来简单,实际动手坑不少——升级过程中 SSH 服务会中断,一旦新版本编译有问题或者配置没迁移好,你就可能被锁在服务器外面。我前后在几十台机器上做过这个操作,从 CentOS 7 到 Anolis 8 都踩过,下面把整套思路、操作细节和避坑经验完整拆开讲,适合有一定 Linux 基础、需要在隔离环境里完成升级的运维和开发同学参考。
1. 升级前的整体设计与方案选型
1.1 为什么选择源码编译而不是替换二进制包
离线升级 OpenSSH 有两条路:一是找现成的 rpm 包直接装,二是下载源码包自己编译。很多人第一反应是找 rpm,觉得省事。但实际操作下来,rpm 方案在离线环境里反而更麻烦。原因在于 OpenSSH 的 rpm 包和系统的 OpenSSL 版本、pam 配置、selinux 策略强绑定,你从别的机器上拷一个 rpm 过来,依赖关系经常对不上,rpm -ivh报一堆依赖缺失,而离线环境又没法自动解决依赖。更麻烦的是,不同小版本的 CentOS 和 Anolis 之间包结构还有差异,跨系统拷包很容易出问题。
源码编译的好处是可控。你只需要保证编译机(或者目标机本身)上有 gcc、make、zlib-devel、openssl-devel 这几个基础组件,编译出来的二进制就是针对当前系统架构的,不依赖外部包管理器的版本约束。而且源码编译可以自定义安装路径、自定义编译参数,升级过程完全透明,出了问题也好回滚。所以我个人在离线场景下一律走源码编译,rpm 只在极少数有内部统一包源的场景下才考虑。
这里要强调一个关键点:编译依赖的 openssl 版本。OpenSSH 9.6p1 要求 OpenSSL 1.0.2 以上,CentOS 7 自带的是 1.0.2k,Anolis 8 自带的是 1.1.1 系列,都满足要求。但如果你系统里的 openssl-devel 版本太老,编译时会报找不到某些函数。所以升级前先确认openssl version和rpm -qa | grep openssl的输出,心里有数。
1.2 升级路径的核心风险点在哪
整个升级最大的风险不是编译失败,而是服务中断导致失联。OpenSSH 升级的本质是替换 sshd 二进制,替换过程中旧的 sshd 进程还在跑,但新连接会走新的二进制。如果你在替换后没做配置迁移、没做权限检查就重启 sshd,很可能新 sshd 起不来,而旧的又被你 kill 了,结果就是彻底连不上,只能走带外管理或者机房现场。
所以方案设计的核心原则是:保留旧版本、并行验证、平滑切换。具体做法是先把新版本编译安装到一个独立目录(比如/usr/local/openssh9.6),不动系统自带的/usr/sbin/sshd,然后用新二进制在另一个端口起一个测试实例,验证能正常登录、配置无误后,再替换系统默认的 sshd 并重启。这样任何一步出问题,旧的服务还在,你还有退路。
另外要提前准备好带外通道。不管是云主机的 VNC 控制台,还是物理机的 IPMI,升级前一定要确认能进去。我见过有人升级到一半 sshd 挂了,又没有控制台权限,最后只能等机房值班人员帮忙,耽误好几个小时。这个准备工作花不了几分钟,但关键时刻能救命。
1.3 版本与系统兼容性对照
不同系统的基础环境差异会影响编译参数和依赖安装方式,下面这张表是我实际做过验证的对照,供参考:
| 系统类型 | 自带 OpenSSH | 自带 OpenSSL | 编译依赖安装方式 | 备注 |
|---|---|---|---|---|
| CentOS 7 | 7.4p1 | 1.0.2k | yum(需本地源或离线包) | 老系统,pam 配置需重点检查 |
| CentOS 8 | 8.0p1 | 1.1.1c | dnf | 注意 systemd 服务单元差异 |
| Anolis 8 | 8.0p1 | 1.1.1 系列 | dnf | 与 CentOS 8 基本一致 |
| Anolis 7 | 7.4p1 | 1.0.2k | yum | 兼容 CentOS 7 操作 |
这张表的意义在于:你拿到一台机器,先对号入座,知道它的基础版本,就能预判编译时可能遇到的依赖问题。比如 CentOS 7 的 openssl 是 1.0.2k,编译 OpenSSH 9.6p1 时某些新特性可能不支持,但基础功能没问题;而 Anolis 8 的 1.1.1 系列兼容性更好。
2. 离线包准备与依赖梳理
2.1 需要准备哪些离线文件
离线升级的第一步是在一台能联网的机器上把东西备齐。很多人漏东西,到了目标机上才发现缺这个缺那个,来回拷很折腾。我整理了一份完整清单,按这个准备基本不会漏:
- OpenSSH 源码包:
openssh-9.6p1.tar.gz,从官方镜像下载。 - zlib 源码包:
zlib-1.3.tar.gz(可选,如果系统 zlib-devel 版本够用可以不编译)。 - 编译工具链离线包:gcc、make、glibc-devel、zlib-devel、openssl-devel、pam-devel。CentOS 7 用 yum 下载到本地目录,命令是
yum install --downloadonly --downloaddir=/tmp/deps gcc make zlib-devel openssl-devel pam-devel。 - pam 相关配置文件备份:
/etc/pam.d/sshd,升级后要迁移。 - 现有 sshd 配置备份:
/etc/ssh/sshd_config及ssh_host_*密钥文件。
这里有个细节:--downloadonly下载的包只包含直接依赖,如果目标机缺间接依赖,还是装不上。稳妥的做法是在一台和目标机系统版本完全一致的机器上执行下载,或者用yumdownloader --resolve把依赖树一起拉下来。我一般会多下几个常见的依赖包备用,反正 U 盘空间不值钱。
2.2 依赖包的离线安装顺序
把包拷到目标机后,安装顺序也有讲究。先装基础编译工具,再装库开发包,最后才是 OpenSSH 编译。如果顺序反了,可能出现库找不到的情况。具体命令:
# 进入离线包目录 cd /tmp/deps # 批量安装,--nodeps 慎用,除非确认依赖已满足 rpm -ivh *.rpm如果遇到依赖冲突,比如系统已装了新版本的某个库,rpm 会报错。这时候不要盲目加--force,先rpm -qa | grep 包名看看已装版本,确认兼容后再决定是否强制。我踩过一次坑:强制装了不兼容的 glibc 开发包,导致编译出来的 sshd 运行时报符号错误,最后只能重装系统库,得不偿失。
提示:离线安装 rpm 前,先执行
rpm -qa | grep -E "gcc|make|zlib|openssl|pam"摸清家底,避免重复安装或版本冲突。
2.3 源码包的校验与解压
源码包拷过来后,别急着解压,先做完整性校验。官方下载页会提供 SHA256 校验值,用sha256sum openssh-9.6p1.tar.gz对比一下。离线环境里文件经过多次拷贝,偶尔会出现损坏,校验能提前发现问题。校验通过后解压:
tar -zxvf openssh-9.6p1.tar.gz -C /usr/local/src/ cd /usr/local/src/openssh-9.6p1解压后先别编译,看一眼目录结构,确认configure脚本存在、contrib目录里有ssh-copy-id等辅助脚本。这些细节能帮你判断包是否完整。
3. 编译安装与配置迁移实操
3.1 configure 参数怎么选
编译 OpenSSH 的核心是configure的参数。参数选错了,要么编译出来的 sshd 缺功能,要么和系统 pam 不兼容。我常用的参数组合是这样的:
./configure \ --prefix=/usr/local/openssh9.6 \ --sysconfdir=/etc/ssh \ --with-pam \ --with-zlib \ --with-ssl-dir=/usr \ --with-privsep-path=/var/lib/sshd逐个解释一下为什么这么选。--prefix指定安装到独立目录,这是为了和系统自带版本隔离,方便回滚。--sysconfdir=/etc/ssh让新版本直接读系统现有的配置目录,这样配置迁移的工作量最小。--with-pam必须加,否则新 sshd 不支持 PAM 认证,很多依赖 PAM 的登录方式会失效。--with-ssl-dir=/usr指向系统 openssl 库路径,如果系统 openssl 装在别处要相应调整。--with-privsep-path指定特权分离目录,这个目录必须存在且权限正确,否则 sshd 启动会报错。
编译前建议先./configure --help看一眼当前版本的参数列表,不同小版本参数名可能有微调。配置完成后,configure会输出一个摘要,重点看 PAM、OpenSSL、zlib 三项是否都是 yes,如果有 no,说明对应依赖没找到,要回去检查。
3.2 编译与安装的现场记录
configure 通过后,执行make编译。编译时间取决于机器性能,一般几分钟到十几分钟。编译过程中如果报错,最常见的是头文件找不到,比如fatal error: openssl/opensslv.h: No such file or directory,这说明 openssl-devel 没装好或者路径不对。解决办法是确认rpm -ql openssl-devel | grep opensslv.h能找到头文件,然后检查--with-ssl-dir是否指向正确。
编译成功后执行make install,新版本会装到/usr/local/openssh9.6。装完后先别动系统 sshd,用新二进制做个版本确认:
/usr/local/openssh9.6/sbin/sshd -V正常应该输出OpenSSH_9.6p1。如果报库找不到,用ldd /usr/local/openssh9.6/sbin/sshd看依赖,缺哪个库补哪个。
3.3 配置迁移与权限处理
新版本装好后,配置不能直接照搬。OpenSSH 9.6p1 对某些配置项有变化,比如默认禁用了部分老旧的加密算法。稳妥的做法是以系统现有的/etc/ssh/sshd_config为基础,对照新版本的sshd_config示例(源码目录里有)做增量调整。重点检查这几项:
PermitRootLogin:确认是否允许 root 登录,按实际策略设置。PasswordAuthentication:密码登录是否开启。PubkeyAuthentication:密钥登录是否开启。Subsystem sftp:sftp 子系统路径要指向新版本的sftp-server。
权限方面,/var/lib/sshd目录必须存在,属主 root,权限 700。/etc/ssh下的密钥文件权限也要检查,私钥必须是 600。这些权限不对,sshd 启动时会直接拒绝。
注意:配置迁移时不要覆盖
/etc/ssh/ssh_host_*密钥文件,这些是主机密钥,覆盖后客户端会报 host key 变更警告,影响正常连接。
3.4 并行测试实例的启动方法
这是整个流程里最关键的一步。在替换系统 sshd 之前,先用新二进制在另一个端口起一个测试实例:
/usr/local/openssh9.6/sbin/sshd -p 2222 -f /etc/ssh/sshd_config -D-p 2222指定测试端口,-D前台运行方便看日志。启动后另开一个终端,用ssh -p 2222 用户名@主机IP测试登录。能正常登录、能执行命令、sftp 能用,说明新版本工作正常。测试通过后 Ctrl+C 停掉测试实例,再进入正式替换。
这个并行测试的思路,本质上是给自己留了一条验证通道。我见过有人跳过这步直接替换,结果新 sshd 因为配置问题起不来,旧进程又被 kill 了,只能走控制台救场。
4. 平滑切换与回滚机制
4.1 替换系统 sshd 的正确姿势
测试实例验证通过后,开始正式替换。替换的核心是备份旧二进制、拷贝新二进制、重启服务。步骤要一气呵成,但每一步都要确认:
# 备份旧版本 cp -a /usr/sbin/sshd /usr/sbin/sshd.bak.$(date +%F) cp -a /usr/bin/ssh /usr/bin/ssh.bak.$(date +%F) # 拷贝新版本 cp /usr/local/openssh9.6/sbin/sshd /usr/sbin/sshd cp /usr/local/openssh9.6/bin/ssh /usr/bin/ssh # 检查权限 chmod 755 /usr/sbin/sshd /usr/bin/ssh拷贝完成后,先别重启,用sshd -t做配置语法检查。这个命令会校验配置文件,有错会直接报出来。语法检查通过后,再重启服务:
systemctl restart sshd重启后立刻用新开终端测试登录,确认没问题再关掉旧终端。这里有个经验:重启后不要马上断开当前会话,保留一个已登录的会话作为退路,万一新连接有问题,还能用这个会话回滚。
4.2 回滚方案怎么落地
回滚方案必须在升级前就准备好,而不是出了问题才想。最简单的回滚就是把备份的旧二进制拷回去,重启服务:
cp -a /usr/sbin/sshd.bak.20240101 /usr/sbin/sshd systemctl restart sshd但要注意,如果新版本已经改过配置文件,回滚时配置也要一并回滚,否则旧版本可能读不懂新配置。所以升级前要把/etc/ssh/sshd_config也备份一份。我一般会在升级前把整个/etc/ssh目录打包备份,回滚时整体还原,最省事。
4.3 服务单元与开机自启的确认
CentOS 7 和 Anolis 8 的 sshd 服务单元文件位置略有不同,但一般都在/usr/lib/systemd/system/sshd.service。替换二进制后,服务单元不需要改,因为它指向的是/usr/sbin/sshd,你替换的就是这个路径。但如果你把新版本装到了别的路径,就要改服务单元里的ExecStart。改完记得systemctl daemon-reload。
开机自启确认用systemctl is-enabled sshd,输出 enabled 就说明没问题。升级后建议重启一次机器做完整验证,确认开机后 sshd 能正常起来。这个验证在测试环境做就行,生产环境重启要谨慎评估。
5. 常见问题排查与避坑经验
5.1 升级后无法登录的排查顺序
升级后连不上是最让人紧张的情况。排查要有顺序,从网络层往上查:
- 端口是否监听:
ss -tlnp | grep 22,看 sshd 有没有起来。 - 进程是否存活:
ps -ef | grep sshd,看主进程和子进程。 - 配置是否报错:
sshd -t再跑一遍,看有没有语法问题。 - 日志有没有线索:
journalctl -u sshd -n 50或看/var/log/secure。 - 权限是否正确:重点看
/var/lib/sshd和密钥文件权限。
大部分无法登录的问题集中在配置语法错误和权限错误两类。日志里通常会有明确提示,比如Bad configuration option或Permissions 0644 for '/etc/ssh/ssh_host_rsa_key' are too open,照着改就行。
5.2 常见问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
sshd 启动报Missing privilege separation directory | /var/lib/sshd不存在 | mkdir -p /var/lib/sshd && chmod 700 /var/lib/sshd |
登录报Permission denied | PAM 配置未迁移 | 检查/etc/pam.d/sshd是否存在且内容正确 |
| sftp 连不上 | 子系统路径指向旧版本 | 改Subsystem sftp指向新版本 sftp-server |
| 编译报 openssl 头文件缺失 | openssl-devel 未装或路径错 | 装 openssl-devel,检查--with-ssl-dir |
| 新 sshd 运行报符号错误 | 编译时链接了不兼容的库 | 检查ldd,重装匹配版本的开发包 |
| 客户端报 host key 变更 | 主机密钥被覆盖 | 恢复原ssh_host_*文件,或客户端清理 known_hosts |
这张表是我这些年遇到问题后整理的,基本覆盖了八成以上的场景。遇到表里没有的,先看日志,日志里的错误信息通常很直白。
5.3 几个容易忽略的细节
第一个细节是selinux。如果系统开了 selinux,替换 sshd 二进制后可能因为安全上下文不对导致启动失败。用ls -Z /usr/sbin/sshd看上下文,和备份文件对比,不一致就用restorecon -v /usr/sbin/sshd恢复。这个坑我在 Anolis 上踩过,当时排查了半天才发现是 selinux 拦的。
第二个细节是防火墙。升级过程如果改了端口或者重启了网络服务,防火墙规则可能受影响。升级前确认firewall-cmd --list-all里 22 端口是放行的,升级后如果连不上,先看防火墙。
第三个细节是客户端兼容性。OpenSSH 9.6p1 默认禁用了一些老算法,如果客户端是很老的版本(比如某些嵌入式设备),可能协商失败。这种情况要么在服务端配置里显式开启兼容算法,要么升级客户端。生产环境里如果有老设备,升级前一定要评估。
提示:升级完成后,用
ssh -V确认客户端版本,用sshd -V确认服务端版本,两边都对了才算真正完成。
6. 升级后的验证与收尾
6.1 功能验证清单
升级不是重启完就结束了,要做一轮完整验证。我通常按这个清单过一遍:新连接能登录、密钥登录能用、密码登录能用(如果开启)、sftp 能传文件、scp 能拷贝、端口转发能用、日志正常记录。每一项都实际测一下,别只看进程在就以为万事大吉。
验证时建议用不同客户端测,比如命令行 ssh、图形化工具、脚本调用,确保各种场景都覆盖。有些问题只在特定客户端上出现,多测几种能提前发现。
6.2 旧版本清理与空间释放
验证稳定运行几天后,可以清理旧版本残留。但别急着删备份,至少保留一个旧二进制和一份配置备份,放在一个固定目录里,标注好版本和日期。我一般保留最近两个版本的备份,太老的再清理。清理时注意别误删/etc/ssh下的密钥文件,那些是长期使用的。
6.3 文档记录与后续维护
最后一步是记录。把升级的系统版本、OpenSSH 版本、编译参数、遇到的问题和解决办法写进运维文档。这个习惯看起来麻烦,但下次再升级或者别人接手时,能省大量时间。我自己的文档里会记清楚每台机器的升级日期和回滚点,出问题时能快速定位。
离线升级 OpenSSH 这件事,技术难度不算高,但细节多、风险集中。把备份做足、把测试通道留好、把回滚方案备好,这三件事做到位,基本不会出大问题。我在实际操心里最大的体会是:永远假设新版本会起不来,按这个假设去准备退路,反而每次都很顺利。