Ubuntu 22.04手动编译升级OpenSSH 9.6与OpenSSL 3.2完整指南 在运维工作中最烦的一件事就是安全扫描报告像雪花一样飘过来打开一看OpenSSH 和 OpenSSL 又各自挂着好几个 CVE。Ubuntu 22.04 LTS 自带的 OpenSSH 8.9p1 和 OpenSSL 3.0.2 虽然官方源持续推送补丁但总有那么几种场景让人坐不住要么是合规要求版本号必须到某个线要么是扫描器盯着软件版本号不放要么就是想用上新版算法和特性。于是手动编译升级 OpenSSH 9.6p1顺手适配 OpenSSL 3.2.0就成了一个绕不开的活儿。这篇文章就是我从准备到回滚的完整记录适合有一定 Linux 基础、需要给服务器做版本升级的运维同学参考。1. 升级前的环境检查与备份1.1 为什么选择手动编译升级有人可能会问Ubuntu 不是有 apt 源吗等官方更新不就行了理论上没错但实际操作中你会遇到几个问题。首先某些安全扫描工具判断漏洞是看版本号的比如它发现你跑的是 OpenSSH_8.9p1就认定你有 CVE-2023-51385 之类的风险哪怕你其实已经打了补丁。这种时候解释成本比升级成本还高。其次OpenSSH 9.6p1 修复了用户名枚举等一批漏洞同时引入了一些新的密钥交换算法但 Ubuntu 22.04 的官方源未必会在第一时间把大版本推上来更多时候是长期维护某个小版本。最后有些内网环境需要统一 ssh 客户端行为或者要编译进特定的 PAM 模块这都得靠源码编译才能灵活控制。所以我的选择是源码编译安装到独立目录然后通过软链接或者改 systemd 单元的方式切换。这样做的好处是随时能回滚不污染系统自带的 ssh 组件风险可控。1.2 系统环境确认与备份要点动手之前先确定自己当前的环境。执行一条命令就能看到现状ssh -V正常情况会输出类似这样的内容OpenSSH_8.9p1 Ubuntu-3ubuntu0.6, OpenSSL 3.0.2 15 Mar 2022这里要注意OpenSSH 后面的 Ubuntu-3ubuntu0.x 是发行版自己的小版本号OpenSSL 3.0.2 是系统默认的库版本。文章开头提到的“openssl version mismatch”这类报错往往就是升级之后新旧库混用产生的后面排查部分我会专门展开。备份是升级操作的救命稻草我每次都会把下面这些文件原样复制一份到 /root/backup-ssh/ 下/etc/ssh/ 整个目录包含 sshd_config 和所有主机密钥/usr/sbin/sshd 二进制的原始版本/usr/bin/ssh、/usr/bin/scp、/usr/bin/sftp/usr/lib/openssh/sftp-server/etc/pam.d/sshd/lib/systemd/system/ssh.service 和 ssh.socket如果条件允许我会建议先在虚拟机或备用机上演练一遍。远程升级最怕的就是 sshd 起不来然后自己被关在门外。后面我会讲怎么尽量规避这种情况但备份这一步绝对不能省。2. 编译安装 OpenSSL 3.2.02.1 编译前的准备工作和工具链确认编译 OpenSSL 需要基础工具链Ubuntu 22.04 上执行apt update apt install -y build-essential wget curl zlib1g-devOpenSSL 源码编译本身不需要额外的 libssl-dev但有 zlib 支持的话编译出的库能利用压缩功能所以提前装好 zlib1g-dev 没坏处。Perl 也是 OpenSSL 编译的必需组件系统自带不用额外处理。有个细节值得说OpenSSL 3.2.0 在 2023 年 11 月发布和 OpenSSH 9.6p1 几乎是前后脚的关系。OpenSSH 9.6 的官方发布说明里明确提到针对 OpenSSL 3.2 做了兼容性修复这也是我把这两个版本搭配在一起的原因。如果你手头拿的是 OpenSSH 9.5 之前的版本直接链 OpenSSL 3.2 可能会遇到编译告警甚至运行异常所以版本搭配不是乱选的。2.2 编译参数选择与安装路径规划下载源码包并解压cd /usr/local/src wget https://www.openssl.org/source/openssl-3.2.0.tar.gz tar -xzf openssl-3.2.0.tar.gz cd openssl-3.2.0接下来是 configure 环节我的参数是./Configure \ --prefix/usr/local/openssl-3.2.0 \ --openssldir/usr/local/openssl-3.2.0/ssl \ shared \ zlib注意看这里的 --prefix 和 --openssldir 都指向了独立目录 /usr/local/openssl-3.2.0。这是整个升级方案里最核心的决策绝对不要覆盖系统自带的 OpenSSL。Ubuntu 22.04 有一大堆系统库链的是 OpenSSL 3.0.x你要是把 3.2.0 直接跑到 /usr 下面apt 的包管理、curl、wget、Python 等组件会立刻出问题那场面比 sshd 起不来还要灾难。shared 选项是必须的它让 OpenSSL 编译出动态库 libssl.so 和 libcrypto.so。如果只编静态库后面 OpenSSH 链接时会麻烦很多维护也不方便。zlib 则是开启压缩支持。make 编译make -j$(nproc)-j 后面的参数用 CPU 核心数能明显缩短编译时间。整个过程大概几分钟到十几分钟不等取决于机器性能。编译完成后执行安装命令make install_sw这里我特意用了 install_sw 而不是 install。install_sw 只安装软件部分也就是二进制和库文件跳过文档和示例的安装。对于生产环境装文档没什么意义还多占几十 MB 空间。验证一下安装结果/usr/local/openssl-3.2.0/bin/openssl version输出OpenSSL 3.2.0 23 Nov 2023 (Library: OpenSSL 3.2.0)这个版本号在后续排查中很有用因为它以十六进制形式编码在库内部比如 3.2.0 的版本号对应是 0x30200000而系统自带的 3.0.2 对应 0x30000020。后面如果你在日志里看到 built against 30000020 或者 might be 30200000就知道说的是哪个版本了。2.3 验证 OpenSSL 库文件是否被正确链接编译 OpenSSH 之前确认一下库文件确实在预期位置ls -l /usr/local/openssl-3.2.0/lib/你应该能看到 libcrypto.so.3 和 libssl.so.3 等文件。此时可以先跑一下 openssl 的自检功能/usr/local/openssl-3.2.0/bin/openssl speed -seconds 1 rsa2048如果这条命令能正常输出 RSA 2048 的性能数据说明这个独立安装的 OpenSSL 基本可用可以放心交给下一步的 OpenSSH 编译使用了。3. 编译安装 OpenSSH 9.6p13.1 下载源码与依赖检查OpenSSH 源码包可以从官方镜像站获取cd /usr/local/src wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.6p1.tar.gz tar -xzf openssh-9.6p1.tar.gz cd openssh-9.6p1编译之前检查一下依赖特别要确认系统和 PAM 相关头文件是否完整apt install -y libpam0g-dev libselinux1-devlibpam0g-dev 提供 PAM 开发头文件如果你打算开启 UsePAM yes这个必须装。libselinux1-dev 在 Ubuntu 上默认不需要但加上有备无患免得哪天内核启用了 SELinux 或者你把这套包移植到 CentOS 上时抓瞎。3.2 configure 参数解析与 OpenSSL 适配OpenSSH 的 configure 是整个编译过程里最值得细看的环节。我的完整参数如下./configure \ --prefix/usr/local/openssh-9.6p1 \ --with-ssl-dir/usr/local/openssl-3.2.0 \ --with-pam \ --with-md5-passwords \ --with-privsep-path/var/empty/sshd \ --with-sandboxseccomp_filter逐个说--prefix指定 OpenSSH 安装目录。设成独立目录的好处是后面能直接找到所有新文件回滚时一删就干净。--with-ssl-dir指向刚才装的 OpenSSL 3.2.0。configure 会到这个目录下找头文件和库文件这样编译出的 sshd 就会链接到新版本的 OpenSSL而不是系统自带的 3.0.2。--with-pam启用 PAM 认证支持。Ubuntu 默认的 sshd 是带 PAM 的如果你这里漏了后面会发现密码登录永远失败。--with-md5-passwords兼容老旧的 MD5 哈希口令。有些历史遗留系统还在用这种哈希打开这个选项能保证那些老用户还能登录。--with-privsep-path指定权限分离目录。sshd 会在此目录下以非特权身份运行子进程是 OpenSSH 安全机制的关键一环。--with-sandboxseccomp_filter在 Linux 上使用 seccomp 内核沙箱机制给 sshd 增加一层防护。configure 输出里有一行很关键你会看到类似这样的编译特性汇总OpenSSL headers version: 3.2.0 OpenSSL library version: 3.2.0这行信息说明链接的确实是新版本 OpenSSL。如果你在这里看到 3.0.2就要回头检查 --with-ssl-dir 是否写对了或者是不是环境变量里 LD_LIBRARY_PATH 干扰了 configure 的检测。3.3 编译、安装与二进制替换configure 通过后就开始编译make -j$(nproc)OpenSSH 的编译比 OpenSSL 快不少一两分钟基本完事。接着安装make install安装完成后新二进制会出现在/usr/local/openssh-9.6p1/sbin/sshd/usr/local/openssh-9.6p1/bin/ssh/usr/local/openssh-9.6p1/bin/scp/usr/local/openssh-9.6p1/libexec/sftp-server此时我不建议直接把这些文件复制到 /usr/bin 和 /usr/sbin 覆盖系统自带的版本而是通过系统服务配置来切换这样回滚时最干净。先看看当前的 systemd 服务怎么写的systemctl cat ssh输出里会有一行 ExecStart/usr/sbin/sshd -D ...这就是 sshd 服务启动时的实际命令。为了让系统用新编译的 sshd我采用创建 drop-in 配置的做法systemctl edit ssh在弹出的编辑窗口里写入[Service] ExecStart ExecStart/usr/local/openssh-9.6p1/sbin/sshd -D $SSHD_OPTS注意第一行 ExecStart 是必须的它的作用是清空原有的启动命令否则会出现多个 ExecStart 导致服务无法启动。$SSHD_OPTS 是 ssh.service 里常见的环境变量保留它不会影响后续参数传递。同样的道理如果你想让系统里的 ssh 客户端命令也切到新版本可以创建软链接ln -sf /usr/local/openssh-9.6p1/bin/ssh /usr/local/bin/ssh ln -sf /usr/local/openssh-9.6p1/bin/scp /usr/local/bin/scp但不建议直接覆盖 /usr/bin/ssh因为系统还有很多脚本可能依赖旧版本行为一旦新版本有细微变化排查起来会很头疼。3.4 配置系统服务与开机自启改完 systemd 配置后重载再重启服务systemctl daemon-reload systemctl restart ssh systemctl enable ssh此时先看服务状态systemctl status ssh如果一切正常会显示 active (running)。然后确认实际运行的 sshd 版本/usr/local/openssh-9.6p1/sbin/sshd -V同时查看端口和进程ss -tlnp | grep :22 ps -ef | grep sshd我一般还会顺手确认一下新 sshd 链接的 OpenSSL 是不是 3.2.0ldd /usr/local/openssh-9.6p1/sbin/sshd | grep -E ssl|crypto如果输出类似 libssl.so.3 /usr/local/openssl-3.2.0/lib/libssl.so.3说明整个链路正确。如果看到指向系统的 /usr/lib/x86_64-linux-gnu/那就要回看 configure 的 --with-ssl-dir 是否真的生效或者是不是 make clean 之后再重新编译过。4. sshd 配置与远程连接验证4.1 核心配置项调整与测试新版 OpenSSH 对某些配置项的默认值有变化升级后最好检查一下 /etc/ssh/sshd_config重点看这几个地方PasswordAuthentication yes UsePAM yes PermitRootLogin prohibit-password Subsystem sftp /usr/local/openssh-9.6p1/libexec/sftp-server前两行决定密码登录和 PAM 认证是否能正常工作。第三行是根用户登录策略建议保持 prohibit-password允许密钥登录但禁止密码登录。第四行的 sftp-server 路径要特别注意因为新版二进制的 sftp-server 并不在系统默认的 /usr/lib/openssh/ 目录下如果不改这条路客户端连上 sftp 会直接报错。改完配置后先做语法检查/usr/local/openssh-9.6p1/sbin/sshd -t -f /etc/ssh/sshd_config没有任何输出说明配置语法正确。这里有个小坑如果你把 sshd_config 里某行写成Subsystem sftp internal-sftp那就不需要外部 sftp-server 文件路径问题自然消失。要不要用 internal-sftp 看你的偏好我一般保持默认方式方便后续单独管理 sftp 功能。4.2 连接测试与安全性验证在重启 sshd 之前强烈建议先在另一个终端开一个会话以免配置出错把自己锁在外面。我用 tmux 或者 screen 做个保底操作万一服务起不来至少还有窗口能救回来。完成重启后在客户端机器上执行ssh -V ssh useryour-server建议测试下面几种情况密钥登录是否正常密码登录是否正常如果配置了 PasswordAuthentication yessftp 连接是否正常scp 传输是否正常新版本的 ssh 客户端可能会对某些老旧算法发出警告比如 ssh-dss 之类的这是正常现象OpenSSH 9.x 系列默认禁用了不安全的算法。如果内网有老设备必须用这些算法可以在 sshd_config 里手动添加HostKeyAlgorithms ssh-rsa PubkeyAcceptedAlgorithms ssh-rsa前提是你确实清楚这些算法带来的安全风险。安全性验证这一步我还会在服务器上检查监听情况和日志ss -tlnp | grep :22 journalctl -u ssh -n 50如果日志里没有 Pandaemonium都是正常的会话建立和断开记录说明升级基本稳了。5. 常见问题与实战排查5.1 SSH 无法连接时的快速定位流程升级过程中最吓人的就是突然连不上服务器有时候是自己误操作有时候是服务起不来。我的排查流程固定如下先看服务是否在运行systemctl status ssh如果显示 failed立即查看错误日志journalctl -u ssh -n 30常见的错误有两类。第一种是缺少权限分离目录/var/empty must be owned by root and not group or world-writable.解决方法mkdir -p /var/empty/sshd chmod 700 /var/empty/sshd chown root:root /var/empty/sshd第二种是没有可用的主机密钥sshd: no hostkeys available这是升级后最常见的问题之一新版本对主机密钥的位置和权限检查更严格。解决办法是重新生成ssh-keygen -A生成后再次检查 /etc/ssh/ 下 ssh_host_*_key 文件的权限确保都是 root 所有权限 600对应的 .pub 为 644。如果服务在运行但连不上检查端口监听和防火墙ss -tlnp | grep 22 sudo ufw status很多时候是 ufw 默认策略把 22 端口挡了放行即可sudo ufw allow 22/tcp5.2 OpenSSL 版本不匹配的解决方法升级后偶尔会遇到这类报错openssl version mismatch. built against 30000020, you have 30500060其实这个报错的意思是某个程序在编译时链接的是 OpenSSL 3.0.2版本号编码为 30000020但运行时加载的库是另一个版本比如 30500060 对应未来的某个 3.5.x两者不匹配程序直接拒跑。出现这个问题的根源通常是多个 OpenSSL 版本同时存在于系统而动态链接器 LD_LIBRARY_PATH 或者 ld.so.conf 把顺序搞乱了。排查步骤如下ldd /usr/local/openssh-9.6p1/sbin/sshd | grep -E ssl|crypto如果发现链接的是 /usr/local/openssl-3.2.0/lib/ 下的库但还有另一个程序链到了系统库就检查一下环境变量echo $LD_LIBRARY_PATH我一般不建议把 /usr/local/openssl-3.2.0/lib 加进全局 LD_LIBRARY_PATH因为这会影响系统上所有程序容易引发连锁反应。更稳妥的做法是在编译 OpenSSH 时使用 rpath 让二进制自包含运行路径。具体说在 configure 时加入./configure LDFLAGS-Wl,-rpath,/usr/local/openssl-3.2.0/lib ...这样编译出的 sshd 会在启动时优先加载 /usr/local/openssl-3.2.0/lib 下的库不会受系统环境变量干扰。如果你已经编译完了可以修改 systemd drop-in 配置加一个环境变量[Service] EnvironmentLD_LIBRARY_PATH/usr/local/openssl-3.2.0/lib然后 daemon-reload 再重启 ssh效果相同。5.3 证书校验与密钥兼容问题有同学反映升级后 sshd 日志里出现error: ssl certificate verify result: unable to get local issuer certificate这个错误通常出现在配置了证书认证的场景。比如你给 sshd 配了 HostCertificate那么客户端连接时就会校验证书链。如果 CA 证书没有放在系统信任区或者客户端本地信任库不完整就会出现上述报错。解决办法分两头。在服务器端确认 sshd_config 里设置的 HostCertificate 和 HostKey 指向的路径存在且权限正确在客户端如果你自己搭的 CA 没导入操作系统信任区连接时加参数指定ssh -o CAKeys/path/to/ca.pub userhost或者把 CA 证书放到客户端的 ~/.ssh/known_hosts 里让 ssh 自动信任。另外一个常见问题是密钥算法兼容。升级到 OpenSSH 9.6p1 后旧客户端可能无法用 RSA 密钥登录因为新版默认不再接受 SHA-1 签名的 RSA 密钥。如果必须兼容可以在 sshd_config 中打开旧算法但要意识到这属于降低安全性实际生产里建议逐步迁移到 ed25519 或者其他新算法。5.4 常见问题速查表为了方便查阅我把升级过程里容易出现的问题整理成表现象可能原因解决方法sshd 启动失败提示 /var/empty 权限错误权限分离目录不存在或权限不对mkdir -p /var/empty/sshd chmod 700 /var/empty/sshd chown root:root /var/empty/sshdsshd 启动失败提示 no hostkeys available主机密钥缺失ssh-keygen -A密码登录失败PAM 未启用或配置路径不对开启 --with-pam 重新编译sshd_config 中 UsePAM yesSSH 连不上端口无监听服务未启动或防火墙拦截systemctl status ssh 查看日志ufw allow 22/tcpOpenSSL version mismatch运行时加载的库版本与编译时不一致使用 rpath 或修改 systemd 环境变量 LD_LIBRARY_PATHsftp 无法使用sftp-server 路径不对修改 Subsystem sftp 指向新版路径或改用 internal-sftpunable to get local issuer certificate证书链不完整确认 HostCertificate 路径客户端指定 CAKeys 或导入 CA 证书旧客户端无法用 RSA 登录新版本默认禁用 SHA-1 签名临时在 sshd_config 启用 HostKeyAlgorithms ssh-rsa后续迁移到 ed25519这个表不是万能药但它能覆盖升级过程中绝大多数会踩的坑。真到排查的时候先看日志再对照表里列出方向去验证效率会高很多。6. 回滚与备份恢复方案该讲的升级和排查说完了最后留个后手怎么回滚。如果采用了我前面推荐的 drop-in 方式启动新版 sshd回滚非常轻松。删除 /etc/systemd/system/ssh.service.d/ 下的 drop-in 文件rm -rf /etc/systemd/system/ssh.service.d systemctl daemon-reload systemctl restart ssh这样 systemd 会回到原来 ssh.service 里定义的 ExecStart也就是系统自带的 /usr/sbin/sshd。如果你还改了 /etc/ssh/sshd_config把备份的原始配置恢复回去即可。如果只是临时切换新版做验证软链接之类的东西一并删掉就好。如果你没有用 drop-in而是直接把新版 sshd 覆盖到了 /usr/sbin/sshd那回滚就依赖第 1.2 节提到的备份。把备份的原始 sshd 二进制恢复回去cp /root/backup-ssh/sshd /usr/sbin/sshd然后重启服务。这里要提醒升级前必须确认备份目录里有你执行备份那一时刻的所有文件特别是主机密钥和 sshd_config否则回滚后可能出现权限不匹配导致无法连接的情况。我在实际踩过几次坑之后的体会是升级这种操作不怕过程繁琐就怕没有后路。多花五分钟做备份和回滚预案比出问题时熬夜救服务器划算得多。尤其是远程机器先把 tmux 窗口开好把回滚命令准备好心里才踏实。另外还有个值得养成的习惯编译 OpenSSH 或 OpenSSL 时把用到的 configure 参数和步骤记到运维笔记里。半年后你再看这份记录会感谢自己的。因为到时候系统又该升级了有了这套流程你只需要改一下版本号就能照着再来一遍。