后台收到一个升级工单把一批存量服务器的 OpenSSL 从 1.1.1 系列升到 1.1.1l采用 RPM 方式。这个操作看起来简单——下载个包、rpm -Uvh 一把梭但实际上坑不少。OpenSSL 是系统最底层的加密库一半以上的服务都链着它升级不当轻则服务起不来重则 sshd 都连不上。这篇文章把整个升级流程从头到尾拆一遍包括怎么判断当前版本、怎么选升级方案、怎么构建 RPM、怎么处理依赖、怎么验证和回滚以及我实际踩过的几个典型问题给准备做同样操作的同学一个参考。1. 升级前的版本确认与方案选型1.1 先搞清楚“升的是什么”在动手之前第一步永远是确认现状。很多机器上跑着的 OpenSSL 版本五花八门有的是系统自带的基础版本有的是某个应用自编译的版本甚至同一个系统里可能存在多份 OpenSSL。如果不先摸清楚升级完发现业务用的根本不是系统这个库那就白忙活一场。确认当前版本用一条命令就够了openssl version -a这条命令不只是显示版本号还会把编译参数、OpenSSLDir、证书路径、引擎目录等信息一并打出来。比如你要看它链接的是哪个 libcrypto、前缀目录是什么都能在这里看到。配合which openssl和ldd /usr/bin/openssl基本能定位出系统默认的 OpenSSL 到底是从哪来的、依赖哪些库文件。在 RHEL/CentOS 系系统里正常情况下/usr/bin/openssl对应的是openssl这个 RPM 包核心库文件则分别在openssl-libs和openssl-devel里。用下面一组命令可以快速理清包归属rpm -qf /usr/bin/openssl rpm -qf /usr/lib64/libssl.so.1.1 rpm -q openssl openssl-libs openssl-devel1.2 升级方案选哪个OpenSSL 升级常见的有三种路线第一种是用发行版软件源里的更新包直接更新第二种是到官网下载源码自己编译安装到 /usr/local 下第三种就是把官网源码打成 RPM 包再替换系统包。我这次选的是第三种原因很直接用 RPM 管理可以让系统的 rpm 数据库始终知道 OpenSSL 的状态后续查询、升级、卸载都方便而且不会污染到 /usr/local 这种独立目录。源码编译装到 /usr/local 的方案我最早也试过但它有几个问题很麻烦一是系统原有 RPM 包还在库里同时存在多个 OpenSSLldd检查时到底链到哪个会出现混乱二是 /usr/local 路径的库不在系统的默认搜索路径里很多服务加载不到或者需要手工改 ld.so.conf 和 PATH运维上非常难管理三是卸载时没有统一记录收尾很啰嗦。RPM 方式把这些都规避了——它和系统自带的包是同一套管理机制升级、依赖、回滚都一起管了。不过 RPM 方案也有代价官网源码的 RPM 打包 spec 文件需要自己维护编译参数也得自己确认。后面我会把 spec 的关键写法展开讲。如果现场情况不允许自己构建 RPM也可以先查一下发行版自带的较新版本包是否能满足需求——比如某些仓库里自带 1.1.1k 或 1.1.1l 的更新直接yum update openssl就完事。但前提是仓库里有这个版本而且你的系统还在维护期内。没有现成包的时候自己构建 RPM 就是最可控的路。2. 升级前必做的依赖与风险体检2.1 找出“谁在依赖 OpenSSL”OpenSSL 几乎和系统里一半以上的服务都有关系。升级前必须知道哪些东西链着它否则新包装上去旧服务的动态链接若指向旧库很可能直接起不来。我在实际操作中发现最容易被忽略的是那些并不直接调用openssl命令、但在内部使用 libcrypto 或 libssl 的程序比如 Python、Perl、curl、wget、各种数据库客户端和中间件。收集依赖关系我通常用rpm的反查功能rpm -q --whatrequires openssl-libs rpm -q --whatrequires libssl.so.1.1()(64bit) rpm -q --whatrequires libcrypto.so.1.1()(64bit)--whatrequires会把所有声明依赖这些包的 RPM 都列出来。输出结果通常很长关键是关注那些静态编译、通过网络对外提供服务、或者配置了开机自启的组件。systemctl list-units --typeservice --staterunning配合上面列出的包名交叉比对基本能圈出高风险服务。另外那些用源码编译装到/usr/local的 Nginx、Apache、MySQL 不在 RPM 数据库里rpm 反查看不到。这些要手工用ldd去检查链接目标ldd /usr/local/nginx/sbin/nginx | grep ssl如果看到libssl.so.1.1 /usr/lib64/libssl.so.1.1说明它链接的是系统库升级会影响它如果是/usr/local/openssl/lib/libssl.so.1.1这类路径那它走的是自己的版本升级系统库不直接冲击它但证书路径和加密套件兼容性仍要留意。2.2 预判升级风险点OpenSSL 升级最怕的是 ABI 不兼容和符号缺失。1.1.1 系列内部的小版本升级比如 1.1.1a 升到 1.1.1l基本保持 ABI 稳定风险比跨大版本低很多但不是零风险。服务如果用了比较冷门的 API或者直接调用了部分内部符号小版本升级也可能出现运行时报错。此外还有几个容易被忽略的点证书路径问题新版 OpenSSL 的默认证书目录是/etc/pki/tls/certs如果之前手动改过编译参数或环境变量升级后可能找不到 CA 证书导致 HTTPS 请求失败。引擎模块不匹配有些系统加载了硬件加速引擎或第三方引擎模块这些引擎是和 OpenSSL 版本编译绑定的升级后引擎加载不上会直接报错。编译期的宏定义部分应用在编译时会自动探测 OpenSSL 版本并启用新特性升级后行为变化比如某些加密套件默认开关发生变化。为了尽量降低风险我在实际操作前会做两件事一是找一台配置相同、业务量较小的测试机完整走一遍升级流程二是把当前 OpenSSL 的编译参数完整保存下来openssl version -a | tee /tmp/openssl_before_upgrade.txt后面构建 RPM 时配置参数尽量兼容原有环境的路径和一些关键编译选项避免服务在依赖路径上发生意外变化。3. 构建 OpenSSL-1.1.1l RPM 包的完整过程3.1 准备构建环境和目录结构在 RHEL/CentOS 系系统上构建 RPM 时需要安装 rpm-build、gcc、make、perl 等工具。通常用这个命令一次装齐yum install -y rpm-build gcc gcc-c make perl-core有了 rpm-build 之后会自动生成家目录下的rpmbuild目录结构。如果没生成或想调整可以手动创建标准布局mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}从官网下载 OpenSSL 1.1.1l 的源码包tar.gz 格式放到~/rpmbuild/SOURCES里。这里需要重点注意源码包的校验值一定要核对和官方公布的 SHA256 比对一致后再使用省得建筑过程中被注入额外代码或文件损坏导致编译失败。如果直接用官网 tar.gz 自带的 spec 配置通常还需要额外处理许可证文件、docs 目录等比较繁琐。我实际用的方式是把系统自带的 openssl 源 RPM 里的 spec 文件提取出来然后在此基础上改版本号、修改编译参数这样能最大程度保留系统原有的安装路径和目录习惯。yum install -y yum-utils yumdownloader --source openssl rpm -ivh openssl-*.src.rpm解包后~/rpmbuild/SPECS/下就会出现现成的 openssl.spec。这个文件继承了系统已有的安装习惯比从零写 spec 稳得多。3.2 spec 文件的几个关键改动点提取出来的 spec 文件里需要修改的内容主要有以下几处。版本号位置通常长这样Version: 1.1.1l Release: 1%{?dist}如果是自己打的测试包Release 可以保持1生产环境建议单独加上自定义标记比如1.el7_sec避免和官方未来的升级包冲突。%configure段落里的编译参数是核心。我用的参数尽量贴近系统默认同时又补了几个常用的安全增强项%configure \ --prefix/usr \ --openssldir/etc/pki/tls \ --libdir/usr/lib64 \ shared \ zlib-dynamic \ enable-cms \ enable-md2 \ enable-ec_nistp_64_gcc_128 \ no-mdc2 \ no-ec2m \ no-sm2 \ no-sm4 \ no-ssl3 \ no-ssl3-method \ enable-tls1_3 \ enable-rfc3779 \ enable-idea \ enable-camellia这些参数的含义逐个说清楚--prefix/usr和--libdir/usr/lib64保证安装目录和系统原包一致服务不用改路径就能找到库。--openssldir/etc/pki/tls保持系统原有的 OpenSSL 数据目录证书路径不变化。shared表示构建共享库这是绝大多数应用加载 OpenSSL 的前提。如果只编译静态库后面一堆服务都会出问题。zlib-dynamic让 OpenSSL 动态链接系统的 zlib避免静态链入导致潜在兼容性问题。enable-tls1_3显式开启 TLS 1.3。1.1.1 默认就支持 TLS 1.3但显式写出来能保证编译配置清晰。no-ssl3和no-ssl3-method禁用 SSLv3这个是安全和合规双重要求。如果公司内部有老客户端必须走 SSLv3这里要评估后再改不过业界早就不再建议使用了。enable-ec_nistp_64_gcc_128是在 64 位平台对特定椭圆曲线做优化让 ECDHE 性能更好实测新一点的 CPU 上握手性能会有改善。%install段里要注意保留 openssl 命令和 man page 的安装。有些精简 spec 会漏装文档后续排查问题时 man 手册全没了很不方便。%post和%postun脚本段一般要执行ldconfig -p或ldconfig来刷新共享库缓存。这个细节特别容易被忽略不执行的话即使 RPM 装好了运行时也可能加载不到新的 libssl 库。3.3 使用 rpmbuild 构建并解决卡点一切配置完成后开始构建cd ~/rpmbuild/SPECS rpmbuild -ba openssl.spec-ba表示同时构建二进制 RPM 和源码 RPM。如果你只是想要二进制包也可以改成-bb。构建过程中最常遇到的几个问题我顺便列一下遇到了能少走弯路缺少 perl 模块。OpenSSL 的构建脚本会用到一些 Perl 模块最常见的报错是找不到Text::Template。安装对应 perl 模块包就能解决。缺少which命令。别笑这个在精简容器环境里真是坑。装一下 which 即可。spec 默认要求某些测试依赖可以先临时注释掉%check段跳过测试等主流程跑通后再决定是否补充测试。rpmbuild完成后RPMS/x86_64/目录下通常会有这几个文件openssl-1.1.1l-1.x86_64.rpm openssl-devel-1.1.1l-1.x86_64.rpm openssl-libs-1.1.1l-1.x86_64.rpm openssl-perl-1.1.1l-1.x86_64.rpm3.4 验证 RPM 包的基本信息安装之前用 rpm 工具验证包结构、依赖和签名rpm -K openssl-1.1.1l-1.x86_64.rpm rpm -qp --requires openssl-libs-1.1.1l-1.x86_64.rpm rpm -qpl openssl-libs-1.1.1l-1.x86_64.rpm这里的-qp是在不安装的情况下查询包信息。重点确认两件事一是依赖项是否都能在当前系统满足二是包里包含的.so.1.1文件是否覆盖了原有系统包的文件列表。如果新包缺少某些原包包含的符号链接文件安装后会留下隐患。4. 执行升级安装与系统级验证4.1 安装前的备份和快照准备升级 OpenSSL 这种底层的库必须做好随时回退的准备。建议按下面几层来做第一层弄个虚拟机快照或者容器镜像快照这是最快速的回滚手段。第二层把当前的安装包原样备份出来mkdir -p /root/openssl_backup for pkg in openssl openssl-libs openssl-devel; do rpm -q $pkg /dev/null 21 rpm -e --nodeps --justdb $pkg --noscripts 2/dev/null || true rpm -qa | grep ^$pkg- | xargs -r -n1 rpm -ql /dev/null 21 done其实更稳妥的是直接保留原版本的 RPM 文件。先查出当前版本对应哪个 RPMrpm -qa | grep -E ^(openssl|openssl-libs|openssl-devel)-然后到系统安装源或者 /var/cache/yum 里把对应版本的原 RPM 包拷出来留好。万一升级失败直接rpm -Uvh --oldpackage装回旧包即可。生产环境我还会额外停掉非关键业务、降低负载后再操作尽量避免在业务高峰期做这种底层库升级。4.2 安装顺序和依赖处理升级 OpenSSL 推荐的顺序是openssl-libs先行然后是openssl命令包最后是openssl-devel。原因很简单libs 是基础命令依赖库devel 依赖前面两个。一次性安装更省事cd ~/rpmbuild/RPMS/x86_64 rpm -Uvh openssl-libs-1.1.1l-1.x86_64.rpm \ openssl-1.1.1l-1.x86_64.rpm \ openssl-devel-1.1.1l-1.x86_64.rpm如果报依赖错误比如error: Failed dependencies: openssl-libs(x86-64) 1.1.1k is needed by ...通常是旧包的某些子包没一起升级。这里不建议直接--nodeps强装强装会把系统中依赖旧版本的服务裸奔在“库已经换了但 RPM 校验还不知道”的状态后面想查清楚都难。正确做法是看看报错里缺的依赖是哪个子包把它也加进升级列表里一次装齐。遇到openssl命令正在被占用时rpm 会报file /usr/bin/openssl conflicts或者文本文件冲突。这时先确认是否有进程正占用旧文件、把相关服务暂停再继续升级。4.3 升级后的核心验证项包装完不等于升级完。我用一套标准流程验证确保系统关键功能正常。第一步确认版本和链接库openssl version -a ldd /usr/bin/opensslldd输出里应该指向/usr/lib64/libssl.so.1.1和/usr/lib64/libcrypto.so.1.1并且这些库能被正常加载不能出现not found。第二步用openssl命令做一次基础加解密和连通性测试echo test | openssl enc -aes-256-cbc -e -pass pass:secret | openssl enc -aes-256-cbc -d -pass pass:secret openssl s_client -connect www.example.com:443 -brief /dev/null第一条命令验证加解密功能没有缺失第二条验证 TLS 握手能成功访问公网站点。实际生产环境建议用你的真实业务域名openssl s_client -connect your-api.example.com:443 -servername your-api.example.com同时关注握手协议版本和加密套件是否正常。第三步检查动态链接有没有异常ldconfig -p | grep -E libssl|libcrypto确保 ldconfig 缓存已把新库路径登记好。如果这里看不到新版库赶紧跑一遍ldconfig刷新缓存。第四步检查关键服务状态。优先检查 sshd、Nginx、Apache、Postfix 等与加密链路关系重的服务systemctl status sshd nginx postfix ss -lntp | grep -E :(443|22|25)如果服务启动失败先看日志journalctl -u sshd --since 10 minutes ago --no-pager tail -n 50 /var/log/messages | grep -i error第五步回归测试核心业务接口。这一步必须做不能省。即使是同一版本大版本内的升级加密套件和证书处理也可能出现细微差异。4.4 升级后的一些收尾处理升级完成后还要确认新版 OpenSSL 的配置文件路径不变、证书目录正常。检查一下ls -l /etc/pki/tls/certs/ca-bundle.crt openssl version -d如果OPENSSLDIR变成了/usr/local之类说明编译参数里的--openssldir没设对或没生效需要回看 spec。此时证书链验证大概率会出问题因为系统默认 CA 包路径在/etc/pki/tls新版却去/usr/local找证书。如果系统原本在某些应用里手工配置了自定义 CA 证书路径升级后也要逐个检查别指望默认目录能自动覆盖所有场景。5. 升级过程中遇到的常见问题与排查实录5.1 升级后服务报“libssl.so.1.1: cannot open shared object file”这是升级 OpenSSL 后最常见的错误。用systemctl启动某个服务时报错提示找不到 libssl 的共享库。出现这个问题的原因通常是 ldconfig 缓存没有刷新或者新装的 libssl 文件路径不在动态链接器的搜索范围内。排查步骤如下用find /usr/lib64 /lib64 -name libssl.so.1.1确认库文件是否真实存在。用ldconfig -p | grep libssl看缓存里是否有该库的登记。如果没有登记执行ldconfig刷新如果刷新后还是没有检查文件的符号链接是否完整正常应该存在libssl.so.1.1这个文件libssl.so则通常指向它。最后用ldd /path/to/service_binary | grep ssl逐个检查服务二进制。快捷测试命令ldconfig openssl version -a如果在刷新缓存后依然报错就要看看是不是服务的 systemd unit 里设置了私有/tmp或受限的LD_LIBRARY_PATH这种配置会阻止服务加载系统库。检查/etc/systemd/system/下对应 unit 文件里是否有PrivateTmptrue、EnvironmentLD_LIBRARY_PATH...等配置结合实际情况调整。5.2 编译其他软件时提示找不到 OpenSSL升级之后某些软件在从源码编译时会报类似checking for openssl... no configure: error: OpenSSL not found但明明/usr/bin/openssl存在版本也正确。这里要明白./configure脚本找 OpenSSL 不是靠命令而是靠头文件和库文件。找不到头文件往往是openssl-devel包没装或没升级需要确认/usr/include/openssl/ssl.h是否存在位置是否正确。如果头文件存在那就是库路径问题要把/usr/lib64加进LD_LIBRARY_PATH或者明确指定--with-ssl/usr参数。在 CentOS 7 之类系统上编译某些软件还会遇到openssl-devel依赖旧版本库的怪圈比如error: openssl-devel(x86-64) 1.1.1k is needed by ...。这时把openssl-devel也一起升级到 1.1.1l问题就迎刃而解。5.3 证书校验失败提示 unable to get local issuer certificate升级后如果客户端访问自己公司内部系统时出现证书链验证失败多半是 CA 证书路径或证书格式问题。新版 OpenSSL 对证书链校验更严格对证书扩展项的检查也更仔细以前“能用就行”的证书可能在新版这里就过不去了。排查方向确认当前 OpenSSL 的OPENSSLDIR指向哪里openssl version -d查看。把系统 CA 包更新到最新update-ca-trust。如果是自建 CA 签发的内部证书确认服务器下发了完整的证书链而不仅是站点证书。5.4 多个 OpenSSL 并存时的版本之争系统里同时存在/usr/bin/openssl和/usr/local/bin/openssl时shell 到底执行哪个取决于 PATH 顺序。/usr/local/bin在 PATH 里排在前面时你运行openssl version看到的可能还是旧版本。这类问题看起来像是升级失败其实只是工具路径的问题。解决方式是统一管理不建议长期保留两套平台。如果业务确实需要一个新版 OpenSSL 单独测试把它放到独立目录并通过环境变量引用即可不要把/usr/local/bin直接加到全局 PATH 里。我见过某些同事图省事把/usr/local/bin放到/etc/profile的全局 PATH结果全系统命令版本混乱排查浪费了好几天。5.5 常见问题速查表现象可能原因处理方式服务启动报 libssl 找不到ldconfig 缓存未刷新 / 库文件未生成执行 ldconfig检查 .so.1.1 文件是否存在编译软件找不到 OpenSSLopenssl-devel 未装或版本不匹配安装或升级 openssl-devel 到同一版本HTTPS 证书校验失败OPENSSLDIR 路径不对 / CA 证书未更新检查 openssl version -d执行 update-ca-trustopenssl 命令显示版本没变PATH 里存在旧版本清理 /usr/local/bin 旧包统一系统包升级时报依赖错误子包版本不一致将 openssl-libs、openssl、openssl-devel 一起升级服务启来后 CPU 飙升某些加密套件性能下降对比升级前后加密套件配置调整 cipher 列表6. 升级后还需要关注的几个细节OpenSSL 升级不是装完包就结束的。它像一个底层地基影响范围比表面上看到的广。第一件要关注的是密码学策略。1.1.1 系列对旧加密算法的容忍度比 1.0.2 低得多不少老的 RC4、3DES 套件被移到低位或被禁用。如果系统里还有老旧的客户端程序升级后可能出现握手失败。排查时可以用openssl ciphers -v DEFAULT查看当前默认支持的套件列表对比业务需求做微调。第二件要关注的是应用自身是否缓存了加密库。像 Apache 的 mod_sslNginx 的 http_ssl_modulePython 的一些 C 扩展这些模块在编译时链了 OpenSSL但很多是动态加载的升级系统库后会自动使用新库。可有些应用会把自己的私有 OpenSSL 副本静态链进去这时候升级系统库实际影响不到它需要单独升级应用本身。第三件要关注的是监控。升级后一周内建议把涉及 TLS 握手成功率、证书过期、加密协议版本分布的指标盯一下。我之前遇到过一次升级后某些老用户的内网浏览器访问失败就是因为新版默认不再支持 TLS 1.0而客户端浏览器配置还停留在老版本上。这个问题在升级前没暴露升级完第二天就爆发了还好有灰度窗口及时处理。7. 给准备做 OpenSSL 升级的运维同学的一些体会做了几次 OpenSSL RPM 升级之后我的整体感受是这个事真的不能急也不能图省事直接一条 rpm -Uvh 了事。升级前把环境摸清楚的时间绝对值得花尤其是搞清楚系统里有哪些服务在依赖 OpenSSL、它们各自链的是哪个库。这个东西不出问题则已一出问题就是大面积瘫痪。几个经验性的建议先在完全等价的测试环境完整走一遍包括升级、验证、回滚三个环节都测试不要只测升级不测回滚。构建 RPM 时优先从系统源码包提取 spec在此基础上改版本和参数这样能在最大程度上保留系统原有的目录结构和编译习惯。升级安装时把openssl、openssl-libs、openssl-devel作为一个整体来更新不要只更新其中一个版本错位是最常见的依赖报错来源。装完后不要急着退出把ldconfig、openssl version -a、关键服务systemctl status都过一遍确认链路完整了再继续下一步。回滚方案在动手之前就写好。RPM 方式升级的回滚相对简单保留好旧包发现重大问题后用rpm -Uvh --oldpackage装回去再重启相关服务即可。最后再提一个小细节升级完成后顺手把旧版本的源码包和新构建的 RPM 归档到一个固定目录标注好日期和版本。下次再遇到环境排查、等保检查之类的事情时这些记录能帮你省不少事。个人维护的服务器尤其建议这么做两个月后你可能完全忘了当时升的是什么版本、用了什么参数但归档文件不会骗人。
阅读完成 · 觉得有帮助?