简介:面向 Ubuntu 服务器管理员的 OpenSSH 与 zlib 升级资源包,主要解决系统自带 OpenSSH 版本陈旧、安全漏洞无法及时修复的问题。包内提供 9.5p1 版 OpenSSH 源码、1.3 版 zlib 源码以及一键升级脚本,适用于需要高效更新远程连接组件的运维场景,例如生产环境安全加固、等保合规整改或多台服务器统一升级,尤其适合缺乏深度编译经验的中级管理员。压缩包共有三个文件,包含两个源代码压缩包与一个自动执行脚本,整体容量约 3.18MB,结构紧凑,便于快速部署。目前已有 705 人学习或下载,说明该升级方案具备较强参考价值。脚本可自动串联解压、编译、安装和配置等关键环节,显著降低手工操作带来的出错概率;同时 zlib 库的更新还能优化 SSH 数据传输的压缩效率,帮助管理员在短暂维护窗口内完成安全升级,并保持系统稳定运行。 前阵子给一批生产服务器做安全基线检查,扫描报告出来后,OpenSSH 一栏赫然列着好几个中高危 CVE 编号,点开一看版本还是 8.9p1。我本来想等等 Ubuntu 官方源里的更新,结果发现无论 20.04 还是 22.04,apt 源里的 OpenSSH 版本都徘徊在 8.9/9.2 左右,很多安全修复迟迟没有跟进。与其干等,不如自己动手编译升级。这篇文章记录的是我整理的一套 Ubuntu 一键升级 OpenSSH 9.5p1 的方案,同时把 zlib-1.3 这个关键基础库一并升级,保证新老软件在同一个干净环境里编译通过。适合负责服务器运维、安全合规,或者自己管理云主机的朋友直接抄作业。
1. 升级前的环境准备与方案选型
1.1 为什么建议用源码编译,而不是直接 apt upgrade
很多人的第一反应是:OpenSSH 不是可以 apt upgrade 吗?但这里有个容易被忽略的事实:Ubuntu 的 Main 软件源倾向于“稳定性优先”,内部的 OpenSSH 版本在系统生命周期内基本不会做大版本跳跃,而是以安全补丁(backport)的方式持续修复。听起来好像没毛病,可实际操作中你会发现,某些 CVE 的修复补丁并不会第一时间同步到所有 LTS 版本,尤其是一些需要结构性调整的漏洞,比如协议层面的算法变更、密钥交换流程调整,光靠小补丁很难彻底解决。
而 OpenSSH 本身又是一个直接暴露在公网的服务,属于攻击面最大的系统组件之一。等官方源更新可能等上几个月,生产环境的安全扫描可不会等你。源码编译升级的好处是版本可控、修复及时、编译参数可定制;坏处是需要自己处理依赖和服务配置,操作失误可能导致 sshd 起不来。所以我才把整个过程做成了一个带备份和自动回滚逻辑的一键脚本,既保留源码编译的优势,又降低操作风险。
1.2 为什么选中 openssh-9.5p1 和 zlib-1.3 这两个版本
选择 OpenSSH 9.5p1,主要是基于安全和兼容性的平衡点。9.5p1 是 2023 年 10 月发布的便携版,修复了包括 CVE-2023-51385(ssh 命令中的操作系统命令注入风险)在内的多个安全问题,同时引入了 mack-sntrup761x25519-sha512 等新密钥交换算法,还会在连接过程中检查口令认证是否被服务端禁用,从协议层面规避弱算法被强行降级的问题。这个版本对系统自带的 OpenSSL 3.x 支持也很好,Ubuntu 20.04/22.04 上都能顺利编译。
zlib 则相对容易被忽略,但它是 OpenSSH 压缩传输的基础依赖。zlib-1.3 在 2023 年 8 月发布,修复了 CVE-2023-45853(zlib 在 minigzip 等场景下的内存越界问题),虽然影响程度没有 OpenSSH 本身那么直接,但作为基础库,搭建一套自洽的新版本环境会让后续排查更省心。在脚本里我会把 zlib 编译安装到 /usr/local 前缀下,再用 --with-zlib=/usr/local 让 OpenSSH 链接新库,既不影响系统原有库,又能确保最终二进制依赖的是新版本。
1.3 一键脚本的整体设计思路
设计这套脚本时,我给自己定了三个原则:可回滚、可重复执行、出错能定位。可回滚是指执行前自动备份旧的 ssh/sshd 二进制以及 /etc/ssh 整个配置目录,安装过程中任何一步校验失败,都能把旧文件拷回去恢复服务。可重复执行是指脚本中所有下载都带断点续传,编译前也会清理临时目录,这样重跑一遍不会产生冲突。出错能定位则是靠日志文件,脚本执行时的全部输出都会同时写入升级目录下的 upgrade.log,方便事后排查。
脚本整体分四个阶段:环境检查与依赖安装、zlib 编译、OpenSSH 编译、服务替换与验证。这样的分段方式让我在调试时可以逐步确认问题出在哪,而不是一把梭。接下来我会把完整脚本放出来,再逐段解释核心逻辑。
2. 一键升级脚本:从依赖安装到二进制替换
2.1 完整脚本全文
以下脚本我在 Ubuntu 20.04 和 22.04 上都跑过,默认安装在 x86_64 架构的服务器上。如果你的机器是 ARM 架构,理论也能编译,但建议先在测试机上验证一遍。
#!/bin/bash #============================================================= # Ubuntu 一键升级 OpenSSH 9.5p1 + zlib 1.3 # 适用环境:Ubuntu 20.04 / 22.04 / 24.04(x86_64 / aarch64) # 使用方式:sudo bash upgrade_openssh.sh #============================================================= set -Eeuo pipefail VERSION_SSH="9.5p1" VERSION_ZLIB="1.3" BACKUP_DIR="/root/backup_openssh_$(date +%Y%m%d_%H%M%S)" mkdir -p "${BACKUP_DIR}" LOG_FILE="${BACKUP_DIR}/upgrade.log" exec > >(tee -a "${LOG_FILE}") 2>&1 echo "===== OpenSSH ${VERSION_SSH} + zlib ${VERSION_ZLIB} 升级开始 =====" if [[ $EUID -ne 0 ]]; then echo "[ERROR] 请使用 root 用户执行脚本。" exit 1 fi # 1. 备份现有文件 echo "[1/9] 备份现有的 ssh / sshd 二进制与配置目录..." if [ -f /usr/sbin/sshd ]; then cp -a /usr/sbin/sshd "${BACKUP_DIR}/sshd.bak"; fi if [ -f /usr/bin/ssh ]; then cp -a /usr/bin/ssh "${BACKUP_DIR}/ssh.bak"; fi cp -a /etc/ssh "${BACKUP_DIR}/ssh_config_dir.bak" # 2. 安装编译依赖 echo "[2/9] 安装编译依赖..." export DEBIAN_FRONTEND=noninteractive apt-get update -y apt-get install -y build-essential wget gcc make libssl-dev libpam0g-dev \ libsystemd-dev pkg-config # 3. 下载并编译 zlib-1.3 echo "[3/9] 编译安装 zlib-${VERSION_ZLIB}..." cd /tmp rm -rf "zlib-${VERSION_ZLIB}" "zlib-${VERSION_ZLIB}.tar.gz" wget -4 -c --tries=3 "https://zlib.net/zlib-${VERSION_ZLIB}.tar.gz" \ -O "zlib-${VERSION_ZLIB}.tar.gz" tar xzf "zlib-${VERSION_ZLIB}.tar.gz" cd "zlib-${VERSION_ZLIB}" ./configure --prefix=/usr/local make -j"$(nproc)" make install echo "/usr/local/lib" > /etc/ld.so.conf.d/zlib-1.3.conf ldconfig # 4. 下载并编译 OpenSSH 9.5p1 echo "[4/9] 编译安装 OpenSSH-${VERSION_SSH}..." cd /tmp rm -rf "openssh-${VERSION_SSH}" "openssh-${VERSION_SSH}.tar.gz" wget -4 -c --tries=3 "https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION_SSH}.tar.gz" \ -O "openssh-${VERSION_SSH}.tar.gz" tar xzf "openssh-${VERSION_SSH}.tar.gz" cd "openssh-${VERSION_SSH}" ./configure \ --prefix=/usr \ --sysconfdir=/etc/ssh \ --with-zlib=/usr/local \ --with-ssl-dir=/usr \ --with-pam \ --with-systemd \ --with-privsep-path=/var/run/sshd \ --with-md5-passwords make -j"$(nproc)" # 5. 替换二进制 echo "[5/9] 安装新编译产物到系统路径..." install -d -o root -g root -m 0755 /var/run/sshd make install # 6. 测试新 sshd 配置 echo "[6/9] 校验新 sshd 配置..." if ! /usr/sbin/sshd -t; then echo "[ERROR] sshd -t 校验失败,正在自动回滚。" if [ -f "${BACKUP_DIR}/sshd.bak" ]; then cp -a "${BACKUP_DIR}/sshd.bak" /usr/sbin/sshd fi if [ -f "${BACKUP_DIR}/ssh.bak" ]; then cp -a "${BACKUP_DIR}/ssh.bak" /usr/bin/ssh fi systemctl restart ssh exit 1 fi # 7. 重启服务 echo "[7/9] 重启 ssh 服务..." systemctl restart ssh sleep 2 # 8. 输出验证信息 echo "[8/9] 升级后版本信息:" ssh -V /usr/sbin/sshd -V 2>&1 || true ldd /usr/sbin/sshd | grep -E "libz|libssl" || true # 9. 完成 echo "[9/9] 升级流程结束,备份目录:${BACKUP_DIR}" echo "===== 全部完成,请新开一个终端确认可以正常登录再关闭当前会话 ====="2.2 几个必须理解的编译参数
这套脚本里最值得解释的就是 configure 阶段的那几个参数,缺一个都可能在重启后连不上服务器。
--with-pam 是重中之重。Ubuntu 的登录认证默认走 PAM 体系,密码验证、sudo 联动都依赖 /etc/pam.d/sshd 这个配置。如果编译时不加这个参数,新 sshd 将不读取 PAM 配置,密码登录基本会失效。同样道理,configure 阶段还需要系统里有 libpam0g-dev,否则会直接报错。
--with-systemd 也很关键。Ubuntu 的 ssh 服务由 systemd 管理,加上这个参数后,sshd 会在启动完成时向 systemd 发送就绪通知,systemctl restart ssh 就不会出现“服务已启动但实际监听失败”这种错觉。
--with-privsep-path=/var/run/sshd 对应的是 sshd 权限分离功能的工作目录。我见过不少编译教程漏掉这个参数,结果 sshd 启动时报 “Missing privilege separation directory” 的错误。Ubuntu 原本就使用 /var/run/sshd 这个路径,脚本里提前创建目录并保持 0755 权限,能避免这类问题。
2.3 为什么把 zlib 装到 /usr/local,而不是直接替换系统库
这里必须提醒一下:千万别图省事把新编译的 zlib 直接覆盖到 /usr/lib/x86_64-linux-gnu/ 下面。系统里很多核心工具(如 apt、curl 的某些组件)都依赖旧版 libz.so.1,粗暴替换可能导致整个系统的动态库链接错乱。更安全的做法是装到 /usr/local/lib,然后通过 ld.so.conf.d 下的配置文件让系统优先加载新库。
脚本链接 OpenSSH 时用 --with-zlib=/usr/local,编译出来的 sshd 就会记录对 /usr/local/lib/libz.so.1 的依赖。同时脚本会执行 ldconfig 刷新动态库缓存,这样运行时二进制才能正确找到新库。你可以用 ldd /usr/sbin/sshd 验证,正常情况下输出里会出现 “libz.so.1 => /usr/local/lib/libz.so.1” 这一行。
3. 实操验证与常见问题排查
3.1 执行升级前的三项准备工作
在生产环境跑一键脚本之前,强烈建议先确认三件事。
第一,确保你拥有 root 权限或者 sudo 权限,且当前登录会话是通过 SSH 密钥认证而不是纯密码认证。因为升级过程中要重启 sshd,一旦新配置有问题导致密码登录失效,至少还能用密钥从另一条路径登录服务器。没有密钥的用户,建议先找一个不依赖该服务器的带外管理终端,比如云控制台的 VNC。
第二,提前确认防火墙规则。Ubuntu 上如果启用了 ufw,需要确保 22 端口或者你自定义的 ssh 端口是放行的。脚本里虽然不会改防火墙规则,但重启 sshd 的瞬间,旧的连接可能被断开,如果防火墙拦截了新端口,再想登回来就很被动。
第三,下载源码包时尽量保证网络稳定。脚本里的 wget 用了 -4 参数强制走 IPv4,并且加了 --tries=3 重试。如果下载中断也没关系,脚本开头会清理临时文件重新下载。不过对于处于网络隔离环境的内网机器,最好提前把两个 tar 包放到 /root 目录,再把脚本里的下载命令改成直接使用本地包。
3.2 升级后的完整验证清单
脚本执行到最后会输出 ssh -V 和 sshd -V,但这只是第一步。我更建议手动跑一遍下面的验证清单:
# 查看版本信息 ssh -V sshd -V # 检查监听端口 ss -tlnp | grep :22 # 检查新版本是否链接新 zlib ldd /usr/sbin/sshd | grep zlib # 检查服务状态 systemctl status ssh --no-pager光看版本号和监听还不够,真正重要的是“另开一个新会话测试登录”。我习惯在升级后用另一个终端重新 ssh 登录一次,确认验证方式、终端交互、sudo 操作都正常,然后再关闭原先的连接。这样即使当前会话被误杀,你也不会被困在服务器外面。
3.3 升级过程中最容易踩的 5 个坑
我在折腾这套脚本的时候,几乎把每个坑都踩了一遍,整理成下表方便你对照排查。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| sshd -t 校验失败,提示 bad permissions | /etc/ssh/ 下某些密钥文件权限过宽 | 执行 chmod 600 /etc/ssh/ssh_host_*_key,公钥保持 644 |
| 重启后密码登录失败 | 编译时少了 --with-pam,或 pam.d 配置损坏 | 确认 configure 参数里有 --with-pam,并保留原 /etc/pam.d/sshd |
| sshd 启动报 Missing privilege separation directory | --with-privsep-path 指向了不存在的目录 | 执行 install -d -o root -g root -m 0755 /var/run/sshd |
| 新 sshd 链接到旧 libz.so.1 | 没有执行 ldconfig 或 ld 配置缺失 | 写入 /etc/ld.so.conf.d/zlib-1.3.conf 后执行 ldconfig |
| apt 源下载慢或失败 | 官方源在国外,内网访问不稳定 | 提前下载 tar 包到本地,或配置内网镜像源 |
3.4 一次 sshd 启动失败的排查记录
这里分享一次真实排查经历。有一台机器执行升级后,ssh -V 已经显示 9.5p1,但 systemctl restart ssh 报失败。我第一反应是配置文件语法问题,于是单独跑 /usr/sbin/sshd -t,结果它输出 “Missing privilege separation directory: /var/run/sshd”。
原因是我在一台刚初始化的服务器上执行脚本时,/var/run/sshd 并没有默认创建,而旧版 sshd 在某些条件下会自动处理,新版对权限分离目录的要求更严格。排查之后我在脚本里加入了 install -d 创建目录的步骤,顺手把目录权限固定为 root:root 0755,问题解决。这也说明一个问题排查顺序:先看 sshd -t 的明确报错,再用 systemctl status 看 systemd 层面的日志,最后才需要查 /var/log/auth.log。按这个顺序走,基本能快速缩小范围。
4. 回滚方案与升级后的加固建议
4.1 出现问题如何一键回滚
脚本本身已经带了一部分自动回滚逻辑,即 sshd -t 校验失败时自动恢复备份。但如果升级完成后某个配置项不兼容导致服务无法启动,就需要手动回滚备份目录里的二进制。
回滚操作很简单,找到脚本生成的备份目录,执行如下命令:
BACKUP_DIR=$(ls -d /root/backup_openssh_* | tail -n 1) systemctl stop ssh cp -a "${BACKUP_DIR}/sshd.bak" /usr/sbin/sshd cp -a "${BACKUP_DIR}/ssh.bak" /usr/bin/ssh systemctl start ssh ssh -V如果你在升级后又手动修改过配置,回滚前最好把当前 /etc/ssh 目录再存一份备份,方便对比差异。不要急着删除 backup_openssh_* 目录,我通常会把最近三次的备份都留下,确认线上稳定一周后再清理旧备份。
4.2 升级后的安全加固建议
OpenSSH 升级到 9.5p1 只是第一步,接下来顺手做一轮基础的 ssh 加固会让收益翻倍。
先检查 sshd_config 里的几个关键项:PermitRootLogin 如果不需要 root 远程登录,建议改成 prohibit-password;PasswordAuthentication 在条件允许时关闭,优先使用密钥登录;X11Forwarding 默认建议设为 no,减少不必要的转发通道。协议层面可以在 /etc/ssh/sshd_config 中追加 KexAlgorithms、Ciphers、MACs 的允许列表,把弱算法禁掉,配合 9.5 版本默认启用的新算法会很稳妥。
还有一个小建议:把 sshd_config 的日志级别调成 VERBOSE,或者确保 LogLevel 不低于 INFO,这样出现异常登录行为时,/var/log/auth.log 里能看到更详细的指纹、来源 IP 和认证方式信息。升级到这个版本后,日志里对公钥算法和密钥校验的打印也完整很多,对日常巡检非常友好。
完整跑完这套升级,再到新终端验证登录成功的那一刻,心里的石头才算落下。我在实际使用中的体会是:OpenSSH 源码升级这件事,最大的风险从来不是编译过程,而是升级后对系统认证体系的影响。只要把 PAM、systemd、权限分离目录这三个点照顾到位,再把备份和回滚做成默认动作,这套流程完全可以复制到几十台机器上批量执行。最后再分享一个小技巧:升级完成后,把 backup_openssh_* 目录里的 upgrade.log 收集起来,下一次做安全审计时直接对照日志就能说明升级时间和版本来源,比口头解释可靠得多。
本文还有配套的精品资源,点击获取