CentOS 7 升级 OpenSSH 9.x 与 OpenSSL 实战
上周做一轮等保整改扫描器甩过来一份报告目标机 CentOS 7OpenSSH 相关漏洞 7 条两条高危甲方要求一周内闭环。翻了翻那台机器的底子ssh -V打出来是OpenSSH_7.4p1, OpenSSL 1.0.2k-fips——这是 CentOS 7 从娘胎里带出来的版本十年没动过。你可以说红帽这些年一直在往回移补丁但扫描器不认这套逻辑它只看版本号只要不是 8.x 或者 9.x报告里那条就不会消失。所以今天这篇就聊一件事CentOS7 上怎么把 OpenSSH 从自带的 7.4p1 稳稳当当地升到 9.x顺手把 OpenSSL 一起收拾干净让安全漏洞扫描彻底闭嘴。内容会覆盖源码编译和 RPM 打包两条路线、离线内网怎么玩、升级完老设备连不上怎么补救还有我自己在几十台机器上滚过一遍之后踩出来的那些坑。刚接触 CentOS7 的朋友能照着抄作业常年跟服务器打交道的老手也能从兼容性和回滚那几节里捞到点东西。1. CentOS 7 的 OpenSSH 到底欠在哪为什么非升不可先把话说清楚不然很多人一上来就yum update openssh然后发现版本号纹丝不动白忙一场还一头雾水。1.1 版本号背后补丁回移机制与扫描器的判定逻辑CentOS 7 自带的openssh-server-7.4p1-23.el7_9.x86_64这类包采用的是补丁回移策略上游 OpenSSH 修了一个 CVE维护者只把这个补丁单独摘出来打回 7.4p1 的源码树版本号不变Release号往后加一位。你在rpm -q --changelog openssh里能看到一长串 Resolves: CVE-xxxx-xxxx。问题在于大部分安全扫描工具不管是商业漏扫还是开源那几套判定逻辑非常粗暴拿你的版本号去比对一个 CVE 影响范围表7.4p1 9.8p1命中报高危。你拿 changelog 去跟甲方解释这个补丁已经回移了通常换来的回复是报告上还有请整改。更现实的一层是CentOS 7 已经在 2024 年 6 月底走完了生命周期官方源的更新基本停了。也就是说上游新爆出来的漏洞不会再有人帮你回移到 7.4p1 上。这种情况下继续守着老版本风险是真的不是扫描器吓唬你。所以升级的意义有两层一是让扫描器闭嘴二是真的把新版本里那些内存安全修复、算法强化拿到手。1.2 什么情况必须升什么情况可以先记一笔不是所有机器都值得动这一刀。我的判断标准是这样的对外暴露 22 端口、有公网 IP 的机器必升而且优先级最高。这类机器每天被爆破几万次是常态。等保、密评、行业合规要求出具版本截图的场景必升因为你要交差。内网里只允许跳板机访问、且有严格 ACL 的机器可以排期不用连夜干。跑着老旧业务、连编译环境都没有的机器先评估别硬上。有些老业务依赖特定 OpenSSH 行为升级后可能出事。还有一个选项是整个操作系统迁移把 CentOS 7 换到还在维护的发行版上一劳永逸。但如果你的机器上有几十个手工编译的服务、一堆找不到源码的私有包迁移成本会高到让你怀疑人生。这时候原地升级 OpenSSH 反而是性价比最高的选择。注意原地升级 OpenSSH 只解决 SSH 这一个组件的版本问题操作系统内核、glibc、其他库的漏洞该报还是会报。别指望升完 SSH 整份报告就清零了。2. 三条升级路线的成本账源码编译、RPM 打包、操作系统迁移路线选错后面全是返工。我把三条路线摆在台面上算一下。2.1 三路线横向对比路线单机耗时可批量性卸载回滚对 yum 的影响适合场景源码编译直装20-40 分钟差得写脚本靠备份手工还原会被 yum update 覆盖需 exclude一两台、急着交差打成 RPM 安装首次 1 小时后续 2 分钟好一条命令rpm -Uvh降级即可纳入包管理干净十台以上、有内网源操作系统迁移4 小时起差虚拟机快照无机器数量少、业务干净源码编译那条路最大的坑在于yum update会偷偷把你的新二进制覆盖回去。因为/usr/sbin/sshd这个路径仍然归openssh-server这个 RPM 包所有只要 yum 有机会更新它你辛辛苦苦编译的东西一夜回到解放前。解决办法是在/etc/yum.conf里加excludeopenssh*但这又会带来新的问题——将来真有紧急补丁你也收不到了。RPM 路线就干净很多新版本以9.8p1-1.el7的形式装进去rpm -q openssh能查到yum update不会乱动需要降级就rpm -Uvh回到旧包。代价是前期要写 spec 文件、要处理依赖和文件清单第一次会花点时间。2.2 我的选择逻辑我自己的习惯是新环境一律走 RPM老环境先做一台源码编译验证验证通过后再把编译参数固化进 spec 批量推。原因很实在——源码编译的过程本身就是一次探路。你能在这一台上把 sftp 子系统路径、PAM 配置、SELinux 上下文、老设备兼容参数全部试出来确认业务真的不受影响。等你写 spec 的时候脑子里已经有完整的文件清单了不会漏掉/etc/ssh/moduli或者/usr/libexec/openssh/sftp-server这种容易忘的东西。如果你手上只有一两台机器别折腾 RPM 了直接源码编译半小时收工。工具是拿来解决问题的不是拿来炫技的。3. 动手之前备份清单、依赖准备与回滚预案这一节是全文最不能跳过的部分。我见过太多人 SSH 升级失败后只能抱着显示器去机房接键盘。3.1 一份能救命的备份清单先建目录再逐项拷贝。注意用cp -a保留权限和时间戳OpenSSH 对密钥文件权限极其敏感。mkdir -p /root/ssh_upgrade_backup cd /root/ssh_upgrade_backup # 1. 整个 ssh 配置目录 cp -a /etc/ssh /root/ssh_upgrade_backup/etc_ssh # 2. PAM 配置 cp -a /etc/pam.d/sshd /root/ssh_upgrade_backup/pam_sshd # 3. systemd 相关 cp -a /usr/lib/systemd/system/sshd.service /root/ssh_upgrade_backup/ cp -a /usr/lib/systemd/system/sshd-keygen.service /root/ssh_upgrade_backup/ 2/dev/null cp -a /etc/sysconfig/sshd /root/ssh_upgrade_backup/ 2/dev/null # 4. 二进制本体 mkdir -p bin cp -a /usr/sbin/sshd /usr/bin/ssh /usr/bin/ssh-keygen /usr/bin/scp /root/ssh_upgrade_backup/bin/ # 5. 记录当前包清单 rpm -qa | grep -E openssh|openssl /root/ssh_upgrade_backup/pkg_list.txt这里面最关键的是/etc/ssh目录和几个二进制。配置目录里有你的主机密钥ssh_host_*_key、客户端配置、自定义的sshd_config一旦搞丢所有用 known_hosts 校验过指纹的客户端都会弹警告运维群里立刻炸锅。二进制留着是为了在最坏情况下直接拷回去顶一阵。顺手再记一笔ss -tlnp | grep :22确认监听端口systemctl is-active sshd确认服务状态。这些是升级后的对照基准。3.2 编译环境与依赖包含纯离线内网编译 OpenSSH 需要的包不多但一个都不能少yum install -y gcc gcc-c make zlib-devel pam-devel \ libselinux-devel perl perl-devel wget tar \ rpm-build rpmdevtools逐个说说为什么zlib-develSSH 传输要压缩没它编不过。pam-devel不装的话--with-pam直接失效结果是密码登录全部失效只剩密钥登录。这个坑特别隐蔽因为升级过程本身是成功的。libselinux-develCentOS 7 默认开启 SELinux不带上--with-selinux新 sshd 建立会话时无法正确设置安全上下文登录后会掉到非预期域里。rpm-build/rpmdevtools走 RPM 路线才需要。纯离线内网的情况要注意你不能yum install得提前在有网的机器上把依赖包下下来。最省事的做法是在一台同版本 CentOS 7 上执行mkdir -p /tmp/offline_pkgs yum install --downloadonly --downloaddir/tmp/offline_pkgs \ gcc gcc-c make zlib-devel pam-devel libselinux-devel perl rpm-build rpmdevtools然后把这些 rpm 连同源码包一起拷到内网机器yum localinstall -y /tmp/offline_pkgs/*.rpm。如果内网机器连yum localinstall都跑不动说明本地源没配好那就得先用 ISO 镜像挂载搭一个本地 yum 源mount -o loop /path/to/CentOS-7-x86_64-DVD.iso /mnt cat /etc/yum.repos.d/local.repo EOF [local] nameLocal CentOS 7 baseurlfile:///mnt enabled1 gpgcheck0 EOF yum clean all yum makecache这套本地源搭建流程在信创环境比如基于同类内核的国产发行版上同样适用只是依赖包名字可能略有差异遇到报错就去 ISO 里翻包名。3.3 Zlib 和 OpenSSL 要不要跟着一起升zlib我建议不动。CentOS 7 的 zlib 1.2.7 虽然老但历年的 CVE 都有回移补丁而且它被系统里成百上千个程序依赖你换掉它等于拿整个系统做实验收益远小于风险。OpenSSL是另一回事。CentOS 7 自带的是 1.0.2k这个版本在 OpenSSH 9.x 上能编过但有几个现实问题一是 OpenSSL 1.0.2 分支早已停止维护安全上不体面二是部分新版本的 OpenSSH 特性需要 1.1.1 以上三是扫描报告里 OpenSSL 也同样会被点名。所以我一般顺手把 OpenSSL 1.1.1 编到一个独立目录/usr/local/openssl不动系统自带的/usr/lib64/libssl.so.1.0.0。这样 OpenSSH 用新库而curl、rpm、yum这些还继续用系统库互不干扰——因为两者的 SONAME 不一样libssl.so.1.1vslibssl.so.1.0.0加载器不会搞混。3.4 留一条后路绝不断开当前会话这条是血泪教训。升级过程中你当前那条 SSH 连接绝对不能断也绝对不能让它成为唯一的连接。正确姿势是开两个 SSH 终端。终端 A 你用tmux或者screen挂上去干活终端 B 保持空闲不动随时准备在你重启 sshd 后第一个冲进去验证。如果不用 tmux就至少用nohup把关键命令挂到后台防止网络抖动把命令打断在make install半途。顺带把控制台也准备好。如果是虚拟机VMware 或者 VirtualBox 的控制台窗口开在那里如果是物理机确认一下 iDRAC、IPMI 这类带外管理能不能进。真的断了 SSH 又起不来这三样里至少要有一样能救你。注意升级过程中千万不要执行systemctl stop sshd然后去干别的。正确顺序是先把配置改好、sshd -t测试通过再systemctl restart sshd重启命令要和你验证命令写在同一行里用串起来。4. 源码编译升级 OpenSSH完整实操流程准备工作做完正式开工。我会把每一步的意图都讲清楚方便你自己判断哪些能改、哪些不能动。4.1 源码包下载与校验去 OpenBSD 官方镜像站拿源码包。我习惯放在/usr/local/src下编译路径干净将来找起来也方便。cd /usr/local/src wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz.asc wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz版本选择上有个经验不要追最新的 9.9/10.x选 p1 补丁版本里成熟度高的。比如 9.8p1 是修完内存安全系列问题之后的稳定版社区验证充分兼容性踩坑记录也多出问题容易搜到答案。真要用更新的版本先在测试机上跑一周再说。校验这一步很多人图快跳过我建议别跳。国密改造或者等保要求里软件来源可验证是明确条款而且镜像站下载偶尔真会断流产生坏包./configure报一堆莫名其妙错误的时候你会怀疑人生。gpg --verify openssh-9.8p1.tar.gz.asc openssh-9.8p1.tar.gz sha256sum openssh-9.8p1.tar.gzsha256sum的值去官网发布页对一下对不上就重下。4.2 先把 OpenSSL 1.1.1 装到独立目录cd /usr/local/src tar -zxf openssl-1.1.1w.tar.gz cd openssl-1.1.1w ./config --prefix/usr/local/openssl \ --openssldir/usr/local/openssl \ shared zlib make -j$(nproc) make install参数解释一下--openssldir指定配置和证书目录shared生成动态库OpenSSH 要用zlib开启压缩支持。编完之后让系统知道新库在哪儿echo /usr/local/openssl/lib /etc/ld.so.conf.d/openssl11.conf ldconfig -v | grep openssl /usr/local/openssl/bin/openssl version预期输出OpenSSL 1.1.1w 11 Sep 2023。这里有个细节值得说ldconfig加进去之后系统里所有程序都会去这个目录找库但只有 SONAME 匹配的才会被加载。CentOS 7 上绝大多数程序依赖的是libssl.so.1.0.0跟libssl.so.1.1是两回事所以不会串味。实测下来加完这一条之后yum、curl、rpm都照常工作。4.3 编译安装 OpenSSH 9.xcd /usr/local/src tar -zxf openssh-9.8p1.tar.gz cd openssh-9.8p1 ./configure \ --prefix/usr \ --sysconfdir/etc/ssh \ --with-ssl-dir/usr/local/openssl \ --with-zlib \ --with-pam \ --with-selinux \ --with-md5-passwords \ --with-privsep-path/var/empty/sshd \ --without-openssl-header-check每个参数背后都有讲究我挨个拆--prefix/usr让二进制直接装到/usr/bin、/usr/sbin也就是覆盖掉 RPM 装的旧版本。这样systemctl restart sshd用的 systemd 单元不用改PATH 也不用动。代价是rpm -V openssh-server会报文件被修改心里有数就行。--sysconfdir/etc/ssh配置目录沿用系统路径。新版 OpenSSH 的make install会判断sshd_config是否已存在存在就跳过不覆盖所以你的 PAM 配置和自定义策略能保住。但保险起见前面那份备份就是为这一刻准备的。--with-ssl-dir指向我们刚编的 OpenSSL 1.1.1。--with-pam这是密码登录的命门。少了它升级完你会发现密码怎么输都是Permission denied。--with-selinux让 sshd 在 SELinux enforcing 模式下能正确切换会话上下文。--with-md5-passwordsCentOS 7 上有些老账号的密码还是 MD5 哈希$1$开头不带这个参数这些人就登不上来了。虽然现在主流是 SHA-512但混合环境里加上更保险。--with-privsep-path/var/empty/sshd权限分离用的空目录。这个目录必须是 root 所有、权限 711编之前确认一下ls -ld /var/empty/sshd # 如果不存在或权限不对 mkdir -p /var/empty/sshd chown root:root /var/empty/sshd chmod 711 /var/empty/sshd配置脚本跑完之后看一眼输出末尾的摘要确认PAM support: yes、SELinux support: yes、OpenSSL: yes。这三项只要有一个是 no立刻停下来查原因别急着make。接下来编译安装make -j$(nproc) make installmake install的输出里会打印安装了哪些文件扫一眼有没有跳过sshd_config的提示skipping 或者 already exists有的话说明你的配置保住了。4.4 sshd_config 与 systemd 的适配改造新版本 SSH 启动时会严格校验配置文件任何一个已经被移除的配置项都会导致 sshd 拒绝启动。这是升级后起不来最常见的原因。所以先跑一次语法检查/usr/sbin/sshd -t如果报Bad configuration option: xxx去/etc/ssh/sshd_config里把那一行注释掉。常见的几个历史遗留项已移除的配置项移除版本处理方式UsePrivilegeSeparation7.5直接注释新版强制开启RhostsRSAAuthentication7.6注释RSAAuthentication7.7注释Protocol 27.6注释UseRoaming7.6注释KeyRegenerationInterval7.6注释另外几个建议改掉但不改也能跑的项# CentOS 7 自带的写法新版本已弃用该别名 PermitRootLogin without-password # 改成 PermitRootLogin prohibit-password # 老名字新版本推荐用 KbdInteractiveAuthentication ChallengeResponseAuthentication no # 改成 KbdInteractiveAuthentication no还有一条容易忽略Subsystem sftp /usr/libexec/openssh/sftp-server。因为我们用--prefix/usr编译新版的 sftp-server 会被装到/usr/libexec/sftp-server跟配置里的路径不一样。但旧的那个文件还在RPM 包的没被删所以 sftp 照样能用。如果你想用新版的 sftp把这一行改成Subsystem sftp /usr/libexec/sftp-server或者干脆用内置的internal-sftpSubsystem sftp internal-sftpinternal-sftp是编进 sshd 主程序的不依赖外部文件路径问题一劳永逸还省一个进程。我一般直接换成它。systemd 那边基本不用动CentOS 7 的sshd.service里ExecStart/usr/sbin/sshd -D $OPTIONS路径没变服务名没变。但/etc/sysconfig/sshd里如果有SSH_KEYS之类老变量留不留都行新版会忽略。4.5 切换、验证与回滚到这一步深呼吸把重启和验证串在一行里执行systemctl restart sshd sleep 1 ss -tlnp | grep :22 ssh -Vsystemctl restart成功、22 端口在听、ssh -V打出OpenSSH_9.8p1, OpenSSL 1.1.1w这三件事同时满足你才算过了第一关。第二关是用另一个终端真正连进来。不要用当前终端因为当前会话是旧连接就算 sshd 挂了它也可能还挂着给你虚假的安全感。新开一个终端ssh -p 22 useryour-server-ip进去了再验证一下 sftpsftp useryour-server-ip sftp pwd sftp ls sftp bye第三关是确认服务端能力。看看协商出来的算法是不是新的ssh -vvv localhost 21 | grep -E kex: algorithm|server host keysshd -T可以把最终生效的配置全量打出来比翻配置文件靠谱因为它会把 include 和默认值都算进去/usr/sbin/sshd -T | grep -E permitrootlogin|passwordauthentication|subsystem万一失败怎么回滚。如果重启后连不上从带外控制台或者终端 B 进去执行cp -a /root/ssh_upgrade_backup/bin/sshd /usr/sbin/sshd cp -a /root/ssh_upgrade_backup/etc_ssh/. /etc/ssh/ restorecon -Rv /etc/ssh /usr/sbin/sshd systemctl restart sshd备份里的旧二进制顶回去配置目录整体还原SELinux 上下文重建一般就能恢复到升级前的状态。5. 打包成 RPM几十台机器时的省心做法单机搞定了接下来解决还有三十台要办的问题。这时候源码编译就不划算了得打包。5.1 spec 文件骨架与关键字段先在普通用户下建工作目录yum install -y rpmdevtools rpmdev-setuptree把openssh-9.8p1.tar.gz丢进~/rpmbuild/SOURCES/然后在~/rpmbuild/SPECS/openssh.spec里写Name: openssh Version: 9.8p1 Release: 1.el7 Summary: OpenSSH client and server License: BSD URL: https://www.openssh.com/ Source0: openssh-%{version}.tar.gz BuildRequires: gcc gcc-c make zlib-devel pam-devel libselinux-devel Requires: zlib pam libselinux %define _sysconfdir /etc/ssh %define _libexecdir /usr/libexec %description OpenSSH 9.8p1 rebuilt for CentOS 7. %prep %setup -q %build ./configure --prefix/usr --sysconfdir/etc/ssh \ --with-ssl-dir/usr/local/openssl \ --with-zlib --with-pam --with-selinux \ --with-md5-passwords --with-privsep-path/var/empty/sshd make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR%{buildroot}几个关键点的用意BuildRequires和Requires分开写。前者是编译期要的后者是运行期要的。别把gcc塞进Requires否则装到生产机上白占空间。%install里的DESTDIR把文件装到一个临时根目录rpmbuild 再从这里打包不会污染你当前的系统。%files段是最考验人的地方建议先按源码编译那一台机器上的实际文件路径来写用rpmbuild -bb失败时它会提示 File not found照着补。%files大致长这样%files %attr(0755,root,root) /usr/bin/ssh %attr(0755,root,root) /usr/bin/scp %attr(0755,root,root) /usr/bin/sftp %attr(0755,root,root) /usr/bin/ssh-keygen %attr(0755,root,root) /usr/sbin/sshd %attr(0644,root,root) /usr/libexec/sftp-server %dir /var/empty/sshd %attr(0711,root,root) /var/empty/sshd %config(noreplace) /etc/ssh/sshd_config%config(noreplace)是个很有用的标记升级时如果用户改过这个文件rpm 不会覆盖而是生成一个.rpmnew运维的配置能保住。打包rpmbuild -bb ~/rpmbuild/SPECS/openssh.spec产物在~/rpmbuild/RPMS/x86_64/下。注意%files里千万不要把/etc/ssh/ssh_host_*_key写进去。那是每台机器唯一的身份凭据被打进包里分发给三十台机器等于三十台机器共用一套主机密钥SSH 的中间人防护直接废掉。5.2 离线环境的依赖分发与本地 yum 源内网机器装的时候会遇到依赖问题——libselinux、pam一般都有问题不大但如果你连/usr/local/openssl那套库也要一起推就得单独打个包或者干脆在每台机器上跑一遍 OpenSSL 编译脚本。更规整的做法是搭一个内网 yum 源yum install -y createrepo mkdir -p /data/repo/openssh cp ~/rpmbuild/RPMS/x86_64/openssh-9.8p1-1.el7.x86_64.rpm /data/repo/openssh/ createrepo /data/repo/openssh cat /etc/yum.repos.d/openssh.repo EOF [openssh-local] nameLocal OpenSSH Repo baseurlfile:///data/repo/openssh enabled1 gpgcheck0 EOF如果这台机器本身还兼着 HTTP 服务把baseurl换成http://内网IP/repo/openssh其他机器直接指过来一条yum install -y openssh全搞定。批量执行的时候我习惯写成这样一段脚本加上失败即停和日志#!/bin/bash set -e LOG/var/log/openssh_upgrade.log exec $LOG 21 echo $(date) start cp -a /etc/ssh /root/ssh_backup_$(date %F) yum install -y openssh-9.8p1-1.el7 /usr/sbin/sshd -t systemctl restart sshd sleep 2 systemctl is-active sshd ssh -V echo $(date) done set -e保证任何一步失败立刻中止不会出现配置测试失败但服务还是被重启了这种灾难。日志落到文件里事后排查有据可查。6. 踩坑实录常见问题与排查速查表下面这些都是我或者同事真真切切遇到过的。按类别整理出问题的时候直接查表。6.1 连不上、认证失败类现象根因处理方式密码登录全部Permission denied编译时漏了--with-pam重新 configure 加--with-pam确认PAM support: yes密钥登录失败日志报Authentication refused: bad ownership or modes家目录或.ssh权限过松chmod 700 ~/.ssh、chmod 600 ~/.ssh/authorized_keys、chmod 700 ~客户端报REMOTE HOST IDENTIFICATION HAS CHANGED主机密钥被覆盖或重生成确认密钥文件还是原来那份客户端ssh-keygen -R host老客户端老交换机、老存储连不上报no matching host key type found8.8 之后默认禁用ssh-rsaSHA-1见 6.4 节的兼容参数部分账号能登部分不能老账号用 MD5 密码哈希编译时加--with-md5-passwords登录后立刻断开日志无异常/etc/pam.d/sshd被覆盖或缺失从备份还原/etc/pam.d/sshd权限那一组问题特别值得展开。新版 OpenSSH 对权限的严格程度比 7.4 高了不少尤其是authorized_keys所在的整个路径——从/home一路到文件本身任何一级被别人可写都可能被拒。我遇到过最离谱的一次是/home目录权限被谁改成了 777结果所有人密钥登录全挂日志里那句话藏在一堆Failed password中间找了两个小时。排查这类问题最有效的办法是把 sshd 的日志级别临时调高# 在 /etc/ssh/sshd_config 中 LogLevel DEBUG3改完systemctl restart sshd然后在另一个终端尝试登录同时tail -f /var/log/secure。日志会详细告诉你卡在哪个阶段——是公钥没被接受还是 PAM 拒绝还是权限检查不通过。定位完记得把级别调回INFODEBUG3 的日志量太吓人。6.2 服务起不来、启动即退出类现象根因处理方式systemctl status sshd显示Bad configuration option配置里有已移除的指令按 4.4 节的表逐条注释报Privilege separation user sshd does not exist系统里没有 sshd 用户useradd -r -s /sbin/nologin -d /var/empty/sshd sshd报Missing privilege separation directory: /var/empty/sshd目录不存在或权限不对mkdir -p并设chmod 711、chown root:root报no hostkeys available主机密钥缺失或权限不对备份还原密钥chmod 600 ssh_host_*_key启动秒退日志报setsockopt SO_REUSEADDR或端口占用22 端口被别的进程占了ss -tlnp | grep :22找出来处理掉SELinux 阻断了操作ausearch有 AVC 记录二进制上下文不对restorecon -Rv /usr/sbin/sshd /usr/bin/ssh /etc/ssh这里重点说一下 SELinux。不要一看到问题就setenforce 0那等于把整台机器的强制访问控制关掉合规上过不去。正确做法是先看审计日志ausearch -m avc -ts recent如果确定是新装的二进制上下文不对restorecon重建就行restorecon -Rv /usr/sbin/sshd restorecon -Rv /etc/ssh ls -Z /usr/sbin/sshd正确的上下文应该是sshd_exec_t。如果restorecon之后还是不对可能策略里没有对应的文件上下文规则那就需要semanage fcontext手工加。6.3 版本没变、扫描器还在报漏洞这个最让人抓狂明明编译安装一路顺利ssh -V也是新版本扫描报告纹丝不动。可能的原因有这么几个第一你重启的是错的。make install只是把文件放到磁盘上正在跑的 sshd 进程还在内存里跑老代码。必须systemctl restart sshd或者确认服务被正确 reload。用ps -ef | grep sshd看一下进程的启动时间比ssh -V更能说明问题。第二扫描器读的是rpm -q openssh的版本。很多漏扫工具会同时采集 RPM 包版本和实际 banner只要 RPM 包版本还是 7.4p1它照样报。这种情况下如果走的是源码编译路线你只能改走 RPM 打包让包版本变成 9.8p1。第三扫描器缓存。有些平台的扫描结果是缓存的需要手动点一次重新扫描或者等下一个扫描周期。第四真的有另一个 22 端口在外面。端口映射、负载均衡、跳板机转发中间任何一环都可能让你扫到的是另一台老机器。nmap -sV -p 22 目标IP从外部确认一次。排查顺序我一般是ps -ef | grep sshd看进程 →ss -tlnp | grep :22看监听 → 从外部nmap看 banner → 从内部ssh -V看版本 → 最后看扫描器采集口径。基本能定位到是哪一环。6.4 兼容旧设备老客户端连不上的参数补偿OpenSSH 8.8 起默认禁用了ssh-rsaSHA-1 签名和ssh-dss9.8 起连 DSA 算法都整个删掉了。这在安全上是对的但现实里你总会遇到几台十年没换固件的网络设备、几台装不下新客户端的工控机。服务端临时放行只对确实需要的来源放不要全局开# /etc/ssh/sshd_config HostKeyAlgorithms ssh-rsa PubkeyAcceptedAlgorithms ssh-rsa CASignatureAlgorithms ssh-rsa注意PubkeyAcceptedAlgorithms这个关键字在 OpenSSH 8.5 之前叫PubkeyAcceptedKeyTypes8.5 之后改名了。如果你混用不同版本的机器写老的会报Bad configuration option。客户端侧也可以单点配置比在服务端全局放开更安全。在~/.ssh/config里Host old-switch-10.0.0.5 HostkeyAlgorithms ssh-rsa PubkeyAcceptedAlgorithms ssh-rsa KexAlgorithms diffie-hellman-group14-sha1 CASignatureAlgorithms ssh-rsa这样做的好处是只有连这一台老设备时才降级其他连接仍然走强算法。还有一个常被忽略的点是DSA 密钥。OpenSSH 9.8 已经完全移除了ssh-dss如果你手上还有id_dsa这种密钥升级后直接作废。处理方式是提前换成 Ed25519 或者 RSA 3072 位ssh-keygen -t ed25519 -C your_comment换完把公钥推到所有目标机器的authorized_keys确认能登了再升级 OpenSSH。顺序别搞反。7. 几个我踩过之后才明白的细节聊到这儿技术流程基本交代完了。剩下几条是文档里一般不写、但实操中特别容易吃亏的东西。第一make install之后别急着关终端查日志。我见过一次systemctl restart sshd返回 0看着是成功了实际 sshd 起来之后又秒退了——因为 systemd 用-D前台模式启动进程退出的瞬间restart命令还没返回。正确做法是systemctl restart sshd sleep 2 systemctl is-active sshd用is-active的结果说话别信退出码。第二/etc/yum.conf里的exclude要成对考虑。加了excludeopenssh*之后将来真想装个 openssh 相关的工具包比如某些依赖openssh-clients的软件会被一起挡掉。更精细的写法是指定完整包名excludeopenssh-server openssh-clients openssh别用通配符一锅端。第三备份不只是备份还要定期演练还原。我有个同事备份做了三个月的 ssh 配置真出事的时候才发现备份脚本里cp -a /etc/ssh写成了cp -a /etc/ssh/加了个斜杠行为完全不同目录结构错位还原回去 sshd 直接起不来。备份完随手diff -r对比一下原目录和备份目录花不了十秒钟。第四升级窗口尽量选在业务低谷。不是因为升级本身有多危险而是因为升级后可能触发的兼容性问题——某个定时任务用的老客户端连不上了、某个监控采集脚本的指纹校验失败了——这些都需要人盯着。放在业务高峰期做等于给自己上难度。第五别迷信升级完就安全了。OpenSSH 只是一个入口真正决定这台机器安全水位的是有没有禁用密码登录、有没有限制来源 IP、fail2ban 之类的爆破防护有没有开、root 登录有没有关掉。我见过 OpenSSH 升到最新版但PermitRootLogin yes加PasswordAuthentication yes的机器端口上每天几万次爆破版本再新也没用。升级只是把已知漏洞这一项勾掉剩下的功课还得自己做。第六如果条件允许把 OpenSSH 的编译参数、版本号、升级日期记进 CMDB 或者一份简单的台账。我自己的习惯是在/etc/ssh/UPGRADE_INFO里留一行2024-xx-xx upgraded from 7.4p1 to 9.8p1, openssl 1.1.1w, --with-pam --with-selinux。半年后接手的人看到这行字能省掉一整个下午的排查时间。这种小动作收益远比它看起来大。