1. 为什么OpenSSH升级让人又爱又怕
做运维这些年,打交道最多的服务之一就是OpenSSH。说它重要吧,它重要到几乎所有服务器远程登录都靠它;说它烦吧,每次升级都像是在走钢丝——改错了配置文件、编译漏了依赖、重启sshd的时候断开会话,分分钟把自己锁在服务器外面。但OpenSSH的软件更新又是绝对躲不开的日常任务,因为这东西和公网、口令、密钥、端口全都直接挂钩,一旦爆出漏洞,影响面往往是全网的。
最近一段时间,不管是CentOS、Alibaba Cloud Linux 3还是OpenEuler,社区里问升级OpenSSH的人明显多了起来。Windows服务器那边也有人在翻怎么把OpenSSH服务开起来。加上macOS用户天天被系统设置右上角那个红点角标提醒骚扰,恨不得立刻去掉。一个个问题看似零散,背后其实是同一件事:大家一边需要保持远程通道安全可靠,一边又不想被更新提示或老旧版本绑住手脚。
我决定把这几年折腾OpenSSH软件更新的经验整理成一篇实操笔记,覆盖几个最常见的场景:macOS去掉软件更新角标、CentOS编译升级、Alibaba Cloud Linux 3用仓库升级、OpenEuler源码编译,以及Windows服务器开启OpenSSH服务。每个场景都给出可直接复制的步骤和参数,同时说清楚每一步背后的原因——理解了为什么,遇到意外情况你才不至于慌。
先说个基本判断:OpenSSH软件更新不是“越新越好”这么简单。新版本一般会带来更严格的密钥算法策略、新的安全加固选项,但也可能改变默认行为,比如某些老的客户端用旧算法就连不上了。所以升级前必须想清楚自己环境里有哪些客户端、哪些脚本依赖旧行为,否则升完级,自动化任务可能全部挂掉。
2. macOS:怎么把软件更新角标彻底去掉
2.1 角标的来源与处理思路
macOS系统设置右上角那个数字角标,来源无非两种:一种是系统显示有可用的macOS系统更新,另一种是某个App在App Store里有待更新项。很多人明明已经把自动更新关了,那个红点还是死赖着不走,其中一个重要原因是系统层面仍然在定期检查更新状态,一旦发现“有更新可用”就立刻标记。
处理思路分两条路走:一条是直接屏蔽系统更新检查,让系统认为“我已经是最新”;另一条是关闭更新提醒通知,让角标不显示。第一类方法更治本,适合那些不打算立刻升级系统、又不想天天被催的人。
2.2 使用softwareupdate命令隐藏指定更新
如果你不想安装某个具体版本,又想让角标消失,可以用softwareupdate命令配合ignore参数。比如我手头这台Mac,系统提示有macOS更新,但驱动和开发环境都还没适配,我用的命令是这样的:
softwareupdate --ignore "macOS Sonoma 14.5"执行完之后,系统设置里那个角标会很快消失。注意这里的“macOS Sonoma 14.5”要和你实际看到的更新名称严格一致,可以先运行softwareupdate --list查看准确的更新标题,然后原样复制。
如果以后想反悔,重新显示这个更新,用reset-ignored参数:
softwareupdate --reset-ignored2.3 彻底关闭自动检查与通知
有人问过我,为什么ignore了之后过两天角标又回来了?因为macOS系统还会定期检查新的更新,一旦发现比当前版本更高的版本,它会重新生成提醒。所以更稳妥的做法是把系统更新检查频率降到最低,或者直接关闭检查。
在第一代Apple Silicon和后续机型上,可以用下面这个命令关闭所有系统自动更新:
sudo softwareupdate --schedule off配合系统设置里的“自动更新”入口,把“检查更新”“下载新更新”“安装macOS更新”这三项全部关掉。还需要把App Store的自动更新也关掉。做了这两步之后,角标基本不会再冒出来。
不过有个细节别忽略:macOS的安全响应更新(Security Response)有时不归常规更新入口管。如果你对安全比较敏感,建议还是让它把安全响应自动装上,只关掉普通系统更新提醒。具体操作是在系统设置的“软件更新”-“自动更新”里,保留“安装安全响应和系统文件”这一项。
2.4 兜底办法与适用范围
如果你的macOS版本比较老,softwareupdate --schedule参数可能不存在,或者App Store里的角标还是顽固,还有一个土办法:在终端里杀掉并重置通知中心对软件更新的缓存。
killall NotificationCenter这个命令只是临时刷新通知,不保证永久有效。说实话,macOS的更新角标本身就是一个用户界面设计上的“催更”机制,不适合跟它硬刚。我的建议是:如果不是急着用新系统,就用ignore把当前这批更新藏掉,等到下个月新版本来了再处理一次,几分钟的事。
3. CentOS环境升级OpenSSH的完整步骤
3.1 先判断你的CentOS该走哪条路
CentOS这边,升级OpenSSH要分情况讨论。CentOS 7还停留在OpenSSH 7.4的老版本上,CentOS Stream 9则一路更新到OpenSSH 8.7p1左右。不同的版本,能用的升级手段完全不一样。
如果你用的是CentOS 7,想通过yum仓库拿到新版本基本是奢望,官方源里的OpenSSH版本被锁死在系统发布时的那一版。最可靠的方法是源码编译升级。如果你用的是CentOS Stream 8或9,情况会好一点,但默认源里的版本也不会特别激进。
在动手之前,先看当前版本:
ssh -V比如我见过很多云服务器镜像里自带的是OpenSSH_7.4p1,这个版本已经相当老了。接下来要决定是否升级到8.x甚至9.x。
3.2 源码编译升级的准备工作
源码编译OpenSSH最大的坑是依赖关系,尤其是OpenSSL和zlib。OpenSSH新版本编译时会检测系统里的OpenSSL版本,如果太老,有些新特性就是编译不进去,比如基于OpenSSL 1.1.1的某些算法支持。CentOS 7自带的OpenSSL是1.0.2,这直接导致很多OpenSSH 8.8以上版本的功能受限或编译报错。
所以我的建议是:能升级OpenSSL就先升级OpenSSL,至少升到1.1.1系列。当然,升级OpenSSL本身又是一摊子事,因为它被系统大量组件依赖。如果不想动OpenSSL,那OpenSSH版本就别盲目追求最新,选一个和OpenSSL 1.0.2兼容的版本,比如8.7p1或8.8p1,这样编译成功率比较高。
编译前准备好依赖:
yum install -y gcc make wget zlib-devel openssl-devel pam-devel krb5-devel perl这里的openssl-devel要和你系统里的openssl版本对应,否则编译的OpenSSH会链接不上。安装完成后,把旧的rpm包保留好做回滚预案。
3.3 编译参数与关键配置
下载源码包并解压后,配置阶段是决定成败的地方。我在CentOS 7上惯用的配置命令是这样的:
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-md5-passwords --with-tcp-wrappers --with-kerberos5=/usr/lib64 --with-privsep-path=/var/empty/sshd解释一下几个关键参数。--prefix=/usr是为了让新sshd覆盖老版本的安装位置,避免系统里同时存在两套二进制文件造成混淆。--sysconfdir=/etc/ssh让配置文件和原有系统保持一致,ssh_config和sshd_config不用迁移。--with-pam是为了保留PAM认证支持,否则密码登录和一些第三方认证插件会失效。--with-md5-passwords是给老系统兼容用的,新环境可以不加。--with-tcp-wrappers对应/etc/hosts.allow和/etc/hosts.deny,如果你还用这套访问控制就得带上。--with-privsep-path指定权限分离目录,默认是/var/empty/sshd,需要提前建好并设置成root所有、权限700。
然后是编译安装:
make && make install安装完成后,把新版sshd覆盖到正确路径,更新/etc/init.d/sshd或者systemd服务里的启动路径。CentOS 7上我一般直接替换/usr/sbin/sshd,并同步/etc/ssh/下的主机密钥权限。千万记得保留旧的二进制文件备用,万一新版本有问题,可以立刻换回去。
3.4 升级后的密码算法兼容调整
这是最容易踩坑的地方:新版本OpenSSH默认禁用了一些旧算法,比如sshd_config里的HostKeyAlgorithms不再包含ssh-rsa,导致老客户端(比如某些旧版PuTTY、旧设备)连不上。
如果你环境里有这类老客户端,需要在/etc/ssh/sshd_config里显式加上兼容配置:
HostKeyAlgorithms +ssh-rsa PubkeyAcceptedAlgorithms +ssh-rsa KexAlgorithms +diffie-hellman-group1-sha1注意,这里是“+ssh-rsa”而不是“ssh-rsa”,加号表示在默认基础上追加。这一点很容易写错,写错了直接变成“只允许这个算法”,反而更危险。
配置改完后测试语法,然后平滑重启:
sshd -t && systemctl restart sshd测试sshd -t之前,建议开一个额外的登录会话窗口,不要把自己锁死。重启完立刻在新的会话里验证登录,确认无误后再关旧会话。
4. Alibaba Cloud Linux 3与OpenEuler的升级路径
4.1 Alibaba Cloud Linux 3走dnf仓库更新
Alibaba Cloud Linux 3默认自带OpenSSH版本比较新,通常在8.7p1左右,日常修漏洞打补丁直接用dnf就能完成,不需要大动干戈走源码编译。
先用下面的命令检查当前版本和可用更新:
ssh -V sudo dnf list available openssh如果发现仓库里有更新,直接升级:
sudo dnf update openssh -y这个方式最省心,因为仓库里的包和内核、PAM、selinux策略都是配套好的,不容易踩依赖坑。Alibaba Cloud Linux 3的仓库更新节奏相对及时,遇到重要CVE会跟进推送。如果你发现仓库里的版本偏旧,可以考虑启用Plus仓库或者extra仓库,但建议优先保持默认源,因为默认源经过了更严格的兼容性测试。
4.2 静态编译高版本OpenSSH的适用场景
有人会问:既然dnf能更新,为什么还要源码编译?答案是有些场景必须用更新的版本,比如等保检查要求OpenSSH版本不低于某个具体值,而仓库版本还没跟上;或者新版本修复了特定CVE,但对应补丁还没进入仓库。
这时候就在Alibaba Cloud Linux 3上做源码编译。它的编译方式和CentOS类似,但有几个地方不同。Alibaba Cloud Linux 3默认OpenSSL是1.1.1g以上,对OpenSSH 8.9以上的支持很好,所以依赖上面省心很多。另外,它的系统默认使用systemd管理sshd,编译安装时要注意服务文件路径。
编译步骤基本一致,配置命令可以做一点简化:
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-privsep-path=/var/empty/sshd make -j$(nproc) sudo make install编译完还要手动替换二进制和服务:
sudo cp /usr/local/sbin/sshd /usr/sbin/sshd sudo systemctl restart sshd4.3 OpenEuler源码编译的坑与对策
OpenEuler是另一个常见场景。OpenEuler 22.03 LTS自带的OpenSSH版本在8.8p1左右,版本不算太老,但等保或内网安全扫描可能要求更高。源码编译OpenEuler时最常遇到的问题有两个。
第一个是configure阶段报错找不到zlib或OpenSSL头文件。OpenEuler用dnf安装依赖时,包名和CentOS不完全一样:
dnf install -y gcc make wget zlib zlib-devel openssl openssl-devel pam-devel krb5-devel perl装完再跑configure,一般就能过。第二个是编译后启动sshd时提示“/var/empty/sshd must be owned by root and not group or world-writable”。这个目录权限问题我几乎每次都能遇到,因为OpenEuler默认可能没有这个目录或者权限不对。解决办法:
sudo mkdir -p /var/empty/sshd sudo chown root:root /var/empty/sshd sudo chmod 700 /var/empty/sshd设置的路径要和configure时的--with-privsep-path参数保持一致。如果configure里没指定,默认就是/var/empty,新版本OpenSSH默认路径可能是/var/empty/sshd,反正都要确保存在且权限正确。
OpenEuler下另一个比较隐蔽的问题是selinux。如果系统开启了selinux enforcing模式,编译安装的新sshd二进制没有对应的selinux策略标签,启动可能直接被拒。排查方法:
sudo ausearch -m avc -ts recent如果看到avc记录是denied,临时放行可以先设置permissive模式:
sudo setenforce 0长期方案是给sshd端口和二进制添加正确的selinux策略,或者写te文件加载。说实话,建议直接把selinux调成permissive,然后认真检查sshd_config的权限和端口绑定,这是很多生产环境实际采用的做法。
4.4 源码编译OpenSSH通用自检清单
不管在哪套Linux上源码编译,升级完都建议按下面这个清单过一遍,可以避免后续很多问题:
- 确认sshd二进制路径。which sshd、sshd -V输出的路径要和systemd服务里的启动路径一致。
- 检查配置语法。sshd -t必须通过,再重启服务,顺序不能反。
- 验证密钥登录。新版本OpenSSH默认禁止空密码登录和某些弱算法,如果你的公钥是用老算法生成的,要提前转换或重新生成。
- 检查PAM模块。升级后密码登录失败,多数是PAM配置路径问题,确认编译时带上了--with-pam,并且/etc/pam.d/sshd文件没有被覆盖成空文件。
- 备份原二进制和配置。至少保留一份旧版sshd和sshd_config.old,出问题能在两分钟内回滚。
5. Windows服务器开启OpenSSH服务的两种方式
5.1 Windows自带OpenSSH的启用方法
Windows Server 2019和Windows 10 1809以后的系统,已经原生集成了OpenSSH Server功能,不需要额外下载安装包,这是最稳妥的路线。
在管理员PowerShell里执行:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0安装完成后,把服务设为开机自启并启动:
Set-Service -Name sshd -StartupType 'Automatic' Start-Service sshd同时确认防火墙规则已经放行22端口。通常安装好功能后会自动创建防火墙规则,名字类似“OpenSSH-Server-In-TCP”。如果没有,手动加一条:
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22连入之后,默认Shell是cmd.exe。如果想换成PowerShell,需要修改注册表里的DefaultShell项,这一步看个人需求。
5.2 通过Win32-OpenSSH项目获取新版
Windows自带的OpenSSH版本通常跟着系统走,微软在Windows组件里维护独立版本,更新节奏比Linux发行版快不了太多。如果你需要更新的版本,或者在老系统(Windows Server 2016/Windows 8.1)上部署,可以考虑微软官方维护的Win32-OpenSSH项目。
下载对应的zip包,解压到C:\Program Files\OpenSSH,然后把目录加入PATH环境变量。用管理员PowerShell执行安装脚本:
powershell.exe -ExecutionPolicy Bypass -File install-sshd.ps1这个脚本会注册sshd和ssh-agent服务。启动前记得修改C:\ProgramData\ssh\sshd_config,默认配置其实就能用,但建议把PasswordAuthentication改成yes,如果你准备用密码登录的话。默认情况下Windows OpenSSH的密码登录是打开的,不过公钥登录需要在管理员用户的.ssh\authorized_keys里配置,路径和权限要求比Linux严格很多。
5.3 Windows端的公钥与权限配置细节
Windows上配置公钥登录很多人会卡在authorized_keys文件权限上。文件路径默认在C:\Users<用户名>.ssh\authorized_keys,需要确保这个文件和目录只有当前用户和SYSTEM有权访问。如果权限不对,sshd会直接忽略这个文件,不会报错,就是登录时仍然要密码。
可以用icacls命令来修复权限:
icacls C:\Users\admin\.ssh\authorized_keys /inheritance:r /grant "管理员用户名:F" /grant "SYSTEM:F"注意“管理员用户名”要替换成实际的Windows账户名。这里最容易出错的是你用了一个带空格的账户名,或者域账户格式没写对。另外,ssh-agent服务也要开启并设置为自动启动,否则密钥转发和某些客户端操作会异常。
Windows下遇到sshd起不来的排查方法也简单:查看系统事件日志里的应用程序日志,找sshd相关的错误,多数是权限问题或端口冲突。如果22端口被占用了,改sshd_config里的Port参数,再去防火墙放行新端口。
6. 升级过程中的常见问题与排查实录
6.1 编译报错No curses library found
这个错误在CentOS和OpenEuler上都可能出现。原因是缺少ncurses-devel,configure阶段检测不到curses库。装一下就好:
yum install -y ncurses-devel # 或 dnf install -y ncurses-devel这个问题不大,但很多人卡在这里是因为报错信息写得比较绕,其实只是缺一个devel包,不算顶层依赖问题。
6.2 升级完密码登录失败,日志无明确提示
这种情况九成是PAM配置被覆盖了。源码安装的OpenSSH默认会在编译目录生成一个sshd_config,安装时也可能覆盖或新建/etc/pam.d/sshd。如果这个文件内容为空或者没有包含系统原有的pam配置,密码认证就会失败。
排查方法:看/var/log/secure或journalctl里有没有pam_unix相关报错。修复办法是比对同系列系统的pam配置模板,或者直接把系统原有配置恢复:
cp /etc/pam.d/sshd.rpmnew /etc/pam.d/sshd 2>/dev/null || cp /etc/pam.d/sshd.old /etc/pam.d/sshd如果没有备份,去同版本的另一台机器上拷贝一份也是可行的。这个坑我踩过不止一次,所以强烈建议在升级前先手动备份/etc/pam.d/sshd。
6.3 老客户端连接时报“Unable to negotiate”
升级后老客户端连不上,通常是算法协商失败。正常的新版本内部已经禁用了一些老旧算法,比如ssh-rsa证书签名和diffie-hellman-group1-sha1密钥交换。日志里一般是“no matching key exchange method found”之类的信息。
解决办法上面的3.4节已经说过,用加号追加老算法。但这里想强调一点:如果你的环境允许,最好逐步淘汰这些老客户端,而不是长期保留弱算法。因为加回去的每一条都是安全风险。我在实际项目里会先确认哪些跳板机或嵌入式设备确实需要老算法,然后把允许的IP范围限定好,再临时开一个低强度算法窗口,等设备升级完就立刻撤掉配置。
6.4 服务启动失败:privilege separation目录权限
这个错误在前面OpenEuler部分提过,但在CentOS上也很常见。提示通常是“/var/empty/sshd must be owned by root and not group or world-writable”。解决办法就是把目录权限改成700,owner设为root。如果是CentOS 7,这个目录默认存在,但升级后可能被uninstall脚本删掉,重新建一下就行。
配置完目录权限后记得重启sshd。如果还是不行,看目录所在的文件系统是不是挂载时启用了noexec之类的选项,那也会导致sshd子进程无法启动。
6.5 Windows端ssh-agent服务未启动导致密钥失效
Windows上如果配置了公钥登录但一直失败,先检查ssh-agent服务状态。Win32-OpenSSH默认会把ssh-agent注册成服务,但不会自动启动。没有agent服务,很多依赖密钥代理的场景(比如跳板机转发)就会失败。
Set-Service -Name ssh-agent -StartupType 'Automatic' Start-Service ssh-agent另外Windows上的管理员账户公钥配置还有一个隐藏坑:OpenSSH默认会检查用户组权限,如果Users组对该用户目录有权限,authorized_keys就不生效。用上面提到的icacls收紧权限,别给Users组任何权限。
6.6 一个适合保存的排查速查表
每次升级OpenSSH,我手上都会有一张速查表,整理成下面的样子,遇到问题直接按顺序排查:
| 现象 | 优先排查项 | 解决思路 |
|---|---|---|
| 编译无法通过 | 缺少devel包 | 安装zlib-devel、openssl-devel、pam-devel等 |
| sshd -t报错 | 配置文件引入了未知选项 | 注释掉新版本不支持的参数,改用兼容写法 |
| 密码登录失败 | PAM配置损坏 | 恢复/etc/pam.d/sshd原始配置 |
| 老客户端连不上 | 算法协商失败 | 追加ssh-rsa、diffie-hellman-group1-sha1等算法 |
| 服务重启后没起来 | 目录权限/selinux/firewall | 检查/var/empty/sshd权限、selinux状态、端口放行 |
| Windows公钥登录失效 | authorized_keys权限 | 用icacls收紧权限,只留用户和SYSTEM |
| Windows端口不通 | 防火墙规则 | 添加OpenSSH-Server-In-TCP规则并检查22端口 |
这张表对我处理生产环境的紧急问题帮助很大,因为它把“现象-原因-解决”串成了一条最短路径,不用每次都从依赖开始查。
7. 个人对OpenSSH升级这门“手艺”的体会
把上面这些场景都折腾过一遍之后,我的感受是:OpenSSH软件更新本质上不是“点个升级按钮”那么简单的事情,它牵涉操作系统底层依赖、认证体系、安全策略和客户端兼容性,是一个典型的“升级链路”问题。很多人觉得难,是因为只盯着OpenSSH一个包,忽略了它和OpenSSL、PAM、selinux、防火墙这些周边组件之间的耦合。
每次升级前,我都会花十分钟把下面这几件事确认完再动手:当前版本是多少、目标版本是多少、中间差了几个大版本、目标版本默认禁用了哪些算法、系统里有没有老客户端必须用这些算法、有没有可用的回滚手段。这十分钟看起来是“浪费”,其实能避免后面两三个小时的排错。
最后再分享一个我个人的习惯:源码编译升级完OpenSSH后,我不会立刻删除旧版本二进制,也不会立刻重启生产环境所有sshd,而是先在一台测试机上完整验证登录、scp、sftp、密钥认证、密码认证、PAM对接这些常规操作,确认无误后再逐渐推送到其他机器。同时更新系统里的/etc/ssh/sshd_config时,尽量用追加配置项的方式而不是直接替换整个文件,这样即使某个参数写错了,回滚也只是删掉几行,而不是恢复整个配置。
OpenSSH这台“门”能不能守好,很多时候不取决于你选了什么版本,而取决于你有没有把升级前后的每一步想明白。希望这篇笔记能帮你把这条路走得稳一点。