
在做服务器安全整改的时候“OpenSSH版本过旧”几乎是每台内网服务器都逃不掉的整改项。麒麟系统在国产化环境里用得很多但默认自带的OpenSSH版本普遍停留在7.x左右安全扫描工具随便跑一遍就是一堆CVE而且新版客户端和服务端之间的算法协商也经常因为版本太老而出问题。前阵子我负责的一台银河麒麟V10服务器正好赶上这趟整改需要把系统自带的OpenSSH升级到9.8p1。整个流程走下来从环境准备到编译安装再到踩坑排查花了大半天时间。这篇文章把完整过程整理成一份可复用的操作记录给正在处理同类任务的运维同行做个参考。1. 升级前的风险评估与环境确认1.1 为什么偏偏要动OpenSSH先说说这次升级的直接原因。安全扫描报告上是满满一页OpenSSH相关的高危漏洞清单尤其是2024年公开的regreSSHion漏洞CVE-2024-6387影响范围覆盖了OpenSSH 8.5p1到9.7p1之间的很多版本修复这个漏洞的版本正好是9.8p1。这也是为什么很多人一上来就指定要升级到9.8p1而不是随便装一个新版就完事。另外还有一层原因来自合规侧。很多等保整改和内部安全基线会明确要求SSH相关组件的版本必须满足某一阈值而麒麟系统出厂自带的OpenSSH在这类检查面前基本不合格。再加上新版OpenSSH在算法层面做了不少增强比如支持更新的密钥交换算法老版本客户端在连接一些较新的服务器时偶尔会报出no matching key exchange method之类的算法协商失败问题。无论是从安全角度还是日常使用角度升级OpenSSH都是一件躲不开的事。1.2 动手前先摸清这五件事第一次在麒麟系统上做这种升级我差点栽在一个很基础的环节上。所以这里强烈建议任何操作之前先花十分钟把下面五件事确认清楚。第一系统版本和CPU架构。执行cat /etc/os-release和uname -m确认是银河麒麟V10的哪个版本架构是x86_64还是aarch64。这个直接决定了后面准备源码包时要注意什么虽然OpenSSH本身跨平台但openssl、zlib这些组件在编译时是纯C源码还好如果是拿二进制RPM包架构选错就直接装不上。第二当前OpenSSH的版本。ssh -V这条命令会输出类似OpenSSH_7.4p1, OpenSSL 1.1.1k的信息。记下这个版本号升级前后做个对比也方便确认你到底跨了几个大版本。第三有没有备用通道。这句话是重点中的重点如果你不在机房而手头又没有带外管理或者虚拟机快照那升级前必须先准备一条后路否则升级过程中一旦sshd出了任何问题你连服务器都登不进去那就只能求人了。后面我会单独讲怎么快速开一个临时通道。第四包管理器是apt还是yum。麒麟V10的桌面版和服务器版底层体系不完全一样有的基于Debian有的基于CentOS导致包管理器不一样。这个决定了你在准备编译依赖时用apt-get install还是yum install命令差别很大。第五编译工具链是否齐全。OpenSSH用源码编译gcc、make、openssl头文件、zlib头文件这些是硬性前提。如果系统里连gcc都没有得先确认软件源可用不然准备工作就得从装gcc开始。2. 依赖梳理与离线安装包准备2.1 OpenSSH升级真正麻烦的是依赖链很多人以为OpenSSH是一个孤零零的软件解压、编译、安装三步走就够了实际远没这么简单。它编译时会动态链接zlib做数据压缩链接openssl做加密和密钥协商还要通过PAM做账号认证。这就像做菜光有主料不够油盐酱醋都得备齐不然下锅的时候才发现缺这缺那火候全耽误了。对麒麟这种国产化系统来说依赖链的问题还会被放大。老版本OpenSSH对新系统的适配还好说反而是新版OpenSSH对系统自带openssl库的版本特别敏感。如果系统里是OpenSSL 1.0.2那一代的老库直接编译新版OpenSSH大概率会卡在configure阶段报错信息基本是Cant find recent OpenSSL libcrypto。所以升级OpenSSH前先把zlib、openssl、pam这三条依赖链的版本列出来看一眼做到心里有数。2.2 离线环境下把工具链和源码一次备齐大多数内网环境的麒麟系统是不能随便访问公网的所以离线预置这一步特别关键。我在这次操作前的做法是先找一台可以联网的机器把下面的工具包全部准备好再拷贝到内网。如果目标机器本身有软件源或能访问内网Yum/Apt仓库直接先装编译依赖# yum系部分麒麟服务器版 yum install -y gcc make zlib-devel openssl-devel pam-devel # apt系部分麒麟桌面版 apt-get install -y build-essential zlib1g-dev libssl-dev libpam0g-dev接着准备源码包。OpenSSH 9.8p1的源码可以去OpenSSH官网下载另外建议把zlib-1.2.13.tar.gz一并带上以防目标机器zlib版本过低。放在/usr/local/src目录下备用。如果目标机器连内网软件源都没有那就只能靠离线RPM/Deb包了。在能联网的同版本系统上用yumdownloader或repotrack把openssh相关的软件包连依赖一起拉下来yum install -y yum-utils mkdir /root/openssh-rpms repotrack openssh openssh-server openssh-clients -p /root/openssh-rpms然后把整个目录拷到内网机器用rpm -Uvh *.rpm或dpkg -i *.deb安装。但说实话在追求指定版本和可控性的场景里源码编译依然是更稳的路径。2.3 为什么不建议顺手把OpenSSL也升级这里要说一个经验之谈。看到新版OpenSSH对openssl版本有要求很多人的第一反应是先把openssl升级了觉得底层库越新越好。除非系统自带的openssl确实低于1.1.1否则千万不要顺手把openssl一起升级。原因很简单openssl是系统底层组件curl、nginx、很多数据库驱动、各种服务全都动态链接着它单独把它换成一个新版本很可能导致其它服务在运行时报libssl.so.3: cannot open shared object file之类的错误。严重情况下yum/apt命令本身都会挂掉那才是真正的灾难。这次我检查了一下麒麟V10自带的openssl版本是1.1.1系列完全满足OpenSSH 9.8p1的编译要求所以我没有动系统openssl直接跳过升级减少了一个极大的风险变量。如果你检查后发现确实老得离谱也建议把新版openssl单独编译到/usr/local/openssl不要覆盖系统的然后编译OpenSSH的时候用--with-ssl-dir/usr/local/openssl指定路径。3. 核心操作编译安装OpenSSH 9.8p13.1 先开一个telnet备用通道防止被锁在门外升级sshd这种服务最忌讳的就是直接开干。我处理这件事的第一步不是解压源码而是先给服务器开一个telnet备用通道。telnet虽然是明文协议平时绝对不推荐使用但作为升级窗口期内的临时保命通道它是成本最低、见效最快的手段。麒麟系统上临时启用telnet很简单yum install -y telnet-server telnet systemctl start telnet.socket systemctl enable telnet.socket然后确认23端口放行。很多内网环境还有firewalld或iptables需要临时放行firewall-cmd --zonepublic --add-port23/tcp当然前提是你能搞定telnet登录后的账号认证root登录还要检查/etc/securetty里有没有pts/0等终端配置。等OpenSSH升级完成、SSH验证通过后记得立即关掉这个通道systemctl stop telnet.socket systemctl disable telnet.socket这个备用通道看起来很简单但关键时刻真的能救命。有一次我远程升级一台服务器sshd配置测试没通过服务起不来要不是提前开着telnet就只能买机票去机房了。3.2 编译安装前的准备与依赖安装在确定系统信息、依赖版本都OK之后我把准备好的源码包传到服务器上开始正式操作。cd /usr/local/src tar -zxvf openssh-9.8p1.tar.gz cd openssh-9.8p1编译之前还要做两件事。第一确认存在sshd系统用户如果不存在则手动创建id sshd || useradd -r -s /sbin/nologin sshd第二创建OpenSSH运行时需要的特权分离目录mkdir -p /var/empty/sshd chmod 755 /var/empty/sshd chown root:root /var/empty/sshd这个目录是OpenSSH做privilege separation用的很多编译安装失败或启动报错都跟它有关系提前建好能省不少事。3.3 编译参数与安装命令详解然后就是编译参数。这一步我建议别直接用./configure默认参数要结合麒麟系统的特点来。我用的命令如下./configure \ --prefix/usr/local/openssh \ --sysconfdir/etc/ssh \ --with-pam \ --with-md5-passwords \ --with-privsep-path/var/empty/sshd \ --without-openssl-header-check每个参数都有它的用意--prefix/usr/local/openssh是指定安装目录。不要直接用默认的/usr/local更不要直接覆盖系统自带的openssh目录这样万一出问题原系统文件基本没被破坏回滚空间更大。--sysconfdir/etc/ssh是让新版本继续读取原有配置这样可以沿用在/etc/ssh里的主机密钥和配置习惯不用把配置搬到新路径省了后续一堆麻烦。--with-pam这个是必须的不启用PAM的话系统账号的密码登录基本上会失灵。--with-md5-passwords是为了兼容老系统里还在使用MD5散列的账号密码加上基本没坏处。--with-privsep-path是指定特权分离目录路径对应刚才创建的/var/empty/sshd。--without-openssl-header-check是在确认系统openssl库可用但头文件版本检查过严时使用的跳过configure阶段对openssl头文件版本的强校验。确认没有报错后执行编译和安装make -j$(nproc) make install关于编译过程我想多说一句。如果报zlib.h: No such file or directory说明没装zlib-devel如果报openssl/opensslv.h: No such file or directory说明没装openssl-devel。这些都是依赖缺失补齐再重新configure即可不要硬着头皮往下走。3.4 替换二进制、建立软链与调整服务编译安装完成之后最坑的一步来了。很多人以为make install结束就大功告成了其实新二进制还躺在/usr/local/openssh下面系统的sshd还是旧版本。这时候如果不做替换直接重启sshd服务系统会继续用旧二进制看起来升级成功了实际上是白忙一场。先备份原文件避免出问题无法回滚for f in /usr/sbin/sshd /usr/bin/ssh /usr/bin/scp /usr/bin/sftp; do [ -f $f ] cp $f $f.bak.$(date %Y%m%d) done然后建立软链指向新版本ln -sf /usr/local/openssh/sbin/sshd /usr/sbin/sshd ln -sf /usr/local/openssh/bin/ssh /usr/bin/ssh ln -sf /usr/local/openssh/bin/scp /usr/bin/scp ln -sf /usr/local/openssh/bin/sftp /usr/bin/sftp这样做的好处是原有的systemd服务单元文件里写的是ExecStart/usr/sbin/sshd我们用软链覆盖后服务启动时会自然加载新二进制不需要大改sshd.service文件。如果发现你的服务文件写的是其它路径还得去/usr/lib/systemd/system/sshd.service里把ExecStart改成实际路径。接下来生成主机密钥除非你确定保留原密钥而原目录里已有否则执行一下ssh-keygen -A然后测试配置是否正确/usr/sbin/sshd -t没有输出就代表配置没问题。如果报Permissions xxx are too open这类错误通常是/etc/ssh下的密钥文件权限不对私钥要600公钥要644用chmod修正就行。3.5 验证与回归不要着急重启系统升级完成的验证顺序非常关键。我刚升级完那会儿心里还挺着急想看看效果但克制住了冲动作了系统性的验证。先在服务器本机测试连接ssh -v localhost用-v模式可以看到完整的调试输出如果认证、算法协商、会话建立都能跑通说明基础功能正常。然后确认版本ssh -V输出已经变成OpenSSH_9.8p1说明二进制替换成功。接着检查监听端口是否正常ss -lntp | grep 22确认sshd在监听22端口。最后再找一台远程机器用实际业务账号试登录一次确认远程场景也没问题。整个验证通过之后还要专门提醒一句升级后不要马上重启系统。我见过有人升级完顺手一条reboot下去结果sshd服务因为某个依赖问题没起来整台服务器只能靠物理控制台去抢救。正确做法是先观察一段时间确认一切运行正常再安排后续的维护窗口重启。哪怕要重启也建议先手动执行一次systemctl restart sshd确认服务能被系统正常拉起再说。4. 常见问题与排查技巧实录4.1 动态库加载失败的两种典型场景编译安装最大的坑往往不在编译而在运行时。比如执行sshd时直接报sshd: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file这就是典型的动态库路径问题。原因要么是系统里原来的openssl库路径不在默认搜索路径中要么是二进制依赖的库版本和系统里实际存在的库不匹配。排查方法很直接ldconfig -p | grep libcrypto看看系统能不能找到依赖库。如果找不到确认/usr/local/openssl/lib或系统原有openssl的lib目录下有没有对应的.so文件有的话就把它加入动态库配置echo /usr/local/openssl/lib /etc/ld.so.conf.d/openssl.conf ldconfig还有另一种情况系统里同时存在多个openssl版本sshd链接到了不存在的那个。这种时候可以用ldd /usr/local/openssh/sbin/sshd查看它实际依赖的库路径再对症处理。4.2 sshd服务起不来的高频原因升级后最让人紧张的时刻就是执行systemctl restart sshd后服务没有如预期启动。根据我的经验出现这类问题最常见的原因不外乎下面几个现象可能原因处理方法报错 Missing privilege separation directory: /var/empty/sshd特权分离目录不存在或权限不对mkdir -p /var/empty/sshd chmod 755 /var/empty/sshd报错 Privilege separation user sshd does not existsshd用户缺失useradd -r -s /sbin/nologin sshd报错 /etc/ssh/sshd_config: Bad configuration options配置文件里有新老版本不兼容的参数用备份的旧配置逐项比对删掉已废弃的选项服务起不来但无明确报错systemd单元文件路径还是旧的检查ExecStart路径执行systemctl daemon-reload端口被占用旧sshd进程还在占用22端口先确认并停掉旧进程再重启服务碰到服务起不来先看journalctl -u sshd或/var/log/messages里的日志报错信息通常已经把答案写出来了别一上来就重新编译。4.3 root无法登录/密码认证失败的排查还有一种高频问题是服务起来了但登录不进去要么root直接被拒要么密码怎么输都不对。root被拒通常是sshd_config里PermitRootLogin的默认值问题。新版OpenSSH默认改成prohibit-password也就是禁止root密码登录只允许密钥登录。如果业务上有root密码登录需求需要在/etc/ssh/sshd_config里明确设置为PermitRootLogin yes PasswordAuthentication yes改完记得systemctl restart sshd。密码认证失败还有一个隐蔽原因是/etc/pam.d/sshd文件缺失。编译安装时如果pam相关模块没有正确配置sshd无法通过PAM的认证链路表现就是密码输入正确但登录还是失败。解决办法是从备份中恢复旧版自带的PAM配置或者找一个同版本系统拷贝一份过来。如果系统开了SELinux升级后还要注意安全上下文。很多编译安装的文件SELinux标签是错的需要执行restorecon -Rv /etc/ssh /usr/local/openssh不然即使权限配置全对SELinux也会把sshd挡在外面。4.4 升级后的回滚方案升级前我特意把所有原始文件做了备份就是为了给回滚留条后路。万一升级后出现不可控的问题可以快速回到原来的状态。回滚操作很简单# 恢复原始二进制 cp /usr/sbin/sshd.bak.20240101 /usr/sbin/sshd cp /usr/bin/ssh.bak.20240101 /usr/bin/ssh # 重启服务 systemctl restart sshd如果连配置文件都改过同样把/etc/ssh/sshd_config的备份覆盖回去。另外提醒一点升级时下载的rpm包、源码包不要急着删如果回滚到旧版本后发现驱动或依赖有异常还能再拿这些包出来分析。4.5 避坑清单速查表最后整理一份升级OpenSSH的避坑速查表这是我反复踩坑后总结出来的检查项容易踩的坑正确做法备用通道直接开干升级失败无法远程先开telnet备用确保有物理控制台或快照依赖判断盲目升级系统openssl只在低于1.1.1时才处理否则不动底层库二进制替换make install后不建软链ln -sf把新二进制链到/usr/sbin和/usr/bin配置文件直接用默认配置沿用/etc/ssh原配置增量修改服务文件忘记daemon-reload修改unit后执行systemctl daemon-reload权限问题密钥文件权限过宽私钥600公钥644目录755回归验证升级完立刻重启先本机连接远程连接验证观察稳定后再安排重启PAM配置删掉或覆盖/etc/pam.d/sshd确认PAM文件存在否则密码认证必然失败这次麒麟系统升级OpenSSH处理下来最大的感受就是操作本身不复杂难的是在离线、远程、高风险的环境里把每一步细节都做扎实。特别是二进制替换和服务管理这两块很多人栽了跟头还不知道问题出在哪里。希望这份记录能帮你少走弯路。如果你也在折腾类似的服务器升级建议先把上面的检查清单过一遍再动手遇到具体问题欢迎在评论区交流。