Ubuntu 22.04 LTS 自带的 OpenSSH 停留在 8.9p1,系统 OpenSSL 则是 3.0.2。2023 年底 Terrapin 攻击细节公开后,我评估了一圈自己维护的服务器,发现大部分机器都满足被攻击降级的条件,于是把线上环境统一从源码升级到了 OpenSSH 9.6p1,并同步完成了 OpenSSL 3.2.0 的适配。这篇文章不是简单翻译官方文档,而是我完整跑过一遍的操作记录,包含了编译参数、systemd 接入方式、排错过程,以及“apt 升级把新版本覆盖掉”这类隐蔽坑的应对。适合所有维护 Ubuntu 22.04 服务器的运维、安全和网络工程师参考,也适合想在测试环境里把 OpenSSH 升级到新版本的同学照着操作。
1. 为什么要升级:旧版隐患与 9.6p1 的新能力
1.1 旧版本不再安全的现实问题
很多朋友觉得 Ubuntu 22.04 是 LTS,系统自带的 OpenSSH 8.9p1 用着没毛病,就不愿意动它。但 2023 年底安全圈接连爆出的几个问题,改变了我的看法。
首先是 Terrapin 攻击(CVE-2023-48795)。这个攻击针对 SSH 传输协议本身,攻击者通过中间人方式,在握手阶段有选择地截断和修改扩展协商消息,能够降低连接的安全性。受影响的几乎覆盖了所有主流 SSH 实现,OpenSSH 9.5 及更早版本都在范围内。当时我检查了手头的 22.04 服务器,默认 sshd 配置里确实开着受影响的加密算法路径,如果不升级,风险是实打实存在的。
其次是 OpenSSH 自己的两个漏洞。CVE-2023-51384 涉及 sshd 的 PAM 处理逻辑,CVE-2023-51385 涉及 ssh-agent 加载 PKCS#11 模块时的问题。这两个漏洞的利用条件虽然不像网络上写攻击那样“一键打穿”,但生产环境里只要有利用可能,就该尽早堵上。
还有一个现实问题:Ubuntu 的 apt 源在安全更新时通常只会给 openssh 打补丁,版本号不会跟着升到 9.6p1。也就是说,即使你apt upgrade了一遍,ssh -V看到的仍然是 8.9p1。LTS 的保守策略能保证兼容性,但像ChannelTimeout、UnusedConnectionTimeout这类 9.6 带来的新配置能力,以及针对协议级攻击的缓解措施,不一定都会 backport 到旧版本。所以对于安全要求高、又需要新特性的环境,源码升级是更可控的方案。
1.2 9.6p1 带来了什么变化
OpenSSH 9.6p1 在 2023 年 12 月发布,几个比较重要的变化:
- 修复了 Terrapin 攻击。9.6p1 引入了对 SSH 传输协议更严格的密钥交换检查机制,客户端和服务端都会验证交换过程中的消息数量,中间人想悄悄截断扩展消息就困难了。
- 修复了 PAM 和 ssh-agent 的漏洞,对应 CVE-2023-51384 和 CVE-2023-51385。
- 新增了
ChannelTimeout配置项,可以针对不同类型的通道设置空闲超时,例如对 session、direct-tcpip、X11 等分别控制,比原来只能靠ClientAliveInterval粗暴兜底精细得多。 - 新增了
UnusedConnectionTimeout,可以对“连接建立后一直没有完成认证”的连接做处理,对防扫描也有帮助。
从运维角度讲,9.6.1p1 我最喜欢的就是ChannelTimeout,它能把那些挂着的僵尸 SSH 会话自动回收掉,省得我每次排查连接数都要一个个手动踢。后面第 5 节我会给出示例配置。
2. 升级前准备:摸清现状与留好后路
2.1 检查系统版本、OpenSSH 和 OpenSSL 现状
在动任何源码之前,先把环境摸清楚。我会习惯性地执行这几条命令:
cat /etc/os-release ssh -V openssl version -a systemctl status ssh --no-pager dpkg -l | grep -E "openssh|openssl"在 Ubuntu 22.04 上,正常情况下ssh -V会输出类似这样的内容:
OpenSSH_8.9p1 Ubuntu-3ubuntu0.10, OpenSSL 3.0.2 15 Mar 2022注意这里的 OpenSSL 3.0.2 是 Ubuntu 自带的,版本比较老。我自己遇到的情况是,OpenSSL 3.0.2 虽然也能直接编译 OpenSSH 9.6p1,但既然要折腾一次,干脆把 OpenSSL 也升级到 3.2.0,一次性把基础密码库补到当时的最新稳定版。
另外,记录一下服务相关信息:
systemctl cat ssh hostname id sshd ls -l /etc/ssh/ssh_host_* cat /etc/ssh/sshd_config | grep -E "HostKey|Subsystem|UsePAM|PermitRootLogin"这些信息在编译配置时会用到,尤其是sshd用户、主机密钥路径、PAM 配置路径,记录清楚能避免后面出错时手忙脚乱。
2.2 安装编译依赖并下载源码
编译 OpenSSH 和 OpenSSL 需要 gcc、make、perl,以及 zlib 和 PAM 的开发头文件。执行:
apt update apt install -y gcc make perl wget curl zlib1g-dev libpam0g-dev这里强调一句:不需要安装libssl-dev。因为我们的策略是独立编译 OpenSSL 3.2.0,如果同时装了系统自带的libssl-dev,configure 检测时可能被系统头文件干扰,后面出现版本不匹配的麻烦。保持环境干净,就只装这些基础构建依赖。
源码统一放在/usr/local/src下:
mkdir -p /usr/local/src cd /usr/local/src wget https://www.openssl.org/source/openssl-3.2.0.tar.gz wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.6p1.tar.gz如果你是离线环境,就把这两个包先下载好再传到服务器上。下载完成后我习惯性地用sha256sum对一下官方校验值,虽然大多数时候没问题,但安全升级这种事,多一步验证总不是坏事。
2.3 备份关键配置
升级 OpenSSH 最怕的是搞坏了当前连接,或者配置文件不兼容导致 sshd 起不来。所以备份一定要做。
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.202312 cp /etc/pam.d/sshd /etc/pam.d/sshd.bak tar czf /root/ssh-backup-$(date +%F).tar.gz /etc/ssh /etc/pam.d/sshd dpkg --get-selections | grep openssh > /root/openssh-packages.txt另外还要检查/var/lib/sshd目录是否存在,这个目录是 sshd 权限分离(privilege separation)的工作目录。Ubuntu 自带的 openssh-server 包会创建它,但如果你的系统比较精简,可能不存在。不存在的话执行:
mkdir -p /var/lib/sshd chown root:root /var/lib/sshd chmod 0755 /var/lib/sshd别小看这一步,编译出来的新版 sshd 默认使用的 privsep 路径不一定是这个,configure 时需要手动指定,后面会讲到。
3. OpenSSL 3.2.0 源码编译与适配
3.1 一个原则:不要随意覆盖系统 OpenSSL
这是整个升级过程中最重要的一条心得。OpenSSL 在 Ubuntu 系统里是被 Python、apt、curl、nginx 等大量组件依赖的基础库,直接替换/usr/bin/openssl或者/usr/lib/x86_64-linux-gnu/libcrypto.so.3,很容易把系统搞崩。我见过不少同学升级 OpenSSL 后,apt 直接报错、Python 起不来,最后只能从 live 环境里恢复。
所以正确做法是:把新版 OpenSSL 安装到一个独立目录,比如/usr/local/openssl,只给 OpenSSH 等自编译软件使用。这样系统原有的 OpenSSL 3.0.2 继续服务系统组件,新版 OpenSSL 3.2.0 服务自编译的 OpenSSH,互不干扰。
这意味着你升级完 OpenSSH 后,在终端里执行openssl version可能仍然是 3.0.2,这是正常的。想用新版命令行工具,就调用/usr/local/openssl/bin/openssl。理解这一点,就不会在后续排查时被误导。
3.2 编译安装 OpenSSL 3.2.0 的具体步骤
解压并进入目录:
cd /usr/local/src tar -xzf openssl-3.2.0.tar.gz cd openssl-3.2.0配置编译参数:
./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib参数说明:
--prefix=/usr/local/openssl:指定安装根目录,这是为了隔离系统 OpenSSL。--openssldir=/usr/local/openssl:OpenSSL 的运行时数据目录,包括证书路径、openssl.cnf 等。shared:生成动态链接库,后续 OpenSSH 编译时动态链接libcrypto.so.3。zlib:启用 zlib 压缩支持,需要系统里有 zlib1g-dev。
然后编译:
make -j"$(nproc)"-j后面的数字是并行编译线程数,我用$(nproc)自动获取 CPU 核数,编译速度会快很多。如果机器内存比较小,可以改成-j2,避免编译过程中 OOM。
编译完成后,强烈建议跑一遍测试:
make test这一步会花一些时间,但能验证 OpenSSL 在你这套硬件和系统环境下是否正常。生产环境升级,多花这几分钟很值。
确认测试通过后安装:
make install安装完成后,还要让系统动态链接器能找到新版 OpenSSL 的库文件:
echo "/usr/local/openssl/lib" > /etc/ld.so.conf.d/openssl-3.2.0.conf ldconfig验证一下动态库加载情况:
ldconfig -p | grep libcrypto /usr/local/openssl/bin/openssl version -a如果/usr/local/openssl/lib/libcrypto.so.3出现在列表里,且openssl version输出OpenSSL 3.2.0,说明安装成功。
3.3 常见适配坑:头文件与库文件版本不一致
很多人编译完 OpenSSL 后直接编译 OpenSSH,结果遇到一堆莫名其妙的报错。最常见的一类就是 OpenSSH 编译时使用的头文件和库文件版本不一致。
OpenSSL 的版本号在头文件opensslv.h里记录得明明白白:
grep -E "OPENSSL_VERSION_NUMBER|OPENSSL_VERSION_TEXT" /usr/local/openssl/include/openssl/opensslv.h正常情况下应该看到:
#define OPENSSL_VERSION_NUMBER 0x30200000L #define OPENSSL_VERSION_TEXT "OpenSSL 3.2.0 23 Nov 2023"而/usr/local/openssl/bin/openssl version输出的文本版本应该和这里的OPENSSL_VERSION_TEXT对应。如果头文件写的是 3.2.0,但系统实际加载的libcrypto.so.3来自系统自带的 3.0.2,OpenSSH 编译和运行时就会报openssl version mismatch。这个问题的详细排查我在第 6 节单独展开。
为了避免这个问题,在 OpenSSL 编译安装完成后,可以顺手清理一下可能残留的旧版本头文件干扰,确保/usr/local/openssl/include/openssl/opensslv.h是唯一的、且被 OpenSSH configure 能看到的那一份。
4. OpenSSH 9.6p1 源码编译与安装
4.1 configure 关键参数解析
回到/usr/local/src,解压 OpenSSH 源码:
cd /usr/local/src tar -xzf openssh-9.6p1.tar.gz cd openssh-9.6p1配置这一步是最关键的。我最终的 configure 命令是这样:
export CFLAGS="-I/usr/local/openssl/include" export LDFLAGS="-L/usr/local/openssl/lib -Wl,-rpath,/usr/local/openssl/lib" export PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig ./configure \ --prefix=/usr/local/openssh \ --sysconfdir=/etc/ssh \ --with-ssl-dir=/usr/local/openssl \ --with-zlib \ --with-pam \ --with-md5-passwords \ --with-privsep-path=/var/lib/sshd逐个解释为什么这么配:
--prefix=/usr/local/openssh:安装目录,避免和系统自带的/usr/bin、/usr/sbin下的 OpenSSH 文件混在一起。--sysconfdir=/etc/ssh:这是关键。指定后,新版 sshd 会继续读取/etc/ssh/sshd_config,同时也会使用/etc/ssh/ssh_host_rsa_key这些已有的主机密钥。如果你不指定,默认会去/usr/local/etc找配置,那就等于白干,还得重新生成密钥。--with-ssl-dir=/usr/local/openssl:告诉 OpenSSH 使用我们刚装好的 OpenSSL 3.2.0 头文件和库。--with-zlib:启用 zlib 压缩支持。--with-pam:启用 PAM。Ubuntu 的密码登录依赖 PAM,不加这个,密码登录基本会挂。如果是全新环境,还要保证/etc/pam.d/sshd存在。--with-md5-passwords:保留对旧密码哈希的兼容处理,虽然现在多数系统已经切到 sha512,但为了稳妥我还是加上。--with-privsep-path=/var/lib/sshd:权限分离目录,配合系统已有的/var/lib/sshd。
4.2 make 与安装
configure 顺利跑完后,终端会出现类似OpenSSL header version: 30200000、OpenSSL library version: 30200000的信息,说明头文件和库版本已经对齐。
接下来编译安装:
make -j"$(nproc)" make install安装完成后,检查一下产物:
ls -l /usr/local/openssh/sbin/sshd ls -l /usr/local/openssh/bin/ssh ls -l /usr/local/openssh/libexec/sftp-server然后用ldd确认新版 sshd 链接的是我们的 OpenSSL 3.2.0:
ldd /usr/local/openssh/sbin/sshd | grep -E "ssl|crypto"输出里如果看到/usr/local/openssl/lib/libcrypto.so.3和/usr/local/openssl/lib/libssl.so.3,就说明链接正确。如果看到/usr/lib/x86_64-linux-gnu/libcrypto.so.3,说明 configure 阶段的环境变量没生效,需要回头检查 LDFLAGS 和 PKG_CONFIG_PATH。
这里我踩过一个大坑:最初编译时我用了系统自带的libssl-dev头文件,结果 OpenSSH 编译出来的 sshd 链接了系统 OpenSSL 3.0.2,虽然ssh -V显示的是 9.6p1,但 OpenSSL 还是老版本。后来加了--with-ssl-dir和 LDFLAGS 的 rpath,重新编译才正确。这就是为什么我在前面强调要统一头文件、库文件和 rpath 三者。
4.3 接入 systemd 的两种方式
新版 sshd 在/usr/local/openssh/sbin/sshd,而 Ubuntu 22.04 的 ssh.service 默认执行的是/usr/sbin/sshd。如果不做处理,systemctl restart ssh重启的还是旧版本。
有两种接入方式:
方式一,用 systemd drop-in 覆盖 ExecStart。我比较推荐这种方式,因为不动系统原生的 openssh-server 包,回滚也容易。
mkdir -p /etc/systemd/system/ssh.service.d cat > /etc/systemd/system/ssh.service.d/override.conf <<'EOF' [Service] ExecStart= ExecStart=/usr/local/openssh/sbin/sshd -D $SSHD_OPTS EOF systemctl daemon-reload注意第二行那个空白的ExecStart=是必须的,它会把原来 service 里定义的 ExecStart 清空,然后才能用新的 ExecStart 覆盖。这是 systemd drop-in 的固定写法。
方式二,直接替换/usr/sbin/sshd:
cp /usr/sbin/sshd /usr/sbin/sshd.old.8.9p1 cp /usr/local/openssh/sbin/sshd /usr/sbin/sshd这种方式的好处是 ssh.service 完全不用改,systemd 还是按原来的方式启动。但坏处是系统里新老版本的 sshd 文件混在一起,时间长了容易搞不清自己跑的是哪个版本。而且后面apt upgrade openssh-server时,新安装的系统包可能会把它再覆盖回去。
我自己在线上一般用方式一。升级、回滚都清晰,出了问题把 drop-in 删掉、systemctl daemon-reload重启一下 ssh 服务就回到旧版了。
5. 配置调整、启动与验证
5.1 sshd_config 必要调整
新版 sshd 继续使用/etc/ssh/sshd_config,大部分原有配置可以直接沿用。但有一处必须调整:Subsystem sftp。
因为我编译的 OpenSSH 安装目录是/usr/local/openssh,它的 sftp-server 在/usr/local/openssh/libexec/sftp-server。而 Ubuntu 自带的 sftp-server 在/usr/lib/openssh/sftp-server。如果 sshd_config 里的 Subsystem 还指向旧路径,那么新版 sshd 启动后,SFTP 连接可能会失败。
打开/etc/ssh/sshd_config,找到这一行:
Subsystem sftp /usr/lib/openssh/sftp-server改成:
Subsystem sftp /usr/local/openssh/libexec/sftp-server如果你担心 AppArmor 对自定义路径的限制,也可以把新版 sftp-server 软链到原路径,然后让配置保持原样:
ln -sf /usr/local/openssh/libexec/sftp-server /usr/lib/openssh/sftp-serverUbuntu 桌面版默认启用了 AppArmor,服务器版不一定有。如果 SFTP 遇到权限问题,优先检查dmesg | grep apparmor或者/var/log/syslog。
改完配置后,先验证配置语法:
/usr/local/openssh/sbin/sshd -t -f /etc/ssh/sshd_config没有问题就刷新 systemd 并重启服务。我习惯在重启之前先开一个 tmux 或者多留一个 SSH 会话,防止万一启动失败导致自己把自己锁在门外。
systemctl daemon-reload systemctl restart ssh systemctl status ssh --no-pager -l确认sshd进程是新版的:
pgrep -a sshd看到类似/usr/local/openssh/sbin/sshd -D的进程,说明已经切换成功。也可以查进程的软链接:
ls -l /proc/$(pgrep -f "/usr/local/openssh/sbin/sshd" | head -1)/exe5.2 启动服务并验证登录
这一步我建议稳一点,分几个维度验证:
先确认监听端口:
ss -tlnp | grep :22然后新开一个终端,用密码登录测试:
ssh user@your-server-ip登录成功后,再验证密钥登录、sudo 权限、以及 SFTP:
sftp user@your-server-ip如果都能正常操作,说明 PAM、认证方式、Subsystem 都没问题。最后确认版本:
ssh -V这时的输出应该是:
OpenSSH_9.6p1, OpenSSL 3.2.0 ...这个输出里的 OpenSSL 版本,取决于你编译时用的$PATH里的openssl命令。如果显示的还是 3.0.2,别着急,那是因为默认openssl命令指向系统旧版,并不代表 sshd 用的是旧库,以ldd /usr/local/openssh/sbin/sshd的输出为准。
5.3 新特性验证与客户端兼容性
9.6p1 新增的ChannelTimeout可以这样测试。在/etc/ssh/sshd_config末尾加上:
ChannelTimeout session:10m保存后执行/usr/local/openssh/sbin/sshd -t,没有报错就说明配置项被正确识别。这个配置的效果是:SSH 会话通道如果连续 10 分钟没有数据传输,sshd 会自动断开连接,对清理遗留会话很有用。我甚至会在某些不敏感的内部机器上设置 30 分钟。
另外要注意一点:9.6 客户端默认使用的scp协议已经是 SFTP 协议,而不是旧的 SCP 协议。如果你有旧版本的服务端,可能会遇到scp报错,这时候可以在客户端手动指定scp -O强制走旧协议。不过本着安全优先的原则,能用新协议还是尽量用。
6. 常见问题与排查实录
6.1 启动失败:找不到 libcrypto.so.3
现象:执行/usr/local/openssh/sbin/sshd -t时提示:
/usr/local/openssh/sbin/sshd: error while loading shared libraries: libcrypto.so.3: cannot open shared object file: No such file or directory原因:新版 sshd 动态链接了/usr/local/openssl/lib/libcrypto.so.3,但系统动态链接器没有找到它。
排查:
ldd /usr/local/openssh/sbin/sshd | grep libcrypto ldconfig -p | grep libcrypto解决:
echo "/usr/local/openssl/lib" > /etc/ld.so.conf.d/openssl-3.2.0.conf ldconfig如果不想影响全局,也可以在 ssh.service 的 override.conf 里加环境变量:
[Service] Environment=LD_LIBRARY_PATH=/usr/local/openssl/lib但最稳妥的做法,还是用编译时的 LDFLAGS 加 rpath,这样 sshd 启动时不依赖外部环境变量。这也是我在前面 configure 里写-Wl,-rpath,/usr/local/openssl/lib的原因。
6.2 启动直接报 openssl version mismatch
现象:启动 sshd 时直接报:
openssl version mismatch. built against 30000020, you have 30200000这个报错的意思是:OpenSSH 在编译时,根据它看到的 OpenSSL 头文件生成了版本号0x30000020(对应 OpenSSL 3.0.2),而运行时加载的 OpenSSL 库版本是0x30200000(对应 OpenSSL 3.2.0),两者对不上,sshd 拒绝启动。
出现这个问题的常见场景是:OpenSSH configure 时,/usr/include/openssl里的头文件来自系统自带的libssl-dev,但链接时用的库却指向了/usr/local/openssl/lib,或者反过来。总之就是头文件和库版本不一致。
排查方法:
grep -E "OPENSSL_VERSION_NUMBER" /usr/local/openssl/include/openssl/opensslv.h grep -E "OPENSSL_VERSION_NUMBER" /usr/include/openssl/opensslv.h如果有两个头文件,且版本号不同,那 OpenSSH configure 检测到哪个全看编译环境的CPPFLAGS和路径优先级。
解决方法是把环境变量统一好:
export CFLAGS="-I/usr/local/openssl/include" export LDFLAGS="-L/usr/local/openssl/lib -Wl,-rpath,/usr/local/openssl/lib" export PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig ./configure --with-ssl-dir=/usr/local/openssl ...然后在make clean && make && make install。编译完成后留意 configure 输出里的 OpenSSL header version 和 library version,两个一致再继续。
6.3 PAM 认证失败或密码验证不通过
现象:公钥登录正常,但密码登录一直失败,日志里出现:
Failed password for invalid user ... PAM authentication failed原因可能有两个:一是编译 OpenSSH 时没有加--with-pam,导致 sshd 虽然允许密码认证,但没有调用 PAM 栈;二是/etc/pam.d/sshd文件缺失或者权限不对。
解决:
# 检查 PAM 文件是否存在 ls -l /etc/pam.d/sshd # 检查 sshd_config 里 UsePAM 是否开启 grep -i usepam /etc/ssh/sshd_config如果UsePAM yes且有配置文件,但还是失败,就重新编译,加上--with-pam并确保libpam0g-dev已安装。Ubuntu 22.04 默认的/etc/pam.d/sshd里有@include common-auth等配置,这些依赖 PAM 库,所以 PAM 这块不能省。
6.4 SFTP 连接异常
现象:SSH 能登录,但sftp卡住,或者客户端提示/usr/lib/openssh/sftp-server: no such file or directory之类。
原因:新版 sshd 启动后,执行了 sshd_config 里配置的 Subsystem 命令,但该路径下的 sftp-server 二进制不存在,或者版本不对。
解决:把/etc/ssh/sshd_config里 Subsystem 路径改成/usr/local/openssh/libexec/sftp-server,确认该文件存在且有执行权限。如果启用 AppArmor 后还有问题,检查 AppArmor 日志,并考虑用软链方式把 sftp-server 放回/usr/lib/openssh/sftp-server。
6.5 apt upgrade 把版本“打回原形”
现象:源码升级完,一切正常。过了一个月,发现ssh -V又变成了OpenSSH_8.9p1。
原因:系统后续apt upgrade时,openssh-server 包更新,会重新覆盖/usr/sbin/sshd、/usr/bin/ssh等文件。如果你采用了直接替换二进制的方式,就会被打回原形。
解决思路有两个:
- 如果你用的是 systemd drop-in 方式(推荐的方案),新版 sshd 在
/usr/local/openssh/sbin/sshd,apt 升级不会碰它,所以这个问题天然规避了。 - 如果你确实替换了系统路径的二进制,那就用
apt-mark hold把相关包锁住:
apt-mark hold openssh-server openssh-client openssh-sftp-server但锁包会带来安全更新缺失的问题,所以我更推荐前者,让系统包保持原始状态,自编译版本独立管理。
6.6 其他问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
sshd 启动失败,提示ss_host_keyPermission denied | 主机密钥权限不对 | chmod 600 /etc/ssh/ssh_host_*,属主改为 root |
| 客户端连接时提示 host key 变化 | 主机密钥路径被换或重新生成 | 确认 HostKey 配置是否指向原密钥 |
| 连不上,ss 端口里没有 sshd | 服务启动失败 | systemctl status ssh、journalctl -u ssh -n 50 |
Permission denied (publickey) | 公钥未配置或权限不对 | 检查 authorized_keys 权限为 600,用户目录 700 |
日志里反复出现error: Could not load host key: /etc/ssh/ssh_host_ed25519_key | 主机密钥缺失 | 执行ssh-keygen -A重新生成缺失密钥 |
7. 回滚方案与升级后加固建议
7.1 快速回滚到系统默认 OpenSSH
升级不是为了回滚,但任何时候都要留好后手。如果新版 sshd 出现严重问题需要回滚,操作并不复杂。
如果你用的是 systemd drop-in:
rm /etc/systemd/system/ssh.service.d/override.conf systemctl daemon-reload systemctl restart ssh这样 ssh.service 就会回到默认的/usr/sbin/sshd,也就是系统自带的 8.9p1。同时记得把/etc/ssh/sshd_config里改过的 Subsystem 路径恢复回来。
如果你是直接替换二进制:
cp /usr/sbin/sshd.old.8.9p1 /usr/sbin/sshd systemctl restart ssh更彻底一点,直接用 apt 重装系统包:
apt install --reinstall openssh-server openssh-client注意重装前要把/usr/sbin/sshd等文件恢复为系统包所管理的状态,否则 dpkg 会提示文件冲突。这也是我不太推荐直接替换/usr/sbin/sshd的原因。
7.2 升级后的安全加固清单
版本升级只是第一步,顺手做一轮安全加固才是完整流程。以下是我每次升级 OpenSSH 后都会检查的几项:
PermitRootLogin设置为prohibit-password,禁止 root 密码登录,只允许密钥。- 如果业务不需要密码登录,直接把
PasswordAuthentication no开起来,能挡掉大部分弱口令扫描。 - 用
ChannelTimeout session:30m这类配置减少长期挂着的空闲会话。 - 调整访问控制,比如只允许内网网段访问 22 端口,或用 fail2ban 做暴力破解防御。
- 定期重启测试:每次重启后确认 sshd 能自动拉起,别掉以轻心。
我个人在实际操作中的体会是,OpenSSH 源码升级的编译过程并不难,真正容易翻车的地方几乎全部集中在版本依赖和路径管理上。只要把 OpenSSL 的独立安装、头文件一致性、LDFLAGS rpath 这三件事搞清楚,剩下就是一个标准流程。还有一个细节想分享:升级完成后别急着把当前窗口关掉,先新开一个会话连续登录几次,再用journalctl -u ssh看看有没有异常,确认完全稳定之后再继续其他操作。这套动作我重复过很多次,目前还没有在线上环境翻过车。