ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

OpenSSH 9.8p1升级实战:修复regreSSHion漏洞与OpenSSL依赖

2026/10/3 12:45:38 拓冰建站 浏览量
OpenSSH 9.8p1升级实战:修复regreSSHion漏洞与OpenSSL依赖 2024年7月1日OpenSSH官方紧急发布了9.8p1修复了一个代号为regreSSHion的远程代码执行漏洞对应CVE-2024-6387。当时我的第一反应不是又一个高危漏洞而是这次轮到了sshd。做过Linux运维的人都清楚sshd长期暴露在22端口上承担着所有远程登录入口的职责一旦它出了可被远程利用的漏洞整台机器的安全边界就形同虚设。这篇保姆级教程就把我从收到漏洞通告到完成OpenSSH 9.8p1升级、顺带处理OpenSSL老版本依赖问题的完整过程写出来。整个流程覆盖环境评估、OpenSSL 3.0编译安装、OpenSSH 9.8p1源码编译、服务切换验证和回滚方案适用于CentOS 7、Ubuntu 22.04这些主流Linux环境照着做就能把风险控制住。1. 为什么偏偏要赶在9.8p1regreSSHion不是普通漏洞很多人看到OpenSSH又发新版了第一反应是“日常更新”但9.8p1这次的发布节奏和版本跨度非常反常。官方直接把版本号从9.7跳到了9.8而且同步修复了一个可以直接导致root权限远程代码执行的严重安全问题。升级不再是“选做题”而是“必答题”。1.1 一个2006年修复过的老问题为何会复活CVE-2024-6387的本质是sshd中的一个信号处理竞争条件signal handler race condition。sshd在客户端连接建立后如果客户端一直不完成身份认证LoginGraceTime设置的超时计时器会触发SIGALRM信号。问题在于这个信号处理器内部调用的函数并不是异步信号安全的当它和主流程中的其他操作同时发生时就可能出现内存损坏进而被攻击者利用执行任意代码。最棘手的一点是这个漏洞不需要任何认证信息也不需要合法的账号密码。攻击者只要能够通过网络访问到sshd端口反复建立空闲连接来触发竞争窗口就有可能拿到root shell。整个利用过程通常在几小时内完成如果目标系统没有开启地址空间随机化ASLR利用难度还会进一步降低。这个问题的讽刺之处在于它其实是2006年CVE-2006-5051修复过的老问题。当时OpenSSH通过修改signal handler解决了不安全函数调用的问题但在后续代码重构中这个修复被部分回退了导致8.5p1到9.7p1之间的版本重新暴露在风险之下。官方称之为regreSSHion既是在描述回归也是在提醒运维人员别轻视历史包袱。1.2 升级范围的判定哪些版本中招、哪些不受影响判断自己是否需要升级先看两个维度系统架构和OpenSSH版本。受影响的是64位glibc Linux系统上OpenSSH版本在8.5p1到9.7p1之间的sshd。这也是当前绝大多数云服务器和物理机默认使用的组合。不受影响的情况包括OpenBSD系统、32位Linux、使用musl libc的Alpine Linux、以及低于8.5p1但已经应用过旧补丁的Red Hat系发行版。我在实际排查中发现很多CentOS 7默认自带的是OpenSSH 7.4p1单看版本号确实不在受影响区间内但问题在于这类老系统往往还带着老旧的OpenSSL 1.0.2k而OpenSSL 1.0.2和1.1.1两个长期支持分支都已经停止维护。也就是说即使sshd本身暂时不被这个CVE波及配套的加密库也已经是安全盲区。这也是为什么本次升级我会把OpenSSL一起纳入范围既然要动编译链就一次把技术债还清。2. 动手前先盘点环境版本、系统、服务管理方式源码编译升级OpenSSH最怕两件事一是远程操作中断导致失联二是新版本与系统既有配置不兼容导致sshd无法启动。这两件事都可以通过充分的预检来避免。花十分钟做环境盘点比升级失败后花两小时救数据划算得多。2.1 三步看清当前版本和依赖第一步确认当前OpenSSH和OpenSSL版本。注意ssh -V的输出是写到stderr的直接执行屏幕上能看到但如果不重定向脚本或文档里抓取时容易漏掉信息。ssh -V 21 # 或者查看sshd本身的版本 /usr/sbin/sshd -V 21 openssl version -a第二步确认操作系统发行版和init系统。不同发行版在编译依赖、服务名和配置文件路径上都有差异不能一概而论。cat /etc/os-release ps -p 1 -o comm systemctl status sshd 2/dev/null || service sshd status第三步确认当前sshd的监听端口、配置文件和认证方式。升级前必须知道当前的登录链路是什么样否则替换完二进制后可能发现配置文件里有关键参数变动导致彻底无法登录。ss -tlnp | grep :22 grep -E ^(Port|PermitRootLogin|PubkeyAuthentication|PasswordAuthentication) /etc/ssh/sshd_config2.2 不同发行版的兼容性差异下面这个表是我在实际操作中踩过坑后总结出来的直接决定你后面要不要额外处理一些环境问题。发行版默认OpenSSL默认OpenSSH注意点CentOS 71.0.2k7.4p1系统自带的OpenSSL老得离谱但yum、curl等大量系统组件依赖它绝对不能直接覆盖CentOS 81.1.1k8.0p1依赖库相对新编译OpenSSL 3.0基本无障碍Rocky/AlmaLinux 93.0.78.7p1系统自带OpenSSL 3.x可以跳过OpenSSL升级步骤直接编译OpenSSHUbuntu 20.041.1.1f8.2p1需要额外确认libpam0g-dev和zlib1g-dev已经安装Ubuntu 22.043.0.28.9p1系统自带OpenSSL 3.x同样可以跳过OpenSSL升级但为了统一版本我一般还是会编译安装最新3.0.xDebian 11/121.1.1n / 3.0.x8.4p1 / 9.2p1apt源里openssl版本比CentOS 7干净很多编译依赖用build-dep最省事如果是CentOS 7这种老系统我强烈建议OpenSSL和OpenSSH都编译安装到独立目录不要动系统自带的任何openssl相关库。因为yum在安装或更新软件包时会重新生成依赖关系一旦发现libssl.so.10被替换或删除整个包管理器都可能瘫痪。2.3 备份策略不备份就升级是给自己埋雷源码编译升级OpenSSH备份的重点不是整个系统快照而是下面这几个具体对象# 备份配置目录 cp -a /etc/ssh /etc/ssh.bak.$(date %Y%m%d) # 备份关键二进制 mkdir -p /opt/ssh-backup/bin cp -a /usr/sbin/sshd /opt/ssh-backup/ cp -a /usr/bin/ssh /opt/ssh-backup/ cp -a /usr/bin/scp /opt/ssh-backup/ cp -a /usr/bin/sftp /opt/ssh-backup/ # 确认没有任何进程正在占用待替换的文件正常sshd常驻时会占用但备份不受影响 lsof /usr/sbin/sshd另外要多说一句如果服务器上有大量存量会话替换sshd二进制后已经建立的连接不会断新连接才会走新版本的sshd。所以备份完二进制后不需要担心当前会话被切断。真正要担心的是restart服务之后新版本启动失败。因此强烈建议操作前先打开一个逃生通道比如云控制台的VNC、带外管理口或者在另一个端口临时启动一个老版本的sshd实例。这样即使正式服务挂了也不至于被锁在门外。3. OpenSSL 3.0升级单独编译安装别碰系统自带库OpenSSL升级是整个过程中最需要克制的一步。很多第一次做升级的人下意识想的是把系统自带的OpenSSL卸载了装新版本上去。这个想法在纯实验环境可行在真实服务器上就是灾难。系统里几十个程序链接的都是/lib64/libssl.so.10这类旧库文件你把它换成新版本ABI不兼容轻则curl断掉重则整个系统网络栈崩溃。3.1 为什么OpenSSH 9.8p1建议搭配OpenSSL 3.xOpenSSH 9.8p1在configure阶段会检测OpenSSL版本虽然它也能连旧版本的libssl但从维护角度讲OpenSSL 1.1.1和1.0.2都已经EOL后续不会再收到任何安全补丁。如果继续用它们编译OpenSSH等于把升级OpenSSH的努力浪费了一半sshd是新的底层加密库全是老的已知漏洞。长期支持分支里我建议选择OpenSSL 3.0.x。它经过多年迭代稳定性已经接近1.1.1的成熟度同时又具备QUIC、FIPS模块等新能力。我写这篇文章时3.0分支的小版本已经更新到3.0.15建议下载这个版本或者去官网选3.0系列的最新补丁版本。别追大版本号够用就好。3.2 编译三步走解压、config、make install把源码下载到/usr/local/src下进行编译是最经典也最清晰的路径cd /usr/local/src wget https://www.openssl.org/source/openssl-3.0.15.tar.gz tar xzf openssl-3.0.15.tar.gz cd openssl-3.0.15 ./config --prefix/usr/local/openssl-3.0.15 \ --openssldir/usr/local/openssl-3.0.15/ssl \ shared no-async make -j$(nproc) make install这里几个参数需要解释一下。--prefix指定了OpenSSL的安装根目录我选择装到独立的/usr/local/openssl-3.0.15下面这样所有文件都在一个目录里后续回滚非常方便。--openssldir指定证书配置目录OpenSSL会在这里放置openssl.cnf和证书目录结构。shared表示生成动态库这是必须的因为OpenSSH编译时默认会链接libcrypto动态库。no-async是很多人在编译时忽略的坑它禁用异步I/O模块。在某些旧内核或者缺少完整内核头文件的环境里async模块会编译失败加上这个参数可以省掉很多折腾。make -j$(nproc)是并行编译nproc获取CPU核心数。如果服务器内存比较小建议把并行度降一降比如-j2避免编译时内存被打满导致OOM。3.3 动态库路径ld.so.conf还是rpath编译完OpenSSL后最重要的一个环节是让系统找到新库。很多教程会教你把/usr/local/openssl-3.0.15/lib64写进/etc/ld.so.conf.d/然后执行ldconfig。这个方案简单高效但影响范围很大只要程序加载了libcrypto.so.3都会切到这个新版本。如果这个新版本和某个老程序不兼容那个老程序就会开始报错。更精细的方案是用rpath。在编译OpenSSH时把新OpenSSL库的路径固化到可执行文件里这样一来只有ssh、sshd这些OpenSSH二进制会使用新OpenSSL库其他系统程序仍然使用自带的旧库。这个隔离效果正好满足了只升级目标服务的需求export LDFLAGS-Wl,-rpath,/usr/local/openssl-3.0.15/lib64 -L/usr/local/openssl-3.0.15/lib64 export CPPFLAGS-I/usr/local/openssl-3.0.15/include如果你的服务器用途比较单一比如就是一个内网堡垒机那用ld.so.conf方案也可以。但我个人的习惯是能用rpath隔离就尽量隔离避免为了修OpenSSH的漏洞反而引入了新的系统级隐患。3.4 验证新OpenSSL是否可用编译安装完成后用绝对路径执行新版本确认它正常加载/usr/local/openssl-3.0.15/bin/openssl version # 预期输出OpenSSL 3.0.15 31 Jul 2024 (Library: OpenSSL 3.0.15 ...)再检查一下动态库路径是否被正确嵌入到后续要编译的程序中。现在暂时不用管等OpenSSH编译完用ldd /usr/sbin/sshd检查libcrypto.so.3的来源就能确认rpath是否生效。4. OpenSSH 9.8p1编译安装configure参数才是重头戏OpenSSL就位后开始编译OpenSSH。这个阶段真正决定成败的不是make那一下而是configure参数是否匹配你的系统环境。参数配错后面启动阶段会报出一堆莫名其妙的错误。4.1 下载源码与准备编译依赖OpenSSH 9.8p1的源码可以到OpenSSH官网或国内镜像站下载。建议下载到/usr/local/src统一管理cd /usr/local/src wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz tar xzf openssh-9.8p1.tar.gz cd openssh-9.8p1编译依赖方面CentOS 7和Ubuntu的命令完全不同。CentOS 7需要安装如下软件包yum install -y gcc make pam-devel zlib-devel perlUbuntu 22.04则需要apt update apt install -y build-essential libpam0g-dev zlib1g-dev这里有个容易忽略的点因为我们要用新编译的OpenSSL所以系统自带的openssl-devel或libssl-dev装不装都不影响OpenSSH编译。但zlib-devel是必须的OpenSSH默认启用zlib压缩支持缺少这个头文件configure会提醒找不到zlib库。4.2 configure关键参数逐一说明下面是完整的configure命令我加了很多注释方便你对照自己的环境修改./configure \ --prefix/usr \ --sysconfdir/etc/ssh \ --with-ssl-dir/usr/local/openssl-3.0.15 \ --with-pam \ --with-privsep-path/var/empty/sshd \ --with-md5-passwords \ --with-kerberos5/usr/lib64 \ LDFLAGS-Wl,-rpath,/usr/local/openssl-3.0.15/lib64 -L/usr/local/openssl-3.0.15/lib64 \ CPPFLAGS-I/usr/local/openssl-3.0.15/include逐个讲清楚这直接关系到后续能不能少踩坑--prefix/usr指定安装目录为/usr。这样make install会把新的ssh、sshd、scp等二进制直接替换到/usr/bin和/usr/sbin下当前shell的PATH立刻生效不需要额外做符号链接。如果你更保守可以装到/usr/local/openssh然后手动维护PATH和systemd服务指向但操作会复杂很多。--sysconfdir/etc/ssh配置文件目录。保持系统原有路径能够直接沿用已有的sshd_config和host key。--with-ssl-dir/usr/local/openssl-3.0.15指定使用新编译的OpenSSL头文件和库。--with-pam启用PAM支持。绝大多数Linux发行版在sshd中启用PAM是为了支持密码认证、账户锁定、审计等功能。如果不启用可能会出现能登录但last记录不到、登录失败但没有任何审计日志等奇怪问题。--with-privsep-path/var/empty/sshd权限分离目录。OpenSSH通过把sshd拆分成特权进程和非特权进程来提高安全性这个目录是非特权进程的临时运行空间必须存在且权限正确否则sshd启动时直接报错。--with-md5-passwords允许使用MD5加密的密码字段。如果你系统里的用户密码是MD5格式而你想兼容它们就加上这个选项。LDFLAGS和CPPFLAGS这里就是我上一步提到的rpath隔离方案。让OpenSSH二进制运行时直接找到/usr/local/openssl-3.0.15/lib64下的libcrypto.so.3而不会去抢其他程序用的旧库。如果你在configure阶段遇到OpenSSL version headers do not match library之类的版本不一致报错可以先确认/usr/local/openssl-3.0.15/include/openssl/opensslv.h和lib目录下的libcrypto确实来自同一版本。如果确认无误仍然报错可以加一个--without-openssl-header-check跳过版本检查但务必保证版本本身是一致的。4.3 make与install覆盖旧版本前先做三件事configure通过后执行编译make -j$(nproc)编译过程通常在几分钟内结束。在make install前先把这三件事做完确认备份目录里的二进制齐全见2.3节的清单。查看一下当前系统sshd_config里是否存在自定义配置块。虽然9.8p1向下兼容大部分旧配置但极少数废弃关键字会在sshd -t阶段就被拒绝。先心里有数后面处理时就不会慌。确认带外管理通道可用。至少确保有一个通过KVM、控制台或其他非SSH方式登录服务器的途径。完成后执行安装make install安装过程会覆盖/usr/bin/ssh、/usr/sbin/sshd、/usr/bin/scp等文件。安装结束后先不要立刻重启服务先做一次配置语法检查/usr/sbin/sshd -t这一步非常关键。它输出为空说明配置正常如果有问题会明确指示哪一行配置有问题。看到报错先冷静逐行解决千万不要在重启服务后再去排查。4.4 检查关键目录和权限安装完成后检查权限分离目录是否存在且属主正确mkdir -p /var/empty/sshd chown -R root:root /var/empty chmod 711 /var/empty chmod 755 /var/empty/sshd同时检查host key的权限。OpenSSH 9.8p1对host key的权限要求更严格私钥权限必须是600公钥可以644chmod 600 /etc/ssh/ssh_host_*_key chmod 644 /etc/ssh/ssh_host_*.pub检查完这些用ldd再确认一下动态库链接到了新OpenSSLldd /usr/sbin/sshd | grep ssl # 预期输出包含类似 # libcrypto.so.3 /usr/local/openssl-3.0.15/lib64/libcrypto.so.3如果看到libcrypto.so.1.1或者libssl.so.10说明rpath没有生效这时候就检查configure时的LDFLAGS是否传入正确重新编译。5. 服务切换与验证别把自己锁在门外编译安装完成但服务还是旧版在跑zi只完成了80。最终的切换和验证环节最容易出问题的地方往往不在启动新版本本身而在于切换方式不够平滑。5.1 先测试新sshd再动正式服务我不推荐直接systemctl restart sshd除非你完全确认配置没有兼容性问题。更稳妥的方式是先开一个测试端口手动拉起一个新版sshd实例/usr/sbin/sshd -f /etc/ssh/sshd_config -p 2222 -E /var/log/sshd-test.log这条命令会在2222端口启动新的sshd进程并把日志输出到/var/log/sshd-test.log。然后从本机或另一台机器连接测试ssh -v -p 2222 root127.0.0.1如果这个过程能正常弹出密码或密钥验证、看到Shell说明新版sshd运行正常可以放心替换正式服务。测试完毕kill掉这个临时进程pkill -f sshd.*-p 2222注意这个pkill可能会匹配到别的进程谨慎起见可以用ss -tlnp先查一下2222端口的PID再精确删除。5.2 重启正式服务并验证版本确认测试无误后正式重启服务systemctl restart sshd # 如果是SysVinit系统 # service sshd restart重新加载后用下面几条命令核对版本、监听状态和进程信息ssh -V 21 /usr/sbin/sshd -V 21 ss -tlnp | grep :22 ps -ef | grep [s]shd tail -n 20 /var/log/secure 2/dev/null || journalctl -u sshd -n 20此时ssh -V输出应该明确显示OpenSSH_9.8p1并且日志里没有报错。5.3 启动失败后的冷启动排查冷启动必须提到台面上讲。如果sshd -t明明显示没问题但restart后进程起不来最常见的三个原因是host key不存在或权限异常。表现为日志里出现sshd: no hostkeys available或sshd: permission denied on key。解决办法是重新生成host keyssh-keygen -A。/var/empty/sshd权限不对。表现为missing privilege separation directory或sshd: /var/empty/sshd must be owned by root and not group or world-writable。照着4.4节修复权限即可。libcrypto动态库找不到。表现为error while loading shared libraries: libcrypto.so.3: cannot open shared object file。用ldd检查确认rpath或ldconfig配置是否正确。真到了无法救场的地步回滚方案要能立刻执行。我用的是备份目录直接覆盖回去systemctl stop sshd cp -a /opt/ssh-backup/sshd /usr/sbin/sshd cp -a /opt/ssh-backup/ssh /usr/bin/ssh systemctl start sshd执行完这几条服务会退回升级前的状态。回滚后不要急着离开把配置文件改动也一并还原然后重新检查一次sshd -t。6. 升级后的配置兼容与安全收尾别让9.8p1的优势打折扣二进制升级成功不代表整个工作就完结了。OpenSSH版本升级后最常遇到的问题有两类旧参数被废弃导致配置校验失败以及新版本默认策略更严格导致某些旧登录方式失效。6.1 旧版sshd_config在新版本下的兼容性处理升级后用这条命令查看实际生效的配置/usr/sbin/sshd -T它会输出所有生效配置项即使你没写在sshd_config里。如果你在日志中看到类似Deprecated option的警告直接编辑/etc/ssh/sshd_config删掉对应行。常见的需要关注的关键字有过时的UseDNS、GSSAPIAuthentication等。另外9.8p1对公钥算法和主机密钥类型的要求比老版本更严格。如果之前还在使用DSA或较弱的RSA host key建议直接生成新的ed25519密钥并在sshd_config中明确HostKey路径。升级后我一般会用下面这组命令把host key刷新一遍并保持配置一致性install -m 600 /dev/null /etc/ssh/ssh_host_ed25519_key ssh-keygen -A chmod 600 /etc/ssh/ssh_host_*_key /usr/sbin/sshd -t systemctl restart sshd6.2 9.8p1实际使用中给运维带来的变化从我实机操作的经验看9.8p1最直观的变化是编译产物对OpenSSL的依赖链路更敏感了。如果你按照本文的方案编译会不会发现sshd二进制启动内存占用略高了那么一点这正常因为它使用的OpenSSL 3.0模块在加载时会有额外的初始化开销不必过度担心。另外新版本对错误日志的记录粒度更细。例如网络上有人扫描22端口以前可能只记录Connection closed by authenticating user9.8p1会记录更多连接细节和认证失败前的状态。这对日常安全审计是好事但也意味着/var/log/secure或journald日志量会上升。建议提前配置logrotate避免日志把磁盘占满。6.3 加固三板斧登录方式、密钥轮换、日志监控升级版本只是排掉了已知的雷真正的安全水位还要靠配置加固来维持。我每次完成sshd版本升级后都会顺手做三件事。第一件事是收紧登录入口。如果运维场景不需要密码登录直接关掉密码认证# /etc/ssh/sshd_config PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no第二件事是确认host key和用户authorized_keys的属主权限。sshd对.ssh目录权限要求很严格用户目录的.ssh权限应该是700authorized_keys是600否则会出现Authentication refused: bad ownership or modes。第三件事是增加失效熔断。在sshd_config中配置合理的MaxAuthTries和LoginGraceTime降低暴力破解风险MaxAuthTries 3 MaxSessions 4 LoginGraceTime 30配置修改后同样要跑一遍/usr/sbin/sshd -t再重启。最后再分享一个关于编译升级的小技巧编译OpenSSH时不同版本之间对configure参数的容忍度差异不小。我在CentOS 7上第一次编译9.8p1时曾因为系统自带的PKG_CONFIG_PATH指向了老版OpenSSL的pc文件导致configure阶段始终检测到1.0.2的头文件。当时排查了很久最终找到的办法是在configure前把环境变量强制清干净export PKG_CONFIG_PATH/usr/local/openssl-3.0.15/lib64/pkgconfig这个小细节看起来不起眼但在跨版本升级场景下特别管用。如果你卡在configure的版本检测上先检查PKG_CONFIG_PATH再考虑加--without-openssl-header-check这类跳过参数。这样的问题往往一查一个准。