☰
Ubuntu下源码编译升级OpenSSH 9.5p1与zlib-1.3实战指南
2026/10/20 22:17:20 网站建设 项目流程

简介:面向Ubuntu服务器管理员与运维人员,这一升级资料包含OpenSSH 9.5p1和zlib 1.3源码包及自动化脚本,用于解决旧版远程管理服务的安全漏洞与压缩库兼容问题,可在Ubuntu环境中一键编译、安装并完成服务切换。资源包体积小,共3个文件,包括两个tar.gz格式源码包与一个shell脚本,整体约3.18MB,适合直接拷贝到生产或测试服务器使用。脚本详细覆盖环境检查、zlib编译安装、OpenSSH编译安装、sshd配置调整、服务重启及ssh -V版本验证等关键步骤,同时包含升级前备份提示,帮助降低操作失误带来的回滚风险。目前已有706人学习下载,适合需要快速加固OpenSSH的Linux运维人员,也适合希望理解源码依赖关系的入门者参考。借助这一套资源,能够将安全补丁快速落地,在提升远程管理稳定性的同时减少手工编译的重复工作。

1. 升级 OpenSSH:被漏洞通报追着跑的一次环境改造

很多开发者的 Ubuntu 服务器跑着 OpenSSH 8.x 甚至更老的版本,平时没人觉得它碍事,直到某天安全扫描报告把sshd列为高危漏洞,并要求限期修复。Ubuntu 的 apt 仓库又不会那么快推送新版 OpenSSH,于是「手动下载源码、编译、升级」这条路就成了一道绕不过去的坎。这套流程本身不难,但真正做起来,踩坑的点几乎都藏在依赖版本、编译参数和配置文件的细节里——一个没注意到,就可能把自己锁在服务器门外。这篇文章就把我实际做过的一套升级整理出来,目标版本是 OpenSSH 9.5p1 + zlib-1.3,适合正在 Ubuntu 上被版本兼容、编译报错、升级后断连折腾的朋友,照着做,能省下不少冤枉路。

2. 版本与环境:为什么是 9.5p1 和 zlib-1.3

2.1 从 apt 仓库到手动编译的路线选择

先说一个很多人会问的问题:为什么不直接用apt install openssh-server?原因很简单——Ubuntu 20.04 的默认仓库里,OpenSSH 版本停留在 8.2p1,22.04 也只有 8.9p1。这两个版本在安全扫描工具里往往会被标记为存在已知 CVE,而且 apt 仓库更新节奏偏慢,指望它快速跟上上游是不太现实的。

另一个思路是添加 PPA 源,比如某些维护者会提供新版 OpenSSH 的 PPA。但 PPA 的方式有两个风险:第一,第三方源的编译配置未必和你的系统完全匹配,依赖关系可能冲突;第二,你无法控制它编译时带了哪些功能特性,比如是否保留 PAM 支持。对于生产环境,我更倾向于从官方源码自己编译,这样每一步配了什么参数、装到了哪里、哪些旧文件被替换了,心里都有数。

版本选择上,OpenSSH 9.5p1 是当时较新的稳定版本线,修复了多个漏洞,也在sshd的默认配置上有一些调整。zlib-1.3 则是配套的压缩库更新,OpenSSH 在启用Compression功能时会链接到 zlib,顺手升级能让链路都处于较新状态。这个组合在我的模拟项目X环境里跑过完整验证,Debian 系的 Ubuntu 20.04/22.04 都能用。

2.2 三个前置依赖的安装与验证

编译 OpenSSH 之前,需要先把系统基础依赖装齐。这里说的不是 zlib 本身,而是编译工具链和 OpenSSL 开发头文件。

sudo apt update sudo apt install -y build-essential gcc make libssl-dev libpam0g-dev libkrb5-dev

这行命令里,build-essential会拉取 gcc、g++、make 等基础编译工具;libssl-dev提供 OpenSSL 的头文件,OpenSSH 编译时缺了它会直接报openssl/opensslv.h: No such file or directory;libpam0g-dev是为了让新版 sshd 保留 PAM 认证支持,如果你原系统曾配置过密码登录或UsePAM相关选项,这一步别跳过;libkrb5-dev则对应 Kerberos 支持,用不到可以不装,但装上有备无患。

安装完后建议顺手验证一下工具链版本:

gcc --version make --version openssl version

我的实测环境里,gcc 9.4.0、OpenSSL 1.1.1f 的组合可以顺利完成 OpenSSH 9.5p1 的编译。如果你系统里的 OpenSSL 版本是 3.x 系列,编译时可能会遇到一些 API 调整导致的小警告,一般不影响最终产物,但要注意编译输出里有没有出现error:级别的信息。这一步做完,环境准备就算打底了。

3. 编译安装:OpenSSH 9.5p1 与 zlib-1.3 的完整落地流程

