
简介一份面向 CentOS7 系统管理员与运维人员的 OpenSSH 升级代码包围绕 OpenSSH 10.0p2 与 OpenSSL 3.0.16 的同步升级展开聚焦无网络或远程升级场景下 telnet 后备登录方案以规避升级中断导致无法登录服务器的风险。包内完整覆盖依赖安装、zlib/OpenSSL/OpenSSH 下载编译、旧版本备份、编译参数配置、动态库链接设置以及升级后 sshd 服务无法启动时的日志检查、权限调整、动态库状态确认等常见排错方法。压缩包共 3 个文件包含 HTML 格式的图文操作指南、inscode 代码配置与 gitignore 忽略规则整体仅 6KB轻量便于对照执行。目前已有 105 人学习下载。借助这份代码包读者可系统掌握 CentOS7 下 OpenSSH/OpenSSL 升级的完整链路和关键细节降低因版本过旧引发的安全漏洞及兼容性问题同时显著提升远程升级场景下的操作成功率与回退效率便于后续维护与二次升级。 从 CentOS 7 升级 OpenSSH 这件事我前前后后做过不下二十次从最初战战兢兢怕把自己锁在服务器外面到现在基本能做到二十分钟内平稳升级完中间踩过的坑确实不少。今天把整个流程、关键参数和常见问题一次性整理出来希望能帮你少走弯路。先说清楚一个认知CentOS 7 自带的 OpenSSH 版本是 7.4这个版本在 2021 年之后就已经不再维护即使你用yum update把系统补丁打到最新OpenSSH 依然是 7.4 的内核只是小版本号变了而已。而安全扫描工具比如 Nessus、OpenVAS和等保测评会直接扫描出大量与 OpenSSH 相关的中高危漏洞CVE 列表一拉一串这时候除了升级到 8.x 或 9.x 的新版本几乎没有别的选择。整个升级方案我建议采用源码编译安装因为 CentOS 7 的官方源里根本没有新版 OpenSSH虽然有第三方源比如 ELRepo 或者某些镜像站的自定义源能提供但生产环境里我不太推荐依赖第三方源原因很简单你无法百分之百确定第三方源里的 RPM 包经过了完整的功能测试而且 RPM 包的编译参数未必适合你的业务场景。源码编译虽然麻烦一点但每一步都是可控的出了问题也容易排查。下面是完整流程。1. 升级前的准备与风险预判1.1 检查当前环境状态动手之前先把这个命令跑一遍ssh -V cat /etc/redhat-release uname -a记录下当前的 OpenSSH 版本和系统内核版本。这里有个容易忽略的细节OpenSSH 9.x 对 OpenSSL 的版本要求是 1.1.1 以上而 CentOS 7 自带的 OpenSSL 是 1.0.2k版本明显不够所以标准的升级路径是“先升 OpenSSL再升 OpenSSH”。但 OpenSSL 又牵扯到系统里大量的二进制依赖比如libcrypto.so.10被一堆程序引用所以 OpenSSL 不能直接覆盖系统的/usr/lib64下的库文件更稳妥的做法是编译安装到一个独立目录比如/usr/local/openssl然后让 OpenSSH 通过编译参数指定使用新版 OpenSSL。这样既满足 OpenSSH 的编译要求又不影响系统里其它依赖旧版 OpenSSL 的程序。1.2 备份关键配置千万别跳过后面的备份操作。OpenSSH 升级最怕的不是升级失败而是升级后 sshd 无法启动你又没有备用通道直接把自己锁在外面只能去机房或者找云平台的管理终端。需要备份的文件和目录有cp -rp /etc/ssh /etc/ssh.bak.$(date %F) cp -p /etc/pam.d/sshd /etc/pam.d/sshd.bak.$(date %F) cp -p /etc/selinux/config /etc/selinux/config.bak.$(date %F)之所以把 SELinux 的配置也备份一份是因为编译安装的 OpenSSH 默认会开启 SELinux 相关的支持但新版本的 sshd 二进制文件可能没有正确的 SELinux 上下文导致登录时权限被拒绝。这个问题我在后面的排查部分会详细说。1.3 安装编译依赖这一步很多人会漏而且漏了之后报错信息特别玄学比如configure: error: *** zlib.h missing或者fatal error: openssl/opensslv.h: No such file or directory。在 CentOS 7 上我们需要下面几个基础依赖包yum install -y gcc gcc-c make automake autoconf libtool perl zlib-devel pam-devel rpm-build我个人的习惯是额外装上gcc-c和pam-devel因为有些老机器上最小化安装时这两个包默认是没有的。pam-devel尤其重要如果缺了它编译出来的 sshd 虽然也能跑但在 PAM 认证方面会有意想不到的毛病比如密码正确却登录不上。提示编译依赖安装阶段建议先执行yum update -y把系统自身的软件源刷新一遍确保依赖包的版本是最新的。这一步不涉及升级 OpenSSH只是让系统处于一个健康的软件包基线状态。1.4 安装并启用 Telnet 保底通道这个做法在运维圈子里有争议但我个人强烈建议做。OpenSSH 升级过程中如果 sshd 服务起不来你还有一个 Telnet 通道能进系统修复不至于直接失联。yum install -y telnet-server telnet systemctl enable telnet.socket systemctl start telnet.socket注意Telnet 是明文传输风险等级很高所以这个通道只是临时保底用的升级确认没问题后必须马上关闭systemctl stop telnet.socket systemctl disable telnet.socket实际上我做过很多次升级之后会忘记关 Telnet后来形成习惯升级完验证完的第一件事就是关掉 Telnet这个操作比升级本身还要重要。2. 源码编译安装新版 OpenSSH 全过程2.1 下载软件包并确定版本到官网下载源码包。目前建议使用 OpenSSH 9.8p1或者你写这篇文档时能看到的最新稳定版配合 OpenSSL 1.1.1w 和 zlib 1.3.1。这里有一个版本兼容性的问题需要提前讲清楚OpenSSH 9.x 已经彻底禁用 SSHv1 和部分弱加密算法如果你的业务里有老设备还在用 SSHv1升级后必须同步调整设备配置。OpenSSL 1.1.1 是 LTS 版本长期支持版稳定性好而且和 CentOS 7 的系统组件兼容性经过大量生产环境验证没必要追求最新的 OpenSSL 3.x。zlib 版本不用太纠结1.2.11 或者 1.3.1 都行只要编译时能正常通过。下载完成后统一放在/opt/upgrade目录下mkdir -p /opt/upgrade cd /opt/upgrade wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz wget https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz tar xf zlib-1.3.1.tar.gz tar xf openssl-1.1.1w.tar.gz tar xf openssh-9.8p1.tar.gz如果你的服务器在某个内网环境里无法直接访问外网就在能联网的机器上下载好源码包然后通过 FTP 或者 U 盘拷进去内网环境升级一定要提前把源码包准备好这一点不用我多说。2.2 编译安装 zlib先编译 zlib因为 OpenSSH 依赖 zlib 库来处理数据压缩cd /opt/upgrade/zlib-1.3.1 ./configure --prefix/usr/local/zlib make -j$(nproc) make install这里的--prefix/usr/local/zlib是刻意指定的独立目录目的和上面说 OpenSSL 时一样避免覆盖系统自带的 zlib防止影响其它依赖系统 zlib 的程序。编译参数-j$(nproc)的意思是使用机器的所有 CPU 核心并行编译编译速度会快很多。如果你的机器配置太低比如 1 核 1G 的小机器可以把$(nproc)去掉直接用make。2.3 编译安装 OpenSSLcd /opt/upgrade/openssl-1.1.1w ./config --prefix/usr/local/openssl --openssldir/usr/local/openssl shared zlib make -j$(nproc) make install这里的shared参数很关键它的意思是生成动态链接库.so 文件而不是静态库。如果漏掉shared参数编译出来的 OpenSSL 只有静态库后面 OpenSSH 编译时虽然也能通过但 SSH 服务运行时会遇到动态库加载的问题。zlib参数则是让 OpenSSL 启用 zlib 压缩支持。编译 OpenSSL 的过程中make test这一步如果你没耐心可以直接跳过问题不大。但如果机器上之前装过旧版 OpenSSL且环境变量里LD_LIBRARY_PATH设置过特殊路径编译时可能报undefined reference to ...之类的错误建议先执行unset LD_LIBRARY_PATH清一下环境变量再编译。2.4 配置动态库路径编译完 OpenSSL 后需要让系统能找到新版 OpenSSL 的动态库。这里不要直接改LD_LIBRARY_PATH毕竟它是全局环境变量影响面太大我建议在/etc/ld.so.conf.d/下新建一个文件配置文件echo /usr/local/openssl/lib /etc/ld.so.conf.d/openssl-1.1.1w.conf ldconfig这个操作的本质是告诉系统动态链接器加载程序时除了默认目录还要去/usr/local/openssl/lib找.so文件。如果你跳过这一步后续启动 sshd 时很可能报error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file因为系统默认找不到/usr/local/openssl/lib目录下的动态库文件。2.5 编译安装 OpenSSH终于到正题了。OpenSSH 的配置参数我整理了一套自认为比较稳妥的cd /opt/upgrade/openssh-9.8p1 ./configure --prefix/usr \ --sysconfdir/etc/ssh \ --with-pam \ --with-zlib/usr/local/zlib \ --with-ssl-dir/usr/local/openssl \ --with-md5-passwords \ --with-tcpwrappers \ --without-hardening make -j$(nproc) make install解释一下这些参数的作用--prefix/usr安装路径覆盖掉系统原来的/usr/bin/ssh、/usr/sbin/sshd这样才能真正替换掉旧版本。--sysconfdir/etc/ssh配置文件目录沿用系统默认位置避免权限和上下文问题。--with-pam启用 PAM 认证CentOS 7 默认用 password-auth 做密码认证不开这个参数 SSH 密码登录可能会出问题。--with-zlib/usr/local/zlib指定使用刚才独立编译的 zlib。--with-ssl-dir/usr/local/openssl指定使用刚编译的新版 OpenSSL。--with-md5-passwords允许使用 MD5 哈希的密码。CentOS 7 默认哈希算法是 SHA512但我见过有些老系统迁移上来后/etc/shadow里还残留 MD5 格式的密码条目如果没有这个参数这些用户将无法通过密码登录。--with-tcpwrappers支持 TCP Wrapper 访问控制/etc/hosts.allow和/etc/hosts.deny。如果你没用到这套机制这个参数不加也行。--without-hardening我加这个参数的原因是 CentOS 7 的默认编译工具链相对较老新版 OpenSSH 的某些安全加固编译选项在 CentOS 7 上会导致编译失败比如-fstack-protector-strong在某些老 GCC 版本上有兼容性问题。但这只是权宜之计生产环境如果编译能通过我还是建议不要加这个参数安全加固特性毕竟是好的。编译过程中最常遇到的错误是下面这几种configure: error: *** OpenSSL headers not found说明 OpenSSL 编译安装失败或者--with-ssl-dir路径写错了。collect2: error: ld returned 1 exit status通常是链接阶段找不到某个库文件重点检查/etc/ld.so.conf.d/里有没有把 OpenSSL 的 lib 目录加进去以及有没有执行ldconfig。error: PATH_MAX undeclaredGCC 版本太老导致编译 C 文件时缺少某个头文件定义在CFLAGS环境变量里加上-stdgnu99能解决或者用./configure CFLAGS-stdgnu99这种方式指定。2.6 替换 PAM 配置并设置自启动编译安装完成后有一个步骤特别容易出问题新版本 OpenSSH 安装时会在/etc/pam.d/下生成一个sshd文件但这个文件是默认模板内容很精简和 CentOS 7 原来的sshd配置差别很大。如果你直接用新模板会遇到使用密码登录时提示Permission denied (publickey,password)的诡异故障。正确做法是把编译安装生成的新文件改名恢复我们备份的原版 PAM 配置cp /etc/pam.d/sshd /etc/pam.d/sshd.new.bak cp /etc/pam.d/sshd.bak.$(date %F) /etc/pam.d/sshd这里补充一个细节如果sshd的 PAM 配置文件里没有加载password-auth模块那么即使是 root 用户用密码登录也会被拒绝日志里显示Failed password for root但实际上是 PAM 阶段就挂了。然后设置开机自启动。源码编译安装不会自动注册 systemd 服务需要手动处理cp /opt/upgrade/openssh-9.8p1/contrib/redhat/sshd.init /etc/init.d/sshd chmod x /etc/init.d/sshd chkconfig --add sshd chkconfig sshd on systemctl daemon-reload这里有个提醒如果你用的是systemctl enable sshd可能会报错因为/etc/init.d/sshd是 SysV 风格的 init 脚本虽然 systemd 兼容支持但管理方式略有不同。直接使用 chkconfig 更符合 CentOS 7 的习惯。2.7 修改 sshd 配置并重启服务先验证一下版本确认编译成功/usr/sbin/sshd -V如果显示的是 OpenSSH_9.8p1说明二进制文件已经替换成功。然后修改配置文件重点关注下面几项。vim /etc/ssh/sshd_config以下几项建议确认或修改PermitRootLogin yes PubkeyAuthentication yes PasswordAuthentication yes UsePAM yes X11Forwarding no MaxAuthTries 3MaxAuthTries建议设置成 3防止暴力破解。X11Forwarding绝大多数场景用不到关掉更安全。配置文件的修改逻辑是新版 OpenSSH 的默认配置里PermitRootLogin默认是prohibit-password意思是禁止 root 用密码登录只允许密钥登录。如果你平时习惯用密码登录 root一定要显式改成yes否则升级完必然遇到 root 密码登录失败的问题。重启 sshd 服务systemctl restart sshd systemctl status sshd到这里如果你还能通过 SSH 正常连上服务器恭喜你升级基本成功了。3. 升级后的验证、配置检查与安全加固3.1 验证 SSH 版本与算法协商重启完成后在当前会话窗口不要关闭新开一个终端窗口测试连接ssh -V ssh -Q cipher ssh -Q macssh -Q cipher能列出当前支持的加密算法列表这个输出可以用来和等保测评报告里的算法要求做对照。新版 OpenSSH 默认已经禁用了 CBC 系列算法和 SHA1 MAC如果业务上有老客户端必须用 CBC 算法需要在sshd_config里显式声明Ciphers参数否则会连接失败。3.2 确认 SFTP 功能正常很多人升级完 OpenSSH 后才发现 SFTP 不好使了日志报类似subsystem request failed on channel 0的错误。原因很可能是编译时没有把sftp-server模块安装到正确位置或者配置文件中Subsystem sftp的路径不对。检查方法grep -i subsystem /etc/ssh/sshd_config正常输出应该是Subsystem sftp /usr/libexec/openssh/sftp-server如果找不到这个配置手动补上并确认/usr/libexec/openssh/sftp-server文件存在。文件不存在的话去编译目录里找一般在openssh-9.8p1/sftp-server或安装路径下的/usr/libexec/目录里复制过来即可。关于 SFTP 还有一个隐藏的大坑新版 OpenSSH 默认ChrootDirectory目录的所有者和权限要求非常严格如果你想给某个用户做 SFTP 隔离目录所属的用户必须是 root权限不能超过 755否则客户端会提示Connection closed或者Permission denied之类的错误。很多老配置在旧版 OpenSSH 下勉强能跑升级后直接失效需要重新按新规则调整目录权限。3.3 登录安全加固版本升上去了安全配置也得顺手收紧。我建议在sshd_config里加上下面这些参数Protocol 2 PermitEmptyPasswords no LoginGraceTime 30s MaxSessions 10 ClientAliveInterval 300 ClientAliveCountMax 2ClientAliveInterval 300和ClientAliveCountMax 2组合的效果是如果客户端在 300 秒内没有任何活动数据sshd 会主动发送探测包连续 2 次没有响应就强制断开。这个参数能有效清理那些挂着不活动的僵尸连接减轻 sshd 的资源占用。还有一个很多人不知道的坑新版 OpenSSH 默认启用了UseDNS yes意味着 sshd 会对客户端的 IP 做反向 DNS 解析如果你的 DNS 配置有问题会导致每次 SSH 登录都卡好几秒钟表现为输完密码后要等很久才进系统。没有特殊要求的话建议直接设成UseDNS no。3.4 验证普通用户和 root 用户登录这一步看似多余但我真的遇到过 root 能登录、普通用户登录不上的情况。原因通常是/etc/ssh/sshd_config里有AllowUsers或DenyUsers限制导致普通用户被拒绝。升级后一定要验证这两类账号root 用户通过密码登录。root 用户通过密钥登录如果原本就配置了的话。普通用户通过密码登录。普通用户通过密钥登录。另外如果你平时用密钥登录升级之后发现密钥失效、一直提示输密码大概率不是 OpenSSH 的问题而是~/.ssh/authorized_keys文件的权限不对。新版 OpenSSH 对文件权限检查非常严格~/.ssh目录权限必须是 700authorized_keys文件权限必须是 600用户的 home 目录本身也不能让其他用户可写否则 sshd 会直接忽略这个文件。4. 常见问题与排查技巧实录4.1 编译时报错zlib.h: No such file or directory这个提示是 zlib 开发头文件没有安装或者编译时找不到。检查一下系统里有没有/usr/include/zlib.hyum install -y zlib-devel ls /usr/include/zlib.h如果确认已经安装还报错那就是--with-zlib参数指定的路径不对。你在./configure里指定的--with-zlib/usr/local/zlib是编译后的安装目录但头文件路径要包含include子目录可以用CPPFLAGS-I/usr/local/zlib/include LDFLAGS-L/usr/local/zlib/lib手动指定。4.2 sshd 无法启动报错Missing privilege separation directory: /var/empty/sshd新版 OpenSSH 使用 privilege separation 机制来分离权限如果/var/empty/sshd目录不存在或者权限不对sshd 启动会直接失败。解决方法mkdir -p /var/empty/sshd chmod 755 /var/empty/sshd chown root:root /var/empty/sshd另外还有一个和这个机制相关的操作新版 OpenSSH 的 sshd 在初始化时会在/var/run/sshd创建 pid 文件如果这个目录不存在也会启动失败。执行mkdir -p /var/run/sshd提前创建好。4.3 sshd 能启动但登录被拒绝日志显示Permission denied优先查/var/log/secure。常见的原因有三个第一PAM 配置被替换成默认模板导致认证流程走不下来。检查/etc/pam.d/sshd内容确认是否包含auth include password-auth这一行。第二SELinux 上下文问题。编译安装的文件默认可能是usr_t类型而 CentOS 7 上 sshd 需要的是ssh_exec_t类型。检查命令ls -Z /usr/sbin/sshd restorecon -v /usr/sbin/sshd如果restorecon不生效可以用chcon -t ssh_exec_t /usr/sbin/sshd手动修改或者干脆在/etc/selinux/config里把 SELinux 设为permissive这里我还是建议保持 enable只针对 sshd 做放行策略。第三authorized_keys文件权限不对。这个问题在上面已经说过了不再重复。4.4 密码登录一直提示Permission denied, please try again但密码确实是对的这算是 OpenSSH 升级后最经典的问题。核心原因基本都在 PAM 配置上。比如sshd_config里的ChallengeResponseAuthentication/KbdInteractiveAuthentication参数可能导致密码走 PAM 的交互式键盘输入通道而这个通道的 PAM 模块配置有缺失。试一下把sshd_config里这两行配置改为ChallengeResponseAuthentication no KbdInteractiveAuthentication no然后重启 sshd 再看。如果还不行检查/etc/pam.d/sshd里的这几行是否存在auth include password-auth account include password-auth password include password-auth session include password-auth缺失的话就用authconfig --enablepam重新生成一下 PAM 配置。4.5 升级完 ssh 命令还是旧版本如果你执行ssh -V发现输出的还是 OpenSSH_7.4最可能的原因是 shell 的 PATH 环境变量里/usr/local/bin排在/usr/bin前面而旧版本还在/usr/local/bin或者/usr/local/sbin里残留着。推荐的做法是彻底清理干净旧二进制文件find /usr/local -name ssh* -type f find /usr/local -name sshd* -type f rm -f /usr/local/bin/ssh /usr/local/bin/scp /usr/local/bin/sftp /usr/local/bin/ssh-keygen /usr/local/sbin/sshd然后确认which ssh /usr/bin/ssh -V4.6 sshd 启动时报错error: Bind to port 22 failed: Address already in use这说明旧版 sshd 进程还在运行新进程无法监听 22 端口。解决方法很简单先把旧进程杀掉再启动新的systemctl stop sshd pkill -9 sshd sleep 2 systemctl start sshd但这里有个细节CentOS 7 上如果sshd是通过 socket activationsystemd 的sshd.socket方式启动的即使systemctl stop sshd也可能被 socket 再次拉起。检查一下systemctl status sshd.socket systemctl disable sshd.socket systemctl stop sshd.socket4.7 升级后某些客户端连不上报no matching key exchange method这种情况大多数发生在老客户端比如 Windows 7 自带的 SSH、老版本 PuTTY上。新版 OpenSSH 默认不再支持diffie-hellman-group1-sha1和diffie-hellman-group14-sha1等弱密钥交换算法而在 CentOS 6/7 里的一些老程序用的恰恰是这些算法。如果你确实需要兼容这些老客户端只能在sshd_config里显式启用旧算法KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 Ciphers aes128-cbc,aes256-cbc MACs hmac-sha1注意要在参数前面加上号意思是在默认算法列表的基础上追加而不是覆盖默认列表。但是说实话从安全角度我并不建议这么做因为这些算法本身就是漏洞扫描器重点扫描的对象如果只是为了一两个老客户端而放弃安全性这波升级就没什么意义了。4.8 升级后的连锁问题依赖 libcrypto 的服务异常编译安装新版 OpenSSL 后如果随意修改了系统默认的 OpenSSL 库链接大量依赖libcrypto.so.10的系统程序比如 httpd、nginx 的某些模块、Postfix 等都可能崩溃。我之前就踩过这个坑给一个有 Postfix 的邮件服务器升级 OpenSSH顺手把/usr/lib64/libcrypto.so.10软链指向了新编译的版本结果 Postfix 直接起不来日志里全是error while loading shared libraries: libcrypto.so.1.1。正确的做法是永远不要动系统默认的 OpenSSL 库文件只让 OpenSSH 通过编译参数--with-ssl-dir/usr/local/openssl去使用新版本并且通过/etc/ld.so.conf.d/openssl-1.1.1w.conf只把新版本的动态库路径加进去。这样系统既有 libcrypto.so.10 支撑旧程序又能在/usr/local/openssl/lib找到 libcrypto.so.1.1 供新版 SSH 使用各走各的路互不影响。写在最后根据我个人经验OpenSSH 升级这件事本身并不难真正的难点在于升级之后的“副作用”处理。尤其是 PAM、SELinux、旧算法兼容这三座大山每一个都能让你折腾半天。我现在的固定流程是升级前备份 → 开启 Telnet 保底 → 编译安装 → 恢复 PAM 配置 → 验证登录 → 关闭 Telnet → 检查 SFTP → 检查算法 → 重启服务二次验证。这套流程走下来再没出过大乱子。最后分享一个小技巧升级完成后在 crontab 里加一个监控任务每天检查一下 sshd 版本和运行状态一旦发现异常能第一时间告警通知。很多问题其实不是升级时爆发而是升级几天后某个依赖被 yum 自动更新破坏才冒出来的有监控就能提前发现问题不至于等用户反馈才火急火燎去处理。本文还有配套的精品资源点击获取