首页 / 资讯中心 / 文章详情

CentOS 7 源码编译升级 OpenSSH 8.3p1 实战指南

CentOS 7 源码编译升级 OpenSSH 8.3p1 实战指南 ★ FEATURED ARTICLE
1. 为什么还要折腾源码编译 OpenSSH放在几年前系统自带的 SSH 服务基本够用装完系统就能连谁也不会多看一眼。但这两年安全审计越来越严等保测评、漏洞扫描、基线核查轮番上阵老版本 OpenSSH 的漏洞报告一个接一个往外冒很多单位的内网服务器被扫出高危项整改通知直接拍到运维脸上。CentOS 7 官方源里长期停留在 OpenSSH 7.4 这个版本虽然红帽会回补部分安全补丁但版本号摆在那里扫描器不认甲方不认合规过不了。这时候最直接的办法就是自己动手把 OpenSSH 升级到 8.3p1 这个相对稳定、漏洞面小很多的版本。源码编译安装 OpenSSH 这件事说难不难说简单也绝对不简单。它不像装个 Nginx 那样编译完make install就完事SSH 是服务器的生命线一旦编译参数选错、依赖缺失、权限设置不当或者升级过程中把旧的 sshd 进程干掉又没起来那这台机器就只能去机房插显示器了。我自己第一次做这个操作的时候就因为没保留旧版本回退路径编译出来的 sshd 启动报错远程连接直接断掉最后靠云厂商的 VNC 控制台才救回来。所以这篇内容我会把整个流程拆得很细包括依赖准备、编译参数怎么选、配置文件怎么迁移、服务怎么平滑切换、出问题怎么回滚全部按实战标准来写。这篇文章适合谁看一是手里管着一批 CentOS 7 服务器、被安全合规逼着升级 OpenSSH 的运维人员二是想搞清楚 OpenSSH 编译安装底层逻辑、不想只会yum update的技术爱好者三是正在准备安全基线整改、需要一份可复现操作手册的工程师。我会尽量把每个步骤背后的原因讲清楚让你不仅知道怎么敲命令更知道为什么这么敲出了问题往哪个方向排查。2. 升级前的整体思路与方案选型2.1 为什么不直接用 yum 升级CentOS 7 的官方仓库里OpenSSH 的版本就是 7.4p1哪怕你yum update openssh一百遍版本号也不会变成 8.x。红帽的策略是 backport也就是把高版本的安全补丁移植到低版本上版本号不变但漏洞修复了。问题是很多安全扫描工具只认版本号看到 7.4 就直接标红根本不看你打了多少补丁。这种情况下要么换操作系统要么自己编译。换系统成本太高业务跑了好几年迁移风险大所以源码编译成了最现实的选择。另外还有一种方案是用第三方仓库比如某些社区维护的 RPM 源里面提供了较新版本的 OpenSSH 打包。这个方案我不是没试过确实省事yum install就完事。但问题在于第三方源的信任成本和兼容性风险你不知道打包者用了什么编译参数也不知道依赖关系有没有被改动万一和你系统里其他组件冲突排查起来非常头疼。而且很多生产环境是内网隔离的根本连不上外部源。所以综合来看源码编译虽然麻烦但可控性最强每一个参数都在自己手里出了问题也知道去哪里找原因。2.2 编译安装的核心思路整个升级过程的核心思路可以概括为保留旧版本、编译新版本、平滑切换、可回退。这十六个字听起来简单但每一步都有讲究。保留旧版本的意思是不要急着yum remove openssh而是把旧版本的二进制文件、配置文件、服务单元都备份好。这样万一新版本起不来你可以迅速切回去保证业务不中断。我一般会在/root/backup_ssh_日期这样的目录下把/etc/ssh整个目录、/usr/sbin/sshd、/usr/bin/ssh这些关键文件都复制一份同时记录当前sshd -V的版本信息。编译新版本的时候最关键的是编译参数的选择。OpenSSH 依赖 OpenSSL 和 zlib这两个库的版本和路径直接影响编译结果。CentOS 7 自带的 OpenSSL 是 1.0.2 系列如果你不额外升级 OpenSSL那编译出来的 OpenSSH 就只能用系统自带的。有些高版本 OpenSSH 要求 OpenSSL 1.1.1 以上但 8.3p1 对 OpenSSL 1.0.2 还是兼容的这一点后面会详细说。平滑切换的意思是不要直接覆盖旧文件然后重启服务而是先把新版本编译好、配置好、测试好确认能正常启动和连接之后再替换系统默认的 sshd。这样即使新版本有问题旧的服务还在跑你还有机会补救。2.3 版本选择的考量为什么选 8.3p1 而不是更高的版本这里面有几个考虑。首先8.3p1 是一个相对成熟的版本发布有一段时间了社区反馈的问题基本都暴露出来了坑比较少。其次它对 OpenSSL 1.0.2 的兼容性比较好不需要额外升级 OpenSSL减少了变量。如果你选 9.x 版本很多都要求 OpenSSL 1.1.1 以上那就得连 OpenSSL 一起编译工作量翻倍风险也翻倍。再者8.3p1 修复了之前几个比较严重的安全漏洞对于合规检查来说版本号已经足够新了扫描器不会再标红。当然如果你有特殊需求比如必须用某个新特性那另当别论。但对于大多数 CentOS 7 的存量服务器来说8.3p1 是一个性价比很高的选择。3. 编译前的环境准备与依赖处理3.1 系统信息确认与备份策略动手之前先把家底摸清楚。执行cat /etc/redhat-release确认系统版本uname -r看内核版本ssh -V看当前 OpenSSH 版本。这些信息记录下来后面排查问题的时候有用。我习惯在操作前建一个工作目录比如/root/openssh_upgrade把所有下载的源码包、编译日志、备份文件都放在里面这样不会到处散落事后也好清理。备份这一步绝对不能省。除了前面说的/etc/ssh目录和二进制文件还要备份 PAM 相关的配置文件因为 OpenSSH 编译时会用到 PAM 模块如果 PAM 配置被改动可能导致登录认证失败。具体要备份的内容包括/etc/ssh/整个目录、/etc/pam.d/sshd、/usr/sbin/sshd、/usr/bin/ssh、/usr/lib/systemd/system/sshd.service。把这些东西打包放到一个安全的地方最好再复制一份到另一台机器上防止本机磁盘故障。提示备份的时候顺便记录一下当前 sshd 的启动参数执行ps -ef | grep sshd看看有没有特殊的启动选项比如指定了配置文件路径或者用了非标准端口。这些信息在配置新版本的时候要用到。3.2 依赖包的安装CentOS 7 最小化安装的话很多编译工具都没有需要先装上一批依赖。核心的依赖包括gcc、make、zlib-devel、openssl-devel、pam-devel、rpm-build。如果你打算自己编译 OpenSSL那还需要perl和perl-core。这些包用 yum 装就行命令如下yum install -y gcc make zlib-devel openssl-devel pam-devel rpm-build perl perl-core装完之后验证一下关键库的版本。openssl version看看 OpenSSL 版本rpm -q zlib-devel确认 zlib 开发包已安装。这里有个细节要注意CentOS 7 的 OpenSSL 版本通常是 1.0.2k这个版本编译 OpenSSH 8.3p1 是没问题的。但如果你之前手动升级过 OpenSSL或者系统里存在多个 OpenSSL 版本那编译的时候就要小心了可能会出现头文件和库文件版本不一致的情况导致编译报错或者运行时段错误。3.3 源码包的下载与校验OpenSSH 的源码可以从官方镜像站下载文件名叫openssh-8.3p1.tar.gz。下载的时候尽量用官方渠道下载完一定要校验哈希值防止文件损坏或者被篡改。官方会提供 SHA256 校验值你可以用sha256sum命令对比一下。如果哈希值对不上千万别用重新下载。下载完成后解压tar -zxvf openssh-8.3p1.tar.gz cd openssh-8.3p1解压出来的目录里有个INSTALL文件里面详细写了编译步骤和参数说明建议花十分钟读一遍。很多人跳过这一步直接./configure结果遇到问题再回头翻文档反而更费时间。4. 编译参数详解与实操过程4.1 configure 参数逐个拆解编译 OpenSSH 的核心就是./configure这一步参数选对了后面基本一帆风顺参数选错了要么编译不过要么编译出来的东西不能用。下面是我常用的 configure 命令然后逐个解释每个参数的作用./configure --prefix/usr \ --sysconfdir/etc/ssh \ --with-pam \ --with-zlib \ --with-ssl-dir/usr \ --with-md5-passwords \ --with-privsep-path/var/lib/sshd \ --with-default-path/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin--prefix/usr这个参数决定了安装路径。为什么是/usr而不是默认的/usr/local因为系统自带的 sshd 就在/usr/sbin/sshd我们把新版本也装到这个位置替换的时候直接覆盖就行不用改 systemd 服务文件里的路径。如果你装到/usr/local那服务文件、PATH 环境变量都要跟着改麻烦不说还容易漏掉。--sysconfdir/etc/ssh指定配置文件目录。默认是/usr/local/etc改成/etc/ssh之后新版本会直接读取系统现有的配置文件省去了迁移配置的步骤。不过要注意编译安装不会自动覆盖已有的配置文件它会生成sshd_config等文件如果目标位置已经有同名文件它会保留原文件并生成.default后缀的新文件。这个机制很贴心防止你的配置被意外覆盖。--with-pam启用 PAM 认证支持。这个参数非常重要CentOS 的登录认证、密码策略、账户锁定都是通过 PAM 实现的如果不加这个参数编译出来的 sshd 就不支持 PAM用户可能无法正常登录。加了之后编译时会链接libpam需要确保pam-devel已经安装。--with-zlib启用 zlib 压缩支持。SSH 传输过程中可以压缩数据节省带宽这个参数让 OpenSSH 链接系统的 zlib 库。不加的话编译出来的 sshd 不支持压缩虽然不影响基本使用但传输大文件的时候会慢一些。--with-ssl-dir/usr指定 OpenSSL 的安装路径。CentOS 7 的 OpenSSL 头文件在/usr/include/openssl库文件在/usr/lib64所以指定/usr就行。如果你自己编译了 OpenSSL 装在/usr/local/ssl那这里就要改成对应的路径。这个参数选错的话编译时会报找不到openssl/ssl.h或者链接时找不到libssl。--with-md5-passwords启用 MD5 密码支持。这个参数是为了兼容老系统的密码哈希格式有些老账户的密码是用 MD5 存的不加这个参数可能无法认证。虽然 MD5 本身不安全但作为兼容选项加上没坏处。--with-privsep-path/var/lib/sshd指定特权分离目录。OpenSSH 的特权分离机制需要一个空目录通常用/var/empty/sshd或者/var/lib/sshd。这个目录的权限必须是root:root且755否则 sshd 启动会报错。编译时指定好路径后面就不用再改了。--with-default-path设置默认的 PATH 环境变量。这个参数决定了用户登录后看到的 PATH 是什么。如果不设置默认值可能不包含/usr/local/bin这些常用路径导致用户登录后找不到某些命令。设置成和系统默认一致就行。4.2 编译与安装的实操记录configure 跑完之后如果没有报错会生成 Makefile。接下来执行make开始编译。编译时间取决于机器性能一般两三分钟到十几分钟不等。编译过程中如果出现警告信息大部分可以忽略但如果出现 error那就必须解决。常见的编译错误包括找不到头文件依赖没装全、链接失败库路径不对、语法错误源码损坏。根据错误提示逐个排查就行。编译完成后先别急着make install而是执行make tests跑一下自带的测试用例。这个步骤很多人会跳过但它能提前发现一些运行时问题比如加密算法不可用、PAM 模块加载失败等。测试全部通过之后再执行make install。make install会把编译好的二进制文件、配置文件、man 手册安装到指定路径。安装完成后检查一下/usr/sbin/sshd的版本/usr/sbin/sshd -V如果输出显示OpenSSH_8.3p1说明安装成功。但这时候先别重启 sshd 服务因为新版本的配置文件和旧版本可能有差异直接重启可能导致服务起不来。4.3 配置文件迁移与兼容性处理新版本安装后/etc/ssh/目录下会多出一些.default后缀的文件比如sshd_config.default。这些是新版本的默认配置不要直接覆盖现有的sshd_config而是用diff对比一下两个文件的差异看看有哪些新增的配置项、哪些废弃的配置项。OpenSSH 8.3p1 相比 7.4 有一些配置项的变化比如ChallengeResponseAuthentication被标记为废弃推荐用KbdInteractiveAuthentication替代UsePrivilegeSeparation这个选项在新版本里已经彻底移除了如果旧配置里有这一行启动时会报错。所以迁移配置的时候要逐项检查把废弃的选项删掉把新增的选项按需加上。我一般会保留旧配置的大部分内容只做必要的修改。比如端口号、监听地址、允许登录的用户组、密钥认证方式这些都保持和原来一致。新增的配置项如果不需要就不加保持配置简洁。修改完配置后用sshd -t测试配置文件语法/usr/sbin/sshd -t如果没有输出说明语法正确。如果有报错根据提示修改。5. 服务切换与平滑过渡5.1 保留旧服务并测试新服务这是整个升级过程中最紧张的一步。我的做法是先不碰正在运行的 sshd 进程而是用新编译的 sshd 在另一个端口上启动一个测试实例。比如/usr/sbin/sshd -p 2222 -f /etc/ssh/sshd_config这样就在 2222 端口起了一个新的 sshd不影响现有的 22 端口连接。然后另开一个终端尝试连接 2222 端口ssh -p 2222 userlocalhost如果能正常登录说明新版本的 sshd 工作正常。再测试一下密钥认证、密码认证、SFTP 等功能确认都没问题。测试通过后再用kill命令把测试实例停掉。注意测试的时候千万不要关闭当前已经连接的 SSH 会话否则一旦新服务有问题你就连不上了。最好同时开两个终端一个用来操作一个用来测试连接。5.2 替换系统服务并重启测试通过后就可以替换系统默认的 sshd 了。由于我们编译时--prefix/usr新版本的二进制文件已经覆盖了/usr/sbin/sshd所以不需要额外复制文件。但 systemd 的服务文件可能还是旧的需要检查一下/usr/lib/systemd/system/sshd.service里的ExecStart路径是否正确。通常不需要改因为路径没变。接下来执行systemctl daemon-reload systemctl restart sshd重启之后立刻用另一个终端尝试连接。如果连接成功说明升级完成。如果连接失败不要慌先看 sshd 的状态systemctl status sshd journalctl -u sshd -n 50根据日志报错排查问题。常见的启动失败原因包括配置文件语法错误、特权分离目录权限不对、PAM 模块加载失败、端口被占用等。5.3 回滚方案的设计万一新版本死活起不来你需要一个快速回滚的方案。这就是为什么前面强调要备份旧版本。回滚的步骤很简单把备份的/usr/sbin/sshd复制回去恢复/etc/ssh目录然后重启服务。但前提是你备份的时候旧服务还在运行而且你备份的文件是完整的。我一般会做一个更稳妥的回滚方案在升级之前把旧的 sshd 二进制文件改个名字保留比如/usr/sbin/sshd.old然后把新的 sshd 放进去。如果新版本有问题直接把sshd.old改回sshd就行不用从备份里恢复。同时旧的配置文件也保留一份比如/etc/ssh/sshd_config.old回滚的时候直接覆盖。另外如果你的服务器有带外管理功能比如 IPMI 或者云厂商的 VNC 控制台那回滚就更有底气了。即使 SSH 完全连不上你还能通过控制台进去操作。如果没有带外管理那操作的时候就要格外小心每一步都确认无误再往下走。6. 常见问题排查与避坑经验6.1 编译阶段常见报错编译 OpenSSH 的时候最容易遇到的问题就是依赖缺失。比如报错configure: error: *** zlib.h missing说明zlib-devel没装报错configure: error: *** OpenSSL headers missing说明openssl-devel没装或者路径不对。这类问题好解决把对应的开发包装上就行。还有一种情况是 OpenSSL 版本不兼容。如果你系统里的 OpenSSL 是 1.1.1 系列而 OpenSSH 8.3p1 的某些代码可能不兼容编译时会报一些奇怪的错误。这时候要么降级 OpenSSL要么升级 OpenSSH 到支持新 OpenSSL 的版本。我个人的经验是CentOS 7 就用系统自带的 OpenSSL 1.0.2别折腾兼容性最好。6.2 启动阶段常见报错sshd 启动失败的原因五花八门我整理了一个速查表方便对照排查报错信息可能原因解决方法Missing privilege separation directory: /var/lib/sshd特权分离目录不存在mkdir -p /var/lib/sshd chmod 755 /var/lib/sshdsshd: no hostkeys available主机密钥文件缺失执行ssh-keygen -A生成所有类型的主机密钥Bad configuration option: UsePrivilegeSeparation配置项已废弃从 sshd_config 中删除该行error: Bind to port 22 on 0.0.0.0 failed: Address already in use端口被占用检查是否有其他 sshd 进程在运行或改用其他端口PAM: Authentication failurePAM 配置问题检查/etc/pam.d/sshd是否存在且配置正确主机密钥这个问题特别常见。新版本的 OpenSSH 可能默认不生成某些类型的密钥比如 ED25519而旧版本的密钥文件可能还在。执行ssh-keygen -A会为所有支持的密钥类型生成密钥对如果已存在则不会覆盖。这个命令很安全放心执行。6.3 连接阶段的疑难杂症有时候 sshd 启动正常但客户端连接时报错。比如Unable to negotiate with x.x.x.x port 22: no matching key exchange method found这是因为客户端和服务端支持的密钥交换算法不匹配。新版本的 OpenSSH 默认禁用了一些不安全的算法而老客户端可能只支持这些算法。解决方法是在服务端的sshd_config里加上KexAlgorithms diffie-hellman-group14-sha1,diffie-hellman-group1-sha1但这样做会降低安全性所以只建议在过渡期临时使用等客户端升级后就去掉。还有一种情况是连接被拒绝提示Connection refused。这通常是 sshd 没起来或者防火墙拦截了。检查systemctl status sshd和firewall-cmd --list-all确认服务状态和端口开放情况。6.4 独家避坑经验做了这么多次升级我总结了几条血泪教训。第一条永远不要在业务高峰期做这个操作。升级过程中 SSH 会短暂中断如果正好有自动化脚本或者运维人员在操作可能会出问题。选一个业务低峰期提前通知相关人员。第二条测试环境先跑一遍。如果你有测试环境先在测试环境完整走一遍流程把可能遇到的问题都暴露出来再在生产环境操作。测试环境和生产环境的差异可能很大但至少能帮你熟悉流程。第三条保持一个已连接的 SSH 会话不要断。在重启 sshd 之前确保你至少有一个已经建立的 SSH 连接还开着。这样即使新服务起不来你还能在这个会话里操作把旧服务恢复回去。这个技巧救过我很多次。第四条记录每一步操作。把执行的命令、输出的结果、遇到的问题和解决方法都记下来。一方面方便事后复盘另一方面如果操作失败需要回滚你能清楚地知道改过哪些东西。7. 升级后的验证与收尾工作7.1 功能验证清单升级完成后不能只看版本号变了就完事要做一轮完整的功能验证。我一般会检查以下几项SSH 密码登录是否正常、密钥登录是否正常、SFTP 是否正常、SCP 是否正常、端口转发是否正常、X11 转发是否正常如果用到的话。每一项都实际测试一遍确保没有遗漏。另外检查一下日志里有没有异常报错。journalctl -u sshd --since 10 minutes ago看看最近的日志有没有认证失败、连接异常之类的记录。如果有大量认证失败可能是配置问题或者有人在暴力破解需要进一步排查。7.2 安全加固建议既然都升级到 8.3p1 了顺便把安全配置也加固一下。我通常会做这几件事禁用 root 直接登录PermitRootLogin no、禁用密码认证改用密钥认证PasswordAuthentication no、限制允许登录的用户或组AllowUsers或AllowGroups、修改默认端口虽然不能根本解决扫描问题但能减少噪音、启用登录失败锁定配合 PAM 的pam_tally2或pam_faillock。这些配置在sshd_config里改完记得用sshd -t测试语法然后重启服务。改完之后一定要用另一个终端测试连接确认没问题再关闭当前会话。7.3 后续维护注意事项升级到 8.3p1 之后后续的维护也要跟上。关注 OpenSSH 的安全公告如果有新的漏洞影响 8.3p1及时评估是否需要再次升级。另外如果系统其他组件依赖 OpenSSH 的库文件升级后可能会出现兼容性问题比如某些监控工具、自动化运维工具可能调用ssh命令新版本的命令行参数可能有细微变化需要留意。还有一点源码编译安装的 OpenSSH 不会被 yum 管理所以yum update不会影响它但也不会自动更新它。你需要自己跟踪版本手动升级。建议在运维文档里记录这次升级的详细信息包括编译参数、配置文件改动、回滚步骤方便以后查阅。我个人在实际操作中的体会是源码编译 OpenSSH 这件事技术难度不算高但心理压力大因为一旦失手就是远程失联。所以准备工作做得越充分操作的时候就越从容。备份、测试、保留回退路径这三件事做到位基本不会出大问题。最后再分享一个小技巧如果你实在担心可以在操作之前先配置一个定时任务比如 30 分钟后自动重启旧版本的 sshd这样即使你操作失误导致连不上等定时任务触发旧服务恢复你又能连上了。当然这只是个保险措施用完记得删掉。
阅读完成 · 觉得有帮助?
咨询建站