3.1 下载源码并解压

源码获取走官方归档路径,OpenSSH 的稳定版可以在官方镜像站找到,zlib 则从 zlib.net 或 GitHub 归档下载。我这里用的是固定版本号 9.5p1 和 1.3,避免拉取滚动更新导致行为不可控。

cd /usr/local/src wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.5p1.tar.gz wget https://zlib.net/zlib-1.3.tar.gz tar -zxvf openssh-9.5p1.tar.gz tar -zxvf zlib-1.3.tar.gz

之所以把源码统一放在/usr/local/src,是为了让后续编译产物和源码保持隔离,编译完的二进制在/usr/local/bin和/usr/local/sbin,源码则在/usr/local/src下,方便排查时对照查看。下载后建议校验一下文件的 SHA256 哈希,特别是从镜像站拉文件时,哈希对不上就换源重下,别在损坏的压缩包上浪费时间。

解压完成后,目录结构应该是两个独立的文件夹,先进入 zlib 的目录开始编译。有一点要提前说明:zlib 是一个独立库,OpenSSH 并不会自动去编译它,而是通过--with-zlib参数指定路径去链接。你先把 zlib 装好,OpenSSH 编译时才能找到它的头文件和静态库。

3.2 先装 zlib-1.3 再编 OpenSSH 的顺序不能反

zlib 的编译比较简单,三步走完:

cd /usr/local/src/zlib-1.3 ./configure --prefix=/usr/local/zlib make -j$(nproc) sudo make install

--prefix=/usr/local/zlib指定了安装目录,这样不会污染系统的/usr目录,后续卸载也方便——直接删掉这个目录就行。-j$(nproc)表示用满所有 CPU 核心并行编译,如果服务器核数很多但内存不大,可以改成-j2或-j4,避免编译时内存被打满。zlib 编译通常一两分钟就能完成,看到make install把头文件和库文件放进/usr/local/zlib就算成功。

接下来是 OpenSSH 本体。这里要特别提醒一个顺序问题:先编译 zlib,再编译 OpenSSH,绝不能反过来。OpenSSH 的configure脚本会去检查 zlib 是否存在,如果找不到,即便最终编译出了 sshd,运行时启用压缩也会报错或者根本起不来服务。我在第一次做的时候图省事,先编了 OpenSSH 再回头装 zlib,结果sshd -t直接报缺库,只能全部重来。

cd /usr/local/src/openssh-9.5p1 ./configure --prefix=/usr/local/openssh \ --with-zlib=/usr/local/zlib \ --with-ssl-dir=/usr/lib/ssl \ --with-pam \ --with-privsep-path=/var/empty \ -sysconfdir=/etc/ssh make -j$(nproc) sudo make install

每个参数都值得展开说明。--prefix=/usr/local/openssh把 OpenSSH 装到独立目录,好处是旧版二进制还在/usr/bin/ssh和/usr/sbin/sshd,万一新版有问题可以快速切回;--with-zlib=/usr/local/zlib就是前面提到的链接关键;--with-ssl-dir=/usr/lib/ssl指向系统 OpenSSL 配置目录,有些环境里这个路径是/usr/lib/x86_64-linux-gnu/ssl,可以用openssl version -d查一下再做对应修改;--with-pam保留 PAM 认证,不配的话密码登录大概率会失败;--with-privsep-path=/var/empty是特权分离的占位目录,OpenSSH 要求这个目录存在且为空;-sysconfdir=/etc/ssh让新版本的配置文件继续沿用/etc/ssh/sshd_config,不会额外生成一套新配置。

如果你打算保持系统原有的服务管理方式,这里还有一个小改动值得做:make install之后,OpenSSH 会默认把sshd放到/usr/local/openssh/sbin下,但 systemd 的ssh.service单元文件写死的路径是/usr/sbin/sshd。最常见做法是把新编译的二进制替换过去,替换前先备份。这一步我会放在后面验证章节详细说,因为替换时机和回滚方案是绑在一起的。

编译过程如果顺利,最后会看到make install输出的安装清单。如果中间报错,优先看是不是缺了libssl-dev或者zlib路径写错,这两个是最高频的问题。

3.3 一键升级脚本:把这套流程固化成 800 行内的自动化

手动编译过一次之后,第二个环境再升级时我就不想重复敲命令了。整条流程完全可以脚本化,做成只传一个目标版本号就能跑完的部署脚本。这类脚本在标题里叫“一键升级”,实际落地时无非是把下载、解压、编译、安装、备份、替换、重启服务这几步串起来。

脚本的核心骨架大致是这样:

