背景
前几天有个朋友找我问个问题,说是误删除了 openssl 相关的文件后, 想重新 yum 安装 openssl,但是执行 yum 命令有报错。
我一想 yum 和 openssl 是有点关系,但好像也不是强依赖吧;
于是我就说既然删错了文件,那就把文件恢复回来吧。去其它同版本操作系统的主机上把相同路径的文件拷贝回来就行了。
隔了2天再次找到我,说拷贝回去后还是不行。
于是我就在 el7 虚拟机上测试了一下;
提供的误操作命令为: whereis openssl | xargs rm -rfv
发现还有个目录应该也被删除了,于是提醒他说,注意把目录也给拷贝回去。
又隔了2天,说还是不行,而且已经远程登陆不上去了,需要现场搞了。
于是我简单问了一下情况后,进行了一次复盘和实验测试,然后成为了本次文章的素材案例。
误操作说明
服务器环境是 rhel8.1,物理机。
用户原本的目的是安装一个 python3.7 的版本,
采用编译安装,因为编译时依赖 openssl 库,于是先编译了 openssl 的包;
期间使用的是 root 账户。
步骤大概是这样的
1
2
3
4
5
6
7
8
9
10
11
12
|
tar xf openssl-1.1.1.tar.gz
cd openssl-1.1.1
# 第一个误操作删除动作(或许只是想清理编译文件)
whereis openssl | xargs rm -rfv
# 后续执行内容
./config --prefix=/usr/local/openssl shared zlib
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/openssl/lib" >> /etc/profile
make -j 8
make install
|
实际测试误删除文件时的效果,其实也没有几个文件
1
2
3
|
[root@x85 openssl-1.1.1]# whereis openssl | xargs rm -rfv
removed '/usr/bin/openssl'
removed '/usr/share/man/man1/openssl.1ssl.gz'
|
此时我们来看一下用户所反馈的执行 yum 报错现象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
[root@x85 openssl-1.1.1]# yum
Traceback (most recent call last):
File "/usr/lib64/python3.6/site-packages/libdnf/common_types.py", line 14, in swig_import_helper
return importlib.import_module(mname)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 658, in _load_unlocked
File "<frozen importlib._bootstrap>", line 571, in module_from_spec
File "<frozen importlib._bootstrap_external>", line 922, in create_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
ImportError: libcrypto.so.1.1: version `OPENSSL_1_1_1b' not found (required by /lib64/libssh.so.4)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/bin/yum", line 57, in <module>
from dnf.cli import main
File "/usr/lib/python3.6/site-packages/dnf/__init__.py", line 30, in <module>
import dnf.base
File "/usr/lib/python3.6/site-packages/dnf/base.py", line 29, in <module>
import libdnf.transaction
File "/usr/lib64/python3.6/site-packages/libdnf/__init__.py", line 3, in <module>
from . import common_types
File "/usr/lib64/python3.6/site-packages/libdnf/common_types.py", line 17, in <module>
_common_types = swig_import_helper()
File "/usr/lib64/python3.6/site-packages/libdnf/common_types.py", line 16, in swig_import_helper
return importlib.import_module('_common_types')
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
ModuleNotFoundError: No module named '_common_types'
|
这一步为什么会报这个错, 其实和误删除了 bin/openssl 这个文件没有啥关系,
实际原因是因为 LD_LIBRARY_PATH 环境变量被破坏了
我们来看一下这个环境变量当前是什么
1
2
|
[root@x85 ~]# env | grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=:/usr/local/openssl/lib
|
眼神好的同学一眼就会发现有个地方不大正常, 前面没有值啊;
可能你也想到了 $LD_LIBRARY_PATH 原始值是空的, 于是命令
echo “export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/openssl/lib” » /etc/profile
最终就解释为 :/usr/local/openssl/lib 这样的错误内容了
我们换一种方式来执行呢
1
2
3
4
|
echo "export LD_LIBRARY_PATH=/usr/local/openssl/lib:$LD_LIBRARY_PATH" >> /etc/profile
[root@x85 ~]# env | grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=/usr/local/openssl/lib:
|
当然还是会不正常
别折腾啦,和这个变量的’格式’没有啥关系;
真正的原因
真正的原因是 用户选择的 openssl 版本错误,
rhel8.1 系统自带的 openssl 版本是 1.1.1c-2,
结果本次编译使用源码包是 1.1.1,
1
2
3
4
|
[root@x85 ~]# rpm -qa | grep openssl
openssl-libs-1.1.1c-2.el8.x86_64
openssl-1.1.1c-2.el8.x86_64
openssl-pkcs11-0.4.8-2.el8.x86_64
|
那个 c-2 可不是什么测试版本的意思,而是类似 a,b,c,d…z 递增的小升级或bug修复版本
1.1.1 是2018年9月出的包, 1.1.1c 是2019年5月发布的包,
1.1.1 最新的包是今年2月发布的 1.1.1t。
而操作系统上的其它程序在编译时就指定了依赖是 1.1.1.c 版本的动态库;
如果是依赖库小版本升级的话,一般都会对老版本进行兼容,不影响上层的程序。
可是现在是降级啊, 那就很容易出错了;
解决办法
简单办法就是使用 unset LD_LIBRARY_PATH 命令,取消这个变量的设定也就立即恢复了,
然后再去恢复 procfile 文件。
分析
其实并不影响新用户ssh登陆, 因为 sshd 启动时已经加载完了 openssl 相关的动态库了;
但是我们看一下 sshd 的依赖库,可以发现已经很危险了。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[root@x85 ~]# ldd /usr/sbin/sshd
/usr/sbin/sshd: /usr/local/openssl/lib/libcrypto.so.1.1: version `OPENSSL_1_1_1b' not found (required by /usr/sbin/sshd)
linux-vdso.so.1 (0x00007ffdb33a5000)
libfipscheck.so.1 => /lib64/libfipscheck.so.1 (0x00007f3aa7762000)
libaudit.so.1 => /lib64/libaudit.so.1 (0x00007f3aa7538000)
libpam.so.0 => /lib64/libpam.so.0 (0x00007f3aa7329000)
libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007f3aa7085000)
libcrypto.so.1.1 => /usr/local/openssl/lib/libcrypto.so.1.1 (0x00007f3aa6ba7000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f3aa69a3000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007f3aa679f000)
libz.so.1 => /lib64/libz.so.1 (0x00007f3aa6588000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f3aa635f000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f3aa6148000)
|
yum 里面报错还提到了 /lib64/libssh.so.4, 所以我们可以看到此时已经使用不了 ssh 去连接别的主机。
但因为 sshd 还加载着正确的库文件, 所以还是可以接受用户登陆的。
1
2
|
[root@x85 ~]# ssh root@10.2.3.86
ssh: relocation error: ssh: symbol EVP_KDF_ctrl version OPENSSL_1_1_1b not defined in file libcrypto.so.1.1 with link time reference
|
那么重启 sshd 服务是否会影响新用户登陆呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[root@x85 ~]# ps -ef | grep sshd
root 21341 1 0 00:55 ? 00:00:00 /usr/sbin/sshd -D <省略相关算法支持参数输出>
root 21001 1 0 00:59 ? 00:00:00 sshd: root [priv]
root 21005 21001 0 00:59 ? 00:00:00 sshd: root@pts/0
root 21347 21006 0 01:01 pts/0 00:00:00 grep --color=auto sshd
[root@x85 ~]# lsof -p 21341 | grep libcrypto
sshd 21341 root mem REG 253,3 4273328 420571 /usr/lib64/libcrypto.so.1.1.1c
systemctl restart sshd.service
[root@x85 ~]# lsof -p 21376 | grep libcrypto
sshd 21376 root mem REG 253,3 4273328 420571 /usr/lib64/libcrypto.so.1.1.1c
|
可以看到 即便 重启了 sshd 仍然加载的是旧模块, 为什么呢?
我也不知道,再细究可能需要去看源码吧。
我猜测原因较为可能有2个;
-
运行中的进程由 systemd 重启时并没有去识别的新的环境变量;
(应该不是这个原因, 但这次没有实验进行验证)
-
虽然 libcrypto.so.1.1 不合适,但还有个依赖 libcrypto.so.1 的合适啊;
它俩实际上是同一个文件的软链接,间接找到了正确的 so 库;
所以即便是reboot重启服务器, 也是可以再次ssh登陆主机的。
(感觉有些玄幻,也太不严谨了,也不大可能)
朋友们要是有知道的,也请指教一下。
openssl 版本升级到 1.1.1n
本来到目前为止还是可以 ssh 登陆到这台主机的, 只是不能通过本机去连接外部的主机而已。
但是用户此时似乎也发现了这个问题,于是下载安装了一个新版本 openssl-1.1.1n;
大致操作步骤如下
1
2
3
4
5
6
7
8
|
tar xf openssl-1.1.1n.tar.gz
cd openssl-1.1.n
./config --prefix=/usr/local/openssl
make -j 8
make install
ln -sf /usr/local/openssl/bin/openssl /usr/bin/openssl
|
问题似乎应该到此结束了吧,小的新版本一般都会兼容老版本
1
2
3
4
5
6
7
8
9
10
11
12
|
# sshd 没有报找不到 so 库了
[root@x85 openssl-1.1.1n]# ldd /usr/sbin/sshd
linux-vdso.so.1 (0x00007fffd37d3000)
libfipscheck.so.1 => /lib64/libfipscheck.so.1 (0x00007f66b8638000)
libaudit.so.1 => /lib64/libaudit.so.1 (0x00007f66b840e000)
libpam.so.0 => /lib64/libpam.so.0 (0x00007f66b81ff000)
libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007f66b7f5b000)
libcrypto.so.1.1 (0x00007f66b7a76000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f66b7872000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007f66b766e000)
libz.so.1 => /lib64/libz.so.1 (0x00007f66b7457000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f66b722e000)
|
但是:
yum 依然还是前面的错误, 不能使用;
而且 ssh 到外面还是不行,强依赖这个旧版本;
只是 sshd 目前还比较正常。
1
2
|
[root@x85 ~]# ssh root@10.2.3.86
ssh: relocation error: ssh: symbol EVP_KDF_ctrl version OPENSSL_1_1_1b not defined in file libcrypto.so.1.1 with link time reference
|
这说明了,C系列库的版本依赖性很强,并不只是替换为新版本就可以忽略的。
关于最终不能登陆主机的情况
那么为什么最后又登陆不上服务器了呢?
这个也是我疑惑的地方,
于是朋友准备请我第二天去现场帮忙排查一下,我也欣然答应了去看看。
结果下午被告知这台服务器已经被机房的同事采用重装系统的方式进行恢复了,不用排查了。
好吧,只能脑补可能的原因了。
基于我多年的经验来分析(猜测),
大概率最后登陆不上服务器的原因,有以下几种可能;
- 程序因为异常没有处理好,长时间大量的错误,导致进程数或文件描述符等占用满了;
- 因为各种程序的异常,导致宕机死了或者是 hang 住、僵死、假死等不死不活的状态;
- 长时间不管导致服务器上的错误越来越多,错误日志占满了磁盘,没空间了;
- 还有别的什么操作忘了告诉我,例如真的去把 /usr/lib64 下 openssl 相关的 so 文件给删了或覆盖了。
如果是1和2的话一般重启下主机就能恢复。
最后故障案例的经验总结
- 操作系统底层的包和库,尽量用厂商提供的,不要自己去编译, 因为依赖关系太复杂;
- 如果真需要更新底层的包, 尽量隔离使用;
例如我以前编译升级 openssh 时, 就单独指定新的 openssh和openssl路径, 不动原来的文件。
- 与系统无关的目的,尽量不要使用 root 账号;
例如本次最初目的就只是打算装个 python, 完全可以只使用普通账号编译,指定用户级别或进程级别的 LD_LIBRARY_PATH 环境变量,不与全局发生冲突。
- 故障还是要及时处理, 本次事件如果在最开始两天就处理了,可能也就不会有后面重装的事情了。
- 监控面还是要覆盖到,这次故障分析中,几乎没有数据支持。
- 基础线上操作准则:先测试环境,再生产环境。
- 都 3203 年了,能容器化的,还是容器化吧,至少不用去线上编译。
再次重申系统级别的库升级或修改一定要谨慎和提前测试哦。
举例: 即便使用了新版本的 openssl-1.1.1n, 可 curl, ssh, yum 等工具还是不认的,强版本依赖。
1
2
|
[root@x85 ~]# curl
curl: relocation error: /lib64/libssh.so.4: symbol EVP_KDF_ctrl version OPENSSL_1_1_1b not defined in file libcrypto.so.1.1 with link time reference
|