尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ubuntu 20.04源码升级OpenSSH至9.9p1:安全加固与避坑指南

Ubuntu 20.04源码升级OpenSSH至9.9p1:安全加固与避坑指南 很多跑在 Ubuntu 20.04 上的服务器OpenSSH 还停留在系统自带的 8.2p1。这个版本是 2020 年初的产物放在当年不算落后但放到现在这个几乎每年都爆几个高危漏洞的环境里多少有点让人不踏实ssh-agent 远程代码执行CVE-2023-38408、ProxyCommand 配置注入CVE-2023-51385、还有 8.7 及以下版本都受影响的提权漏洞CVE-2021-41617每一个都实实在在地写在公网服务器的攻击面上。更要命的是很多人的 Ubuntu 20.04 不是一台“干净”的服务器而是装过 ROS、折腾过显卡驱动、跑过虚拟机的开发机或实验室主机。这种机器上 apt 依赖经常一团乱某次apt upgrade顺手把 openssh-server、libssl、libc 一起动了一遍重启之后发现 SSH 再也连不上了——这种事我见过不止一次。与其把命运交给不自知的依赖变动不如主动做一次可控的 OpenSSH 源码升级把版本握在自己手里。这篇文章记录的是我在 Ubuntu 20.04 上把 OpenSSH 从 8.2p1 源码升级到 9.9p1 的完整过程。内容包括为什么要升、升级前怎么备份、configure 参数怎么选、远程操作时如何避免把自己锁在外面以及升级后最容易翻车的几个坑和对应的自救方案。不管你是刚接触 Linux 的新手还是被安全通告追着跑的运维老手这套流程都能直接照着用。1. 为什么 Ubuntu 20.04 自带的 OpenSSH 8.2p1 值得大动干戈1.1 安全公告和仓库补丁之间的时间差先别急着开喷。Ubuntu 20.04 是有长期支持的Canonical 确实会通过 apt 向 8.2p1 推送安全补丁apt update apt upgrade之后你拿到的仍然是一个被打了补丁的 8.2p1。如果你只在乎“能联网、能登录”仓库版其实够用。问题出在两个地方。第一补丁推送的节奏和策略你控制不了。官方源修漏洞有它的优先级和排期有些高危 CVE 从公开到进仓库要等不少时间中间这段窗口期你的 SSH 服务就暴露在风险里。第二如果你用的不是标准 Ubuntu 官方源而是内网离线镜像、第三方定制镜像、或者基于 20.04 的衍生发行版补丁滞后的问题会更严重这时候“等仓库修”远不如“源码升级到最新版”来得踏实。另外一个常被忽略的动机是新特性。OpenSSH 8.2 之后引入了不少实用能力比如基于 FIDO2/U2F 硬件的sk-ssh-ed25519密钥、更现代的密钥交换算法、更严格的默认加密策略。仓库版 8.2p1 对这些支持是不完整的你想用新特性就得自己升级。所以我在多个场景下都选择了源码编译而不是干等仓库。1.2 升级路线怎么选源码编译、PPA 还是 backports决定升级后路线主要有三条我分别试过直接说结论。升级方案优点缺点apt 仓库安全补丁最稳和 dpkg 状态一致版本永远是 8.2p1新功能没有第三方 PPA 源安装快一条命令搞定第三方包可信度存疑依赖冲突时很难排查backports 仓库比 PPA 正式一点Ubuntu 20.04 的 openssh backports 并不常驻基本指望不上源码编译版本完全可控可定制编译参数和 dpkg 包管理状态脱节后续要自己维护升级我的建议很直接生产环境能扛住就继续用官方源的安全补丁别乱动但如果你明确需要新版本、新特性或者被迫使用滞后镜像那就走源码编译。make install之后 dpkg 里关于 openssh-server 的记录确实会失真这是源码升级固有的代价提前想清楚后面就不会慌。2. 动工之前的巡检、备份与依赖准备2.1 先摸清版本和系统状态任何升级操作第一步永远是搞清楚自己站在哪块地上。用下面这几条命令把家底盘一遍lsb_release -a uname -m ssh -V dpkg -l | grep -E openssh|libssl ss -lntp | grep :22重点关注几个信息系统是不是干净的 20.04、CPU 架构是不是 x86_64、当前 OpenSSH 版本、以及 22 端口监听的到底是哪个 sshd 二进制。很多坑都是在这步提前发现的比如我之前遇到过一台机器上同时装了/usr/sbin/sshd和/usr/local/sbin/sshd两份 sshdsystemd 启动的是系统版但你自己测试时用的又是另一个两边版本不一致升级后服务监听的还是老进程排查起来非常迷惑。还要顺手检查一下/etc/ssh下有没有主机密钥文件sudo ls -l /etc/ssh/ssh_host_*主机密钥是 SSH 服务器的“身份证”正常情况下应该存在且权限正确私钥 600公钥 644。如果这步发现密钥缺失先别继续处理好再升。2.2 备份三样东西配置、二进制、deb 包源码编译升级最怕的不是编译失败而是升级到一半发现自己回不去了。所以我在动手前一定会做三次备份。第一个是配置文件/etc/ssh整个目录拷走外加 PAM 配置sudo mkdir -p /root/openssh-backup/config sudo cp -a /etc/ssh /root/openssh-backup/config/ sudo cp /etc/pam.d/sshd /root/openssh-backup/config/第二个是当前正在用的二进制避免make install覆盖后连旧版都找不回来sudo mkdir -p /root/openssh-backup/bin /root/openssh-backup/sbin sudo cp /usr/sbin/sshd /root/openssh-backup/sbin/ sudo cp /usr/bin/ssh /usr/bin/scp /usr/bin/sftp /usr/bin/ssh-keygen /usr/bin/ssh-agent /usr/bin/ssh-copy-id /root/openssh-backup/bin/第三个是通过 apt 把当前版本对应的 deb 包下载到本地这是最快的回滚手段后面会专门讲怎么用cd /root/openssh-backup sudo apt-get download openssh-server openssh-client openssh-sftp-server别嫌麻烦。这三步加起来不到两分钟但它保证哪怕你把服务搞到彻底起不来也有明确的后路。2.3 编译依赖与可选功能组件OpenSSH 源码编译需要的依赖不多但少了任何一个都会导致编译出来的东西不好用。我的标准依赖安装命令是sudo apt update sudo apt install -y build-essential zlib1g-dev libssl-dev libpam0g-dev libsystemd-dev重点解释一下为什么这些一个都不能少。build-essential提供 gcc 和 make属于基本盘zlib1g-dev是压缩库头文件OpenSSH 压缩支持依赖它libssl-dev提供 OpenSSL 开发文件密钥交换、加密算法全都要靠它libpam0g-dev是为了编译时启用 PAM 支持没有它就算你在sshd_config里写了UsePAM yessshd 启动时也会报 PAM 初始化失败导致密码登录不可用libsystemd-dev则是让 sshd 能和 systemd 的Typenotify配合缺少它的话 systemd 单元可能一直等到超时。如果以后想用 FIDO2 安全密钥登录可以额外装libfido2-dev再编译这一步可选不装也不影响常规使用。3. 源码编译升级 OpenSSH 9.9p1 的分步实操3.1 下载源码并校验完整性去官网确认最新稳定版本号后把源码包下载到/usr/local/srccd /usr/local/src sudo wget -c https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.9p1.tar.gz sudo wget -c https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.9p1.tar.gz.asc官网同时提供了.asc签名文件理论上可以用 GPG 验签。但我实操中更常用的还是比对 SHA256 校验和因为更直观sha256sum openssh-9.9p1.tar.gz把输出的哈希值和官网页面上的 SHA256 逐个字母对一遍。这一步别省你接下来要把这个二进制放到 22 端口上给所有人连源码被动手脚的后果太严重了。3.2 configure 参数怎么写给后续运维省事很多人编译 OpenSSH 时直接./configure一把梭装到/usr/local下。这样不是不行但会给 systemd 单元适配、PATH 查找、AppArmor 策略带来一堆额外麻烦。我的做法是让新版本直接顶替系统路径cd /usr/local/src/openssh-9.9p1 ./configure \ --prefix/usr \ --sysconfdir/etc/ssh \ --with-ssl-dir/usr \ --with-zlib/usr \ --with-pam \ --with-privsep-path/var/run/sshd \ --with-privsep-usersshd这里的每个参数都有讲究。--prefix/usr是最关键的一个它保证二进制安装到/usr/bin、/usr/sbin和 Ubuntu 自带版的路径完全一致systemd 单元里写的ExecStart/usr/sbin/sshd -D不用改PATH 里也直接能找到。--sysconfdir/etc/ssh让新版本继续读你熟悉的配置目录。--with-pam必须加Ubuntu 的密码认证、sudo 联动都依赖 PAM。--with-privsep-path指定特权分离目录Ubuntu 上就是/var/run/sshd也就是/run/sshd。3.3 编译安装与 systemd 单元适配configure 顺利通过后编译安装一气呵成make -j$(nproc) sudo make install-j$(nproc)让编译用满所有 CPU 核心二十秒左右就能编完。make install执行完之后先做一次版本确认ssh -V /usr/sbin/sshd -V如果输出是OpenSSH_9.9p1之类的字样说明二进制已经替换成功。接着检查 systemd 单元里实际调用的路径是否和新二进制一致systemctl cat sshUbuntu 20.04 的ssh.service里ExecStart写的是/usr/sbin/sshd -D $SSHD_OPTS因为我们用了--prefix/usr所以这条路径正好指向新编译的 sshd无需修改。如果你当初真的装了/usr/local路径这里就必须用systemctl edit ssh加一条 override把 ExecStart 指到新 sshd否则重启服务后跑的还是旧进程。3.4 sshd -t 校验与重启技巧任何一次配置或二进制替换之后重启服务之前必须跑语法检查sudo /usr/sbin/sshd -t有输出说明配置有问题没输出就是通过。这条命令是 SSH 升级的生命线我见过太多人跳过它直接 restart结果配置文件里一个无关紧要的旧指令让 sshd 起不来把自己锁在机器外面。检查通过后再重启服务sudo systemctl restart ssh sudo systemctl status ssh --no-pager这里有一个远程操作时非常重要的建议如果服务器离你很远请务必在tmux或screen里执行重启命令。这样就算网络瞬断命令也会在 session 里继续跑完重连之后还能看到完整输出。后面我专门有一节讲这个事为什么值得认真对待。4. 远程升级最容易翻车的三个坑与自救方法4.1 sshd 起不来动态库和 privilege separation 目录源码编译升级后第一个高频翻车点是systemctl restart ssh之后服务直接 failed。用journalctl -u ssh -n 50看日志经常能看到类似这样的报错/usr/sbin/sshd: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory原因很清晰编译时链接的 OpenSSL 库和运行时系统里实际存在的库对不上。Ubuntu 20.04 本来默认是 OpenSSL 1.1.1对应libcrypto.so.1.1但如果你的机器之前装过其他软件栈引入了 OpenSSL 3.x系统里只有libcrypto.so.3新 sshd 启动时就会找不到旧库。排查命令是ldd /usr/sbin/sshd | grep crypto如果显示缺libcrypto.so.1.1先尝试安装sudo apt install -y libssl1.1如果还是找不着那就得搞清楚你编译时用的头文件到底是哪个 OpenSSL 版本必要时卸载混乱的 OpenSSL 3 开发包重新 configure 再编一次。另一个“起不来”的隐蔽原因是/run/sshd目录丢了。权限分离目录缺失时sshd 会直接拒绝启动日志里写着Missing privilege separation directory: /run/sshd。Ubuntu 上这个目录一般在系统启动时自动创建但如果你之前清理过/run下的临时文件或者做过奇怪的加固脚本它可能就不在了。解决办法很简单sudo mkdir -p /run/sshd sudo chmod 0755 /run/sshd4.2 新版本把旧配置拒之门外第二个常见坑是sshd -t直接报错指向/etc/ssh/sshd_config里某一行。OpenSSH 9.x 对老配置的兼容性总体不错但有些旧指令确实被改名或收紧了。最典型的是ChallengeResponseAuthentication这个老指令从 8.7 开始就变成了KbdInteractiveAuthentication的废弃别名继续写在配置里虽然能通过检查但新版日志会有警告某些更老的指令则是直接删除比如 DSA 算法相关的HostKeyAlgorithms ssh-dss早在 7.0 就移除了任何 8.2 时代的配置里还留着它都会导致启动失败。另一个让很多人意外的变化是默认算法策略收紧。9.x 默认不再接受ssh-rsaSHA-1签名如果你手上还有老的自动化脚本或较旧版本的客户端在用 RSA 密钥连接升级后可能突然连不上。处理办法有两种一是升级客户端到支持 SHA-2 的版本二是在 sshd_config 里临时放开PubkeyAcceptedAlgorithms ssh-rsa注意这个配置只是过渡方案能让老客户端继续连但 SHA-1 签名在安全层面已经不值得信任长期用等于把升级意义打了折扣。改完配置一定要再跑sudo /usr/sbin/sshd -t然后重启服务。4.3 手滑误杀会话延迟重启和 tmux 兜底远程升级最刺激的一幕是你在 sshd 上执行systemctl restart ssh然后看着终端卡住不动像死机了一样。其实没那么容易死Ubuntu 20.04 的ssh.service默认KillModeprocess重启只杀掉主监听进程已经建立的会话子进程会保留当前连接理论上不会断。但有前提条件——如果别人的加固脚本、自动化工具把 KillMode 改成了 control-group或者你手快执行了systemctl stop ssh没接着 start那当前会话说没就没了。不要赌这个。我给自己定了个规矩远程改 ssh 相关东西一律在 tmux 里操作并配合延迟重启。tmux new -s upgrade-ssh # 在 tmux 里执行编译、安装、校验 sudo bash -c sleep 3; systemctl restart ssh延迟几秒重启的意义在于命令执行后你还有一点时间观察终端状态就算连接真的闪断tmux 里的命令也已经跑完了重连后tmux attach -t upgrade-ssh能看完整日志。万一重启后服务异常只要当前会话还在立刻执行系统里备好的回滚脚本就能救回来。5. 升级完成后的验证清单与快速回滚预案5.1 一线功能体检服务重启不代表升级成功我每次都要按下面的清单做一遍验证检查项命令预期结果版本号ssh -VOpenSSH_9.9p1配置语法/usr/sbin/sshd -t无输出服务状态systemctl is-active sshactive端口监听ss -lntp | grep :22sshd 监听 0.0.0.0:22主机密钥权限ls -l /etc/ssh/ssh_host_*私钥 600公钥 644本地登录ssh localhost能登录文件传输sftp localhost能列出目录安全日志journalctl -u ssh -n 20无异常报错另外值得关注的是 AppArmor。Ubuntu 上 sshd 有默认 AppArmor 配置如果升级后出现能启动但登录行为诡异的情况看一眼dmesg | grep -i sshd | grep -i denied有输出说明新的二进制触发了 AppArmor 限制常见的诱因是你把二进制装到了非标准路径或者自定义过 profile。处理方式是调整对应的 AppArmor 配置而不是关掉 AppArmor 了事。5.2 五分钟回滚预案升级完成后我会把备份目录封存留档不急着删。万一新版本在新功能带来了更严重的不兼容五分钟左右就能退回旧版sudo systemctl stop ssh sudo cp /root/openssh-backup/sbin/sshd /usr/sbin/sshd sudo cp /root/openssh-backup/bin/ssh /usr/bin/ssh sudo cp /root/openssh-backup/bin/scp /usr/bin/scp sudo cp /root/openssh-backup/bin/sftp /usr/bin/sftp sudo cp -a /root/openssh-backup/config/. /etc/ssh/ sudo systemctl start ssh如果你更希望把 dpkg 的包状态也恢复干净用之前apt-get download保存的 deb 包直接安装cd /root/openssh-backup sudo dpkg -i openssh-server_*.deb openssh-client_*.deb openssh-sftp-server_*.deb注意回滚后要再次执行sshd -t并确认ssh -V变回了旧版本同时测试新开一个会话能正常登录。只要你没删备份目录这个回滚流程永远有效。最后再分享一个个人习惯每次升级 OpenSSH 之前我都会把sshd_config里计划要改的部分先在文本编辑器里改好然后专门用一次正常的systemctl reload ssh而不是 restart让配置热生效。重启是最后一步、也是风险最大的一步把其他所有前置操作都做完、验证完最后那一下重启才值得按下。别小看这个顺序它能帮你把“升级 OpenSSH”从一次惊险的跳伞变成一趟有完整降落伞检查的单人飞行。
返回列表