#!/usr/bin/env bash set -e OPENSSH_VER="9.5p1" ZLIB_VER="1.3" BACKUP_DIR="/root/backup_ssh_$(date +%Y%m%d%H%M%S)" apt update apt install -y build-essential gcc make libssl-dev libpam0g-dev mkdir -p "$BACKUP_DIR" cp /usr/sbin/sshd "$BACKUP_DIR/sshd.old" 2>/dev/null || true cp /usr/bin/ssh "$BACKUP_DIR/ssh.old" 2>/dev/null || true cp /etc/ssh/sshd_config "$BACKUP_DIR/sshd_config.old" cd /usr/local/src wget -c http://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-$OPENSSH_VER.tar.gz wget -c http://zlib.net/zlib-$ZLIB_VER.tar.gz tar -zxvf openssh-$OPENSSH_VER.tar.gz tar -zxvf zlib-$ZLIB_VER.tar.gz cd /usr/local/src/zlib-$ZLIB_VER ./configure --prefix=/usr/local/zlib make -j$(nproc) make install cd /usr/local/src/openssh-$OPENSSH_VER ./configure --prefix=/usr/local/openssh \ --with-zlib=/usr/local/zlib \ --with-ssl-dir=/usr/lib/ssl \ --with-pam \ --with-privsep-path=/var/empty \ --sysconfdir=/etc/ssh make -j$(nproc) make install install -m 0755 /usr/local/openssh/sbin/sshd /usr/sbin/sshd install -m 0755 /usr/local/openssh/bin/ssh /usr/bin/ssh systemctl restart sshd ssh -V

set -e是这脚本的保险丝——任何一个步骤返回非零状态就立刻中止,不会在失败的前提下继续往下执行覆盖文件。备份环节的true是为了避免首次执行时旧版本二进制不存在导致cp报错而中断脚本。install命令替代单纯的cp,好处是能明确设置权限位,sshd文件的权限错乱是升级后服务起不来的隐蔽原因之一。

这个脚本我没有写到绝对健壮,比如没做下载文件的哈希校验、没判断网络超时重试,但对于内网批量环境,这个粒度已经够用。脚本执行完后,sshd -V的输出会确认版本是否切换成功。如果你要在我这个基础上继续完善,建议加上对旧配置的兼容性检查——sshd -t放在systemctl restart之前,配置有问题时提前暴露而不是重启后才发现连不上。

4. 避坑指南:升级 OpenSSH 最容易翻车的 5 个瞬间

4.1 升级到一半 SSH 断连,回不去了

现象:执行完make install后,当前 SSH 会话直接卡死或断开,再连接时提示拒绝连接。

原因:OpenSSH 的make install默认会替换/usr/local/openssh下的文件,但如果你的系统sshd是通过 systemd 启动的,而ssh.service单元文件里的ExecStart指向/usr/sbin/sshd,那么make install并没有真正让运行中的服务切换版本。更危险的是,如果你之后手动把新sshd复制到了/usr/sbin/sshd,但没有重启服务或者重启时配置不兼容,sshd 就会直接退出,当前所有 SSH 连接都会断开。

解决:升级前先确认自己当前会话是 SSH 进来的,并且保留一个带外控制渠道,比如云控制台 VNC 或者本地物理终端。然后在替换二进制之前,先执行sshd -t验证新配置。更稳妥的做法是写一个所谓的“自杀脚本”——把替换、重启、回滚三步打包,重启后在 30 秒内检测 SSH 端口是否还能连通,连不上就自动恢复旧版本。这套逻辑我第一次升级时没做,结果半夜差点跑到机房去按重启键。

4.2 PAM 认证失效,密码和密钥都登不进来

现象:新版本 sshd 能启动,ssh -V也显示 9.5p1,但登录时报Permission denied,密码确认没错,密钥也加载正常。

原因:编译时没带--with-pam参数,导致新sshd没有链接 PAM 库。Ubuntu 默认配置里UsePAM yes,而新 sshd 又没有 PAM 支持,认证链路直接断裂。另一个常见诱因是系统 PAM 配置里引用了旧版本 sshd 才有的模块路径,比如未安装libpam-modules导致的 missing module 报错。

解决:编译参数里补上--with-pam,并且在make install后确认ldd /usr/sbin/sshd能看到libpam.so相关输出。另外检查/etc/pam.d/sshd是否存在,缺失时可以从同版本系统复制一份,或者用sudo pam-auth-update重建。我一般在替换二进制之前就跑一次sshd -t,如果它输出关于 PAM 配置的fatal错误,说明问题在编译参数而不是配置文件。

4.3 版本号没变,还在 8.x 或 9.x 旧版

现象:跑完整个编译安装流程,执行ssh -V显示的依旧是旧版本号。

原因:ssh -V调用的是/usr/bin/ssh,而make install默认把二进制放到了/usr/local/openssh/bin。如果你用了--prefix参数,又没有手动替换/usr/bin/ssh和/usr/sbin/sshd,那么 shell 找到的还是旧版。另外有些发行版会把/usr/local/bin排进 PATH,但 ssh 装在/usr/local/openssh/bin,并不会自动被 PATH 包含。

解决:手动替换两份关键二进制,或者做一个软链接。我之前用的一直是:

install -m 0755 /usr/local/openssh/bin/ssh /usr/bin/ssh install -m 0755 /usr/local/openssh/sbin/sshd /usr/sbin/sshd

替换后记得验证。

4.4 sshd 启动失败但没报错信息

现象:systemctl restart sshd后服务显示 active,但端口22没有监听。systemctl status sshd输出里也没有明显错误。

原因:最常见的是新编译的 sshd 依赖的库文件路径不在系统默认搜索范围内。比如你用--with-zlib=/usr/local/zlib安装了 zlib,但/usr/local/zlib/lib不在ldconfig配置里,sshd启动时找不到libz.so.1。这类问题不会在编译时报错,而是运行时的“隐性缺库”。

解决:执行ldd /usr/sbin/sshd | grep zlib检查依赖是否能被解析。如果显示libz.so.1 => not found,把/usr/local/zlib/lib加到/etc/ld.so.conf.d/下新建的配置文件中,然后运行ldconfig。我的做法是:

echo "/usr/local/zlib/lib" > /etc/ld.so.conf.d/zlib.conf ldconfig

再重启 sshd 就能起来。这个坑很隐蔽,因为make阶段根本不会提示你,只有到运行阶段才爆发。

4.5 新版本跑起来了,但下次重启服务又变回旧版

现象:当前运行的新版 sshd 正常,但系统重启或systemctl restart sshd后,进程又变成旧版。

原因:systemd 单元的ExecStart=/usr/sbin/sshd是静态路径,而/usr/sbin/sshd如果被替换成软链接指向/usr/local/openssh/sbin/sshd,在某些场景下软链接会被替换掉,或者原文件被包管理器更新覆盖。Ubuntu 自动安全更新(unattended-upgrades)可能会覆盖/usr/bin/ssh,重新装回旧版本,导致 PATH 里的版本又回到原点。

解决:彻底一点的做法是生成自定义 systemd unit,直接指向新二进制路径。我在/etc/systemd/system/ssh.service里做覆盖:

[Service] ExecStart=/usr/local/openssh/sbin/sshd -D

然后systemctl daemon-reload && systemctl restart sshd。这样做会让版本切换变得显式,避免被下一次 apt 升级悄悄改回去。代价是你需要保证/usr/local/openssh里的二进制和配置一直完整,丢失的话 systemd 会直接起不来服务——这也是为什么备份目录不能删的原因。

5. 验证与回滚:升级后必须跑通的三步检查

在把版本切换正式交付给业务之前,我有一套固定检查流程,跑完了才算升级真正结束。

第一步是配置文件语法验证和环境依赖验证。新版本对旧配置文件的兼容性并不总是百分百,9.5p1 对某些废弃参数会直接拒绝启动,而不是警告后忽略。所以我每次都会执行:

/usr/local/openssh/sbin/sshd -t

如果输出Bad configuration options之类的行,说明配置里有已删除的指令,参照报错行号逐一清理再重测。-t只测配置不启服务,安全无副作用,可以反复跑。

第二步在替换systemctl restart sshd后,检查服务状态和端口监听情况:

systemctl status sshd ss -ltnp | grep :22

服务状态出现active (running)只是第一步,端口有监听才说明 sshd 真正在服务。接着用另一台机器尝试远程登录一次,而不仅是本地ssh localhost。本地连接成功但远端失败的情况很多,比如防火墙、hosts.allow 规则、云安全组没有放行新监听的 IP 段。

第三步验证版本和特性链路:

ssh -V /usr/local/openssh/sbin/sshd -V 2>&1

ssh -V输出里应该包含OpenSSH_9.5p1,sshd 的版本输出会附带编译参数信息,确认--with-pam、--with-zlib都已生效。再测试压缩链路是否正常启用,从客户端执行ssh -C user@server 'echo connected',如果连接建立且服务器端日志没有压缩相关报错,说明 zlib 1.3 与新版 OpenSSH 的链接没有问题。

最后说回滚方案。我把旧版二进制备份在/root/backup_ssh_*下,回滚时只需把备份的sshd和ssh复原、重启服务。这个方案能成立的前提是旧版配置没有被新版启动过程改写——所以升级前cp一份sshd_config也是必做步骤。现在每次升级 OpenSSH,我都强制自己走一遍“备份 — 编译 — 配置校验 — 重启 — 远端登录验证”的完整链路,缺一步都不收工。这套流程从第一次手忙脚乱到现在稳定复现,中间摔过的跤都在前面章节写进了避坑清单,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询