每次升级OpenSSH最让人崩溃的往往不是编译报错而是升级到一半SSH连接断了然后发现机器就剩一个黑乎乎的屏幕。尤其现在大多数服务器都是远程运维连个本地终端都没有操作一步错轻则服务起不来重则直接被锁在外面。我这些年帮人处理过多次升级翻车现场也踩过不少坑这篇就把OpenSSH升级前后我踩过的雷和总结出的经验一次性整理出来希望能帮准备动手的同行走得稳一点。先说清楚这篇内容覆盖的范围我不只讲Linux下怎么编译还包括升级之前的版本评估、依赖盘点、回滚方案、升级之后的服务验证以及几个容易忽略的环境差异。标题里提到的Alibaba Cloud Linux 3、openEuler源码编译升级、Windows服务器开启OpenSSH服务、CentOS升级OpenSSH这些高频搜索场景都会逐个说。1. 升级前必须想清楚的版本评估与依赖盘点很多人拿到一个OpenSSH新版本第一步就是下载源码包然后直接configure、make、make install。我也这么干过但后来发现这其实是掉坑最快的路径。OpenSSH不是独立存在的软件它依赖系统底层库也和系统自带的很多组件有耦合不先把老底摸清楚后面出的问题会非常恶心。1.1 先看当前版本和运行状态升级之前第一件事永远是确认当前环境到底长什么样。最少要看三样东西ssh -V cat /etc/redhat-release sshd -T | grep -E ^(port|protocol|ciphers|macs|kexalgorithms)第一条看OpenSSH当前版本号第二条看操作系统发行版版本第三条看当前sshd实际生效的配置项。很多人只知道自己的系统是CentOS还是欧拉不清楚内核、库版本、安全策略这些细节。等编译出来的高版本OpenSSH在低版本glibc上面跑不动的时候才想起来查依赖就晚了。检查结束后顺手看一下sshd服务当前有没有非标准的启动参数systemctl cat sshd ps -ef | grep sshd我知道不少生产环境在sshd.service里加了自定义环境变量或者依赖某个启动脚本如果升级完用新版本的sshd替换了旧的二进制却忽略了服务配置文件里的启动参数那才是真麻烦。1.2 依赖库到底缺什么OpenSSH新版本对依赖库是有最低要求的。比如较新版本依赖OpenSSL 1.1.1以上这在我遇到的老CentOS 7系统里很常见。老版本OpenSSL 1.0.2虽然也能编译但新版OpenSSH的很多加密算法和连接特性会直接不生效等于升了个寂寞。还有一种情况更头疼就是系统里有多个OpenSSL版本共存。我之前在一台自己折腾过的机器上见过/usr/lib64下面放着老版本/usr/local/lib下面又编译了一个新版本。源码编译OpenSSH时configure可能自动找到新版本但运行时动态库搜索路径又是另一套结果就是sshd能起来但连不上报错信息还非常模糊。如果环境是CentOS 7这种系统库比较老的我建议先看下系统的OpenSSL版本和zlib版本openssl version rpm -q zlib然后再确认系统里有没有libcrypto、libssl的动态库缺失问题。还有一个比较容易遗漏的点是PAM开发库如果系统装了PAM认证又没有安装pam-devel编译时可能一切正常但装了新版之后认证直接失效出现密码正确却始终无法登录的诡异问题。1.3 别忽略系统安全策略和内核限制还有一类坑是系统安全策略。SELinux开启的机器上升级完OpenSSH之后如果发现无法登录大概率是标签或者策略的问题。我见过最典型的场景就是机器开了SELinux enforcing从源码编译安装后sshd二进制路径发生了变化原来的上下文标签在新的路径上不存在然后策略就把sshd拦住了。虽然可以通过restorecon或者chcon处理但升级之前就要考虑到这个问题不能等连不上之后再排查。内核层面的限制同样要注意比如文件句柄数、进程数限制虽然大多数系统默认够用但高并发场景下sshd连接数暴增时这些限制就会变成障碍。所以我建议升级之前顺手跑一下ulimit -n cat /proc/sys/fs/file-max这些虽然和OpenSSH本身关系不大但在故障排查时能排除不少干扰因素。2. 升级方式选型发行版仓库升级和源码编译到底该怎么选升级OpenSSH的路径很多至少有三条常见路线直接从发行版仓库的测试源或backport源装新版本、用第三方维护的RPM包、源码编译安装。每条路线都有自己的适用场景没有绝对的好坏但选错了真的很浪费时间。2.1 发行版自带仓库或第三方RPM的场景如果系统是Alibaba Cloud Linux 3、openEuler、CentOS 8以上这类还在维护的发行版优先看它自己的仓库里有没有更新版本的openssh包。这类系统的好处是包经过了发行版的完整测试依赖处理、服务管理、安全策略适配都做得比较完整升级操作基本上就是yum update openssh openssh-clients openssh-server # 或者 dnf update openssh*升级完成之后重启sshd服务重启即可不太容易出现编译安装那种依赖断裂。但这里有个前提就是你的系统仓库里确实提供了新版本的OpenSSH否则你去翻旧源或者第三方源风险就上来了。我很少推荐在核心业务环境里从不可靠的第三方源直接拉OpenSSH的RPM包因为OpenSSH涉及系统认证、加密通信一个来源不明的二进制包万一编译选项带毒或者被篡改后果非常严重。2.2 源码编译适合的场景真正适合源码编译升级的一般只有两种情况一是官方仓库版本太老等不了backport二是需要定制编译参数。比如想启用某些特定的密钥交换算法、想关闭某些有漏洞的算法、或者需要编译进特定的补丁这种情况下源码编译确实更灵活。源码编译的核心流程其实不算复杂wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz tar -xzf openssh-9.8p1.tar.gz cd openssh-9.8p1 ./configure --prefix/usr/local/openssh \ --sysconfdir/etc/ssh \ --with-pam \ --with-zlib \ --with-ssl-dir/usr/local/ssl make -j$(nproc) make install这里需要特别注意的是--sysconfdir/etc/ssh如果这个参数不写配置文件会被装到/usr/local/openssh/etc下面你就得手动迁移配置文件否则新sshd默认不认识/etc/ssh/sshd_config服务起来之后行为完全不对。2.3 老系统升级时的特殊考量CentOS 7这种老系统如果你必须升级OpenSSH我的建议是尽量优先考虑backport方案也就是把高版本OpenSSH的补丁打到系统当前版本上。不过backport其实也是官方和第三方在做自己打补丁难度高一般不太现实。实际更多人会选择从ELRepo或者其他源拉包但前面说了自己评估风险。还有一类常见做法是同时升级OpenSSL。因为新版OpenSSH对OpenSSL版本有要求如果底层OpenSSL还是老版本很多新特性都用不上。但这里有个连锁问题OpenSSL被系统其他组件大量依赖比如curl、wget、各种数据库客户端直接升级OpenSSL很容易把这些组件搞崩。所以如果决定升级OpenSSL一定得提前测。我给个简单对照表大家可以直接参考自己的场景来选环境类型推荐方式理由在维护的发行版Alibaba Cloud Linux 3、openEuler等官方仓库更新依赖完整、风险最低CentOS 7等老系统但不想碰系统库第三方RPM自行评估来源相对省事但来源需谨慎需要定制编译参数或算法策略源码编译灵活可控对OpenSSL有新版本需求源码编译OpenSSL同步升级注意依赖连锁反应这个表格只是我个人的选择倾向不代表唯一答案。生产环境的关键不是选哪条路而是选定之后把每个环节做扎实。3. 保命操作连接保持、备份与回滚方案很多人觉得升级OpenSSH很简单可一旦操作失误最常见的后果就是远程连接直接断开然后就再也连不上了。那我下面就系统讲讲怎么通过连接保持、备份和回滚来保命。3.1 先开一条不会断的保底会话我在实操中几乎从不直接使用当前的SSH会话来执行升级。就算升级顺利服务重启的一瞬间当前会话也会断开只是重连快慢的问题。更好的做法是先开一个screen会话或者tmux会话在里面执行升级操作。tmux new -s upgrade如果tmux没安装先装一下或者用screenyum install -y tmux # 或 yum install -y screen为什么强调这个因为就算中途网络抖动导致你的SSH客户端断连tmux里的升级进程还在继续跑等网络恢复以后再连回去用tmux attach -t upgrade就能重新看到当时的界面。这能避免“断连后升级进程被中断sshd服务处于半死不活状态”的尴尬局面。但是tmux本身不保证服务一定不会崩它只是让你的命令不在断线时被杀掉。所以双保险还要再加一层就是在系统本地再留一个可用的登录终端。如果机器在机房或者有带外管理口优先用带外终端。如果什么都没有那就只能祈祷网络稳定了这也是为什么很多人说升级OpenSSH最好选在业务低峰期。3.2 备份文件要覆盖到什么程度备份最怕的就是只备份了配置文件运行时报错时才想起来二进制也得留着。至少这几样东西必须备份cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %Y%m%d) cp /etc/ssh/ssh_config /etc/ssh/ssh_config.bak.$(date %Y%m%d) cp -r /etc/ssh /etc/ssh.bak.$(date %Y%m%d) # 备份当前sshd二进制及相关库 cp /usr/sbin/sshd /usr/sbin/sshd.bak.$(date %Y%m%d) cp /usr/bin/ssh /usr/bin/ssh.bak.$(date %Y%m%d)这里很多人容易漏掉主机密钥。主机密钥是/etc/ssh/ssh_host_*那一批文件如果不小心删了客户端会在重新连接时看到host key改变提示严重的话会被当成中间人攻击直接拒绝连接。所以在升级前我把主机密钥目录整体拷贝一份日期后缀标清楚。还有一个容易遗漏的是PAM模块相关文件尤其在基于源码编译的情况下编译安装会把pam模块装到新路径但配置文件还是老路径这时一定要先备份/etc/pam.d/sshd。3.3 回滚方案要在升级之前就写进笔记回滚不是一句“把备份拷回去”就完了。你得把整个流程完整过一遍万一新版本真的起不来你要在多久内切回旧版本切回之后服务配置用哪份主机密钥是不是也切回去这些都要提前想清楚。我自己的习惯是升级前在/var/log/记录一个文件把当前sshd的启动参数、配置文件路径、依赖库路径、监听端口、宿主目录全部记录下来。这看起来麻烦但真正遇到紧急情况时你就知道一份完整的“环境快照”有多值钱了。sshd -T /var/log/sshd_config_before_upgrade.txt 21这个文件记录的所有被sshd解析后的实际配置非常有用。升级后如果行为异常直接对比这个文件和新版本的sshd -T输出很快就能定位是哪条参数导致的变化。4. 升级执行中的关键步骤与参数接下来这部分我以源码编译为例子来展开因为包管理器升级相对简单编译升级才是最容易出幺蛾子的。4.1 编译前的依赖安装以常见的CentOS系和欧拉系系统为例编译前需要把开发工具链和依赖库装齐yum install -y gcc make autoconf automake openssl-devel zlib-devel pam-devel libselinux-devel如果你在Alibaba Cloud Linux 3上执行包管理器可能是dnf不过命令差不多。有一回我在一台极简安装的机器上编译忘了装pam-devel结果编译过程完全正常装上之后密码认证怎么都不work。排查了大半天才发现少了这个开发包所以我现在编译前一定会反复确认依赖都装齐。对于需要开启SELinux支持的场景还要确认libselinux-devel存在否则编译出来的sshd默认不带SELinux能力某些情况下服务起不来。4.2 configure参数怎么给才合理我常用的编译参数大概是这样的./configure --prefix/usr \ --sysconfdir/etc/ssh \ --with-pam \ --with-zlib \ --with-md5-passwords \ --with-privsep-path/var/empty \ --with-ssl-dir/usr/local/ssl有几个参数我想特别强调--prefix/usr让二进制装到/usr/bin和/usr/sbin这样PATH和系统默认路径都能找到不用额外改环境变量。如果你喜欢装到独立目录也可以但记得后续命令要写全路径。--sysconfdir/etc/ssh强制让配置目录保持系统默认位置。这个非常关键忘了它会让你升级后到处找配置。--with-privsep-path/var/emptyOpenSSH的老规矩需要一个特权分离目录。很多系统默认已经有这个目录但有时候权限不对。检查一下mkdir -p /var/empty chmod 755 /var/empty如果这个目录权限不对新版sshd启动时可能报错Privilege separation user sshd does not exist或者类似权限相关的报错。configure完成之后看看生成的config.h和Makefile重点是确认PAM支持确实被打开了。如果configure输出的summary里没有PAM相关字样多半是pam-devel没装再回去补。4.3 安装过程和服务切换编译安装这一步本身没什么特别的但有几个服务切换的细节值得注意。安装完成后新版sshd二进制默认会覆盖/usr/sbin/sshd。这时先不要急着重启服务先做配置校验/usr/sbin/sshd -t如果语法有问题它会直接告诉你哪一行配置不对。这里要提醒的是新版本OpenSSH对某些老配置项支持变了比如过时的Cipher、MAC配置格式可能新版本就不认了。sshd -t能把这层问题提前暴露出来。校验通过以后重启服务systemctl restart sshd但这时候有个关键区别如果刚才用make install覆盖了/usr/sbin/sshd而systemd服务文件还是旧的启动时如果指定了-f /etc/ssh/sshd_config这种参数必须确认参数仍然有效。如果不是从systemd启动还要看一眼/etc/init.d/sshd脚本里的路径确保它调用的二进制确实是你新装的那个。进一步验证服务状态systemctl status sshd ss -tlnp | grep :22看到服务正常监听22端口再开一个新的SSH会话尝试登录。这个新会话能够正常登录才算升级真正成功。我建议登录之后立即检查版本ssh -V ssh -Q key确认用的确实是你期望的新版本。5. 升级后最容易翻车的三个环节配置文件、SELinux与PAM服务能正常起来只是第一步真正让人头疼的是升级之后各种表面正常但实际没法用的场景。我总结出三个高频翻车点基本覆盖了我见过的绝大多数问题。5.1 新版本配置项变化高版本OpenSSH移除了一些老旧的加密算法和协议。最典型的就是老的ssh-rsa签名算法如果你之前配置里写了PubkeyAcceptedKeyTypes ssh-rsa这类格式新版可能直接不识别。这时候老客户端会突然连不上但服务端日志里只会出现no matching key exchange method之类的提示。遇到这种情况不要急着把老算法加回去因为加回去等于把安全补丁白升了。正确做法是检查客户端版本老旧的客户端能升级就升级业务系统兼容不了就把密钥升级成ed25519或ecdsa。但如果你的场景确实有兼容性需求可以用以下方式临时兜底PubkeyAcceptedAlgorithms ssh-rsa HostKeyAlgorithms ssh-rsa注意这个号是追加的意思代表在默认算法列表后面补充默认不启用的算法。这是高版本OpenSSH的一种兼容写法但不建议长期保留。还有一处常见变化是UseDNS、GSSAPIAuthentication、AllowTcpForwarding这些老参数新版本即便仍然支持但行为上有细微差别。我升级后都会把/etc/ssh/sshd_config和之前备份配置做一次diff对比一条条确认差异避免未知变更。5.2 SELinux对sshd的限制SELinux是升级后连接失败的高频元凶。尤其当你把OpenSSH安装到一个非标准路径或者把自己编译的sshd二进制放到/usr/local/sbin下时SELinux就很有可能不允许这个新路径上的进程监听SSH端口。遇到这个问题时日志里通常会有avc: denied { name_bind }之类的记录。排查时先看SELinux日志ausearch -m avc -ts recent如果确认是SELinux策略导致的可以给新二进制打上正确标签restorecon -Rv /usr/local/sbin/sshd # 或 chcon -t sshd_exec_t /usr/local/sbin/sshd不过我更推荐的做法是升级前就确认二进制安装路径和系统原路径保持一致也就是在前面configure时使用了--prefix/usr这样SELinux标签不会乱。另外有些情况下是端口不是默认的22比如把Port改成了2222这时SELinux的ssh_port_t标签可能没有覆盖到非标准端口需要额外配置semanage port -a -t ssh_port_t -p tcp 2222这里只是提个思路具体要看你的策略如何配置。5.3 PAM认证失效的问题PAM的问题比SELinux更隐蔽因为它不会导致服务起不来而是表现为“密码怎么输都不对”或者“认证成功但很快断开”。升级之后如果发现密码认证不可用注意检查/etc/pam.d/sshd这个文件是否还存在内容是否完整。很多源码编译版本会把PAM配置安装在/usr/local/openssh/etc/pam.d/导致系统目录下的配置文件缺失或者加载路径变了。如果确认PAM配置还在就用下面方式测试PAM模块本身是否正常/usr/sbin/sshd -T | grep -i pam如果输出里没有pam相关配置说明编译时PAM支持没开启。这个只能重新编译解决。还有一种情况是sshd从旧版升级到新版之后和系统其他PAM模块如pam_faillock、pam_access产生了兼容问题。最典型的例子是老版本里某些模块顺序是合法的新版本认证流程变了以后模块顺序就不对了。调整PAM配置顺序前最好先把sshd日志开起来LogLevel VERBOSE然后看/var/log/secure里具体的PAM报错。日志是定位这类问题的最好工具比盲目改配置靠谱得多。6. Alibaba Cloud Linux 3、openEuler与Windows的差异化补充前面讲了很多通用场景但这几个系统的细节差异也值得单独拿出来聊。6.1 Alibaba Cloud Linux 3上的实测情况Alibaba Cloud Linux 3基于较新内核和系统工具链仓库里自带的OpenSSH版本通常已经比较新。如果只是想修复安全漏洞完全可以直接处理dnf list --showduplicates openssh-server dnf update openssh-server openssh-clients openssh只要仓库里有更新版本就先走系统仓库方案这是我在这类在维护的系统上的第一选择。只有在确实没有满足要求的新版本或者需要自行加补丁时才考虑源码编译。Alibaba Cloud Linux 3的系统库默认版本较新编译OpenSSH通常不太容易遇到依赖问题。要注意的点是它默认的sshd相关SELinux策略和服务管理方式都比较规范如果你用源码编译又指定了不一样的路径反而容易打破原有服务管理的闭环。所以我的建议是尽量保持默认路径。6.2 openEuler源码编译升级的特殊点openEuler也是我经常遇到的系统。它的包管理器是dnf仓库版本更新节奏比较稳但有些特定分支上OpenSSH版本还是不够新需要源码编译。openEuler上源码编译时麻烦一点的是它可能同时带有多版本的OpenSSL库。我在欧拉系统上遇到过编译时链接到了旧OpenSSL的问题。这里的对策是configure的时候可以明确指定./configure ... --with-ssl-dir/usr/lib64/openssl编译完之后用ldd /usr/local/bin/ssh检查动态链接库指向确认链接的OpenSSL库路径是对的。openEuler的PAM配置也可能有版本差异源码编译时需要注意--with-pam参数同时把/etc/pam.d/sshd和系统PAM模块的对应关系确认好。另外一个坑体现在服务重启上openEuler的systemd服务单元有时会在升级后被重置最好升级完成后打开服务单元文件确认ExecStart路径还是不是你期待的那个sshd。6.3 Windows服务器开启OpenSSH服务Windows平台也是经常被搜索到的场景。新版Windows 10、Windows Server 2019及以上版本已经内置了OpenSSH Server功能直接用系统功能打开就行。在Windows上开启服务最常用的是PowerShellGet-WindowsCapability -Online | Where-Object Name -like OpenSSH* Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType Automatic在Windows Server上还可以通过服务器管理器添加“OpenSSH服务器”功能效果一样。打开之后先检查防火墙规则Windows把默认的22端口防火墙规则通常已经加好了但自定义端口的话需要手动添加New-NetFirewallRule -Name sshd-custom -DisplayName OpenSSH Custom Port -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 2222Windows上的OpenSSH配置目录在C:\ProgramData\ssh修改sshd_config之后用Restart-Service sshd重启服务。如果发现连不上一个容易忽略的原因是默认shell没有配置好用户登录时可能被默认到cmd或PowerShell。要修改默认shell需要到管理工具里设置或者用命令行New-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell -Value C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -PropertyType String -Force另外Windows OpenSSH的权限模型和Linux差别很大最大的坑是Windows账户和组的命名规则。比如你需要在sshd_config里指定允许某个用户用户名字段里经常要写完整域用户名或本地用户名主机名格式不对就会导致认证失败。这个报错在系统事件日志里会有记录建议先看事件查看器。Windows的sshd默认不支持那种Linux风格的密钥权限校验放公钥的authorized_keys文件位置和权限设置也有自己的坑。如果按Linux经验把公钥放到C盘某个目录往往会因为访问权限问题不生效。官方推荐将公钥放到C:\Users\用户名\.ssh\authorized_keys同时要确保这个文件只对当前用户和SYSTEM账户可访问否则sshd直接忽略它。7. 升级完之后的验证清单与常见认知误区最后再提一嘴验证环节这也是很多人在升级之后容易匆匆略过的地方。升级完不代表事情结束了至少要做一轮完整的验证我个人的习惯是整理成一个小检查清单。7.1 用检查清单做一轮完整验证服务状态层面systemctl status sshd显示activess -tlnp确认监听端口正确。新会话登录验证至少用两种认证方式各登录一次比如密码认证和密钥认证确保都正常。版本与算法验证ssh -V确认新版本号ssh -Q kex确认密钥交换算法ssh -Q cipher确认加密算法。功能验证sftp能否正常使用、scp能否正常传输文件端口转发是否正常这些日常高频功能都要测一遍。日志检查tail -f /var/log/secure观察有没有新的错误或警告。很多人在第2步就放松了随便开个新会话登录成功就宣布升级完成。结果第二天同事说sftp挂了或者某个老客户端连不上才发现认证算法、兼容性配置没有验证。所以我的建议是凡是升级后可能受影响的任何功能都要实测一遍宁可多花10分钟也不要留过夜问题。7.2 经常被误解的两个升级认知第一个误区OpenSSH新版会淘汰部分老算法但这不代表你要立刻全盘禁用老客户端。如果你所在的网络环境确实还有一堆老系统强行升级然后关闭所有老算法只会造成一堆人喊“连不上”。高版本OpenSSH的配置里保留了一些兼容选项使用sshd -T就能看到所有默认算法和当前配置差异可以以此为据来做最小化降级而不是一刀切。第二个误区只升级服务端就可以了。其实很多“升级后连不上”的问题根源在客户端版本太老。OpenSSH客户端和服务端是配套演进的你服务器升到9.x客户端的算法协商列表如果太老两边握不上手问题并不在服务端配置而是客户端不支持新协商逻辑。所以排查连接问题时先看服务端日志也要同时检查发起连接的客户端ssh -V。我个人在实际排查中还有个习惯就是升级后观察至少一个完整业务周期比如跑24小时看看有没有间歇性的连接异常。有些问题不是立刻暴露的可能是某个定时任务、某个老脚本在特定时间段触发了非标准连接行为这些只有通过长时间观察才能发现。把sshd日志级别调成VERBOSE跑几天然后做一次集中审查往往能提前扑掉很多隐患。
阅读完成 · 觉得有帮助?