背景
咸鱼君所在公司的安全部门, 扫描出项目的生产服务安全漏洞,并且很负责任的提供了两份扫描文档,
一份针对nginx(1.1.6),
一份针对php(7.1.3),
并限期一周内改善完!
咸鱼君粗略一扫,全都是服务软件的漏洞,
于是乎, 两个选择摆在眼前:
1.根据文档,按图索骥, 一个补丁一个补丁的“劳模打补丁”;
2.直接升级软件到最新版, 当然, 得确保服务的代码必须要适配!
于是咸鱼君“认真”考虑了一下(实在是懒,补丁太多,当然,也是觉得升级了对应用代码影响不大), 决定升级服务软件来解决漏洞!
下面,详细介绍咸鱼君的升级过程~
评估升级风险!
有相似需求的朋友一定要认真评估“直接升级服务软件版本”方案是否适合自己的业务, 因为服务软件的版本每次升级都会有些变化, 比如php就会舍弃一些函数, 万一项目代码用到了, 结果你直接升级了php
, 最后导致生产的服务代码跑不起来,那就悲剧了!!
评估升级风险的方式,咸鱼君采用的是本地升级服务软件,测试项目代码是否正常~
有条件的也可以搞台克隆生产环境的服务器, 而后测试升级操作~
备份备份备份!
先找到php 的安装目录
whereis php
查看当前版本(7.1.3)
/usr/local/php/bin/php -v
查看当前configure编译参数.这个要保存下,编译新php时要用!
php -i | grep configure
这里列出来的数据格式不对,需要处理一下,建议小伙伴保存在txt中,然后将单引号“ ’ ”替换为空值即可
./configure
--prefix=/usr/local/php
--with-config-file-
path=/usr/local/php/etc
--enable-fpm
--with-mcrypt
--enable-mbstring
--enable-pdo
--with-curl
--disable-debug
--disable-rpath
--enable-inline-optimization
--with-bz2
--with-zlib
--enable-sockets
--enable-sysvsem
--enable-sysvshm
--enable-pcntl
--enable-mbregex
--with-mhash
--enable-zip
--with-pcre-regex
--with-mysqli
--with-gd
--with-jpeg-dir
--with-freetype-dir
--enable-calendar
--with-openssl
--with-png-dir
--with-zlib-dir
--enable-gd-native-ttf
--with-xsl
--with-gettext
--with-pdo-mysql
备份旧版的php文件夹
sudo cp -r /usr/local/php /usr/local/php_7.1_bac
php升级
接下来我们去 https://www.php.net/downloads.php找到合适的稳定的php版本来下载, 记得一定要详细看change log,来决定要不要升级! 否则生产的服务代码因为版本问题挂了,那就悲剧了!! 咸鱼君考察后,选择了 php7.4.4稳定版,粗略一看,应该没问题(事实上后面发现了个小问题!险些悲剧!)
下载
//切换会用户目录(下载文件时先切换自己的目录,这是个好习惯!)
cd ~
wget https://www.php.net/distributions/php-7.4.4.tar.gz
解压
tar -zxvf php-7.4.4.tar.gz
配置编译参数 (注意, 这里有很多问题, 为了流程的连贯性, 建议先往下看,读完全文, 最后再执行, 咸鱼君实力踩坑!)
cd php-7.4.4
//这里的***代表之前保存的编译参数
//但是php7.4.4和之前的有些许不同,所以需要大家先转换!!!!
//务必先不要执行,看完全文再执行,免得踩坑~
./configure --prefix=***
编译
sudo make
安装
sudo make install
恢复配置
cd /usr/local/php_7.1_bac/
sudo cp etc/php.ini /usr/local/php/etc/php.ini
sudo cp etc/php-fpm.conf /usr/local/php/etc/php-fpm.conf
sudo cp etc/php-fpm.d/www.conf /usr/local/php/etc/php-fpm.d/
切换新版php
//平滑重启
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
//当然,有时候急于等待结果的话,可以杀死再重启(不建议)
//咸鱼君为了快速验证,所以采用杀死再重启的方式,小伙伴们视实际情况而定
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
最后,确认服务正常,去访问下生产的服务,是不是正常,如果发现不对,立刻执行以下命令恢复旧php
cd /usr/local
sudo killall php-fpm
sudo rm -r php
sudo cp -r php_7.1_bac/ php
sudo /php/sbin/php-fpm
如果真像上面罗列的升级过程一样简单就好了,事实上,咸鱼君升级php7.4.4的过程中碰到了很多的,并且有些问题是网上找不到明确解答的!, 接下来听咸鱼君细细道来~
php编译期间缺包问题
No package '*******' found
碰到这问题很好解决,缺啥装啥即可!
这里咸鱼君碰到了如下的包缺失问题, 要是你碰到的和我不一样,没关系, 网上这类的问题解决贴一大把,主要因系统环境而异!
No package 'libjpeg' found
sudo yum install libjpeg libjpeg-devel
No package 'libxml-2.0' found
sudo yum -y install libxml2
sudo yum -y install libxml2-devel
No package 'sqlite3' found
sudo yum install sqlite-devel
No package 'oniguruma' found
sudo yum install oniguruma oniguruma-devel
No package 'libpng' found
sudo yum install libpng-devel
以上包都很好解决,下面这个包有点“意思”,
No package 'libzip' found
或
Please reinstall the libzip distributio 或 configure: error: system libzip must be upgraded to version >= 0.11
这个包比较特别, 不知道是不是php7.4.4的适配问题, 咸鱼君发现 yum install 安装的版本比较低,并且下载的1.2.1这个包安装上还是识别不了,只有1.2.0可以用!!!
yum remove libzip libzip-devel //卸载yum安装的低版本
wget https://nih.at/libzip/libzip-1.2.0.tar.gz
tar -xvf libzip-1.2.0.tar.gz
cd libzip-1.2.0
./configure
make
////这句一定要执行,不然编译安装php还是报错找不到!!
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"
sudo ldconfig
sudo make install
php执行时加载“.so”扩展失败问题
咸鱼君安装完各种包,兴冲冲的执行
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
时,发现,报错
PHP Warning: PHP Startup: Unable to load dynamic library “******.so”
好吧, 折腾呗!
根本原因就是, 原来编译安装的扩展, 比如“redis”这类的,只适用于当时的php, 所以我们要想继续使用, 则必须得用新的php重新编译这些扩展,重新安装!
这里注意的时,有些扩展在php的安装包里就有,所以不用额外去下载~
Unable to load dynamic library “ bcmath.so”
cd php-7.4.4/ext/bcmath
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
sudo make
sudo make install
Unable to load dynamic library “redis.so”
wget https://pecl.php.net/get/redis-5.2.1.tgz
tar -axvf redis-5.2.1.tgz
cd redis-5.2.1
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
sudo make
sudo make install
咸鱼君就碰到这两个, 安装完后
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
好吧,再次报错, 原来是新的扩展打上后, 扩展路径和以前不一样了,所以需要修改php.ini, 注意, 安装扩展时,有返回一个安装路径, extension_dir这填充这个路径即可
sudo vim /usr/local/php/etc/php.ini
修改
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20190902/"
最后
cd /usr/local/php/sbin/
sudo killall php-fpm
sudo ./php-fpm
好的,好像没问题了!!(剧透, 接下来有两个大坑!!!)
检查服务正常阶段
看似nginx和php都升级成功了,也不曾有报错了!
接下来我们来到最后阶段, 访问下项目, 看下是不是正常运行!!
大坑一: 验证码不显示!
咸鱼君项目采用的是Laravel5.6 + captcha做的验证码展示, 结果升级后发现,验证码显示不出来, 图片请求全是500错误, 打开Laravel Debug模式,看到报错
Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined function Intervention\Image\Gd\imagettfbbox()
一看, php的gd扩展有问题啊!!!
赶紧去ssh生产,查看gd扩展是不是正常安装的
php -m | grep gd
发现, gd扩展是有的!
这就奇怪了, 随后网上搜索下, 发现了各种“大佬”帖子的“解决方案”
大致方案如下
- php重新编译gd扩展
- 独立下载gd扩展编译安装
………………
好吧, 咸鱼君尝试了, 并没什么用!
这里就不发出这类的“解决方案”了,免得小伙伴们和我一样走“歪路”!
下面是重点了, 解决方案如下
问题原因
其实这个问题的确和 gd 扩展有关系, 主要是和gd扩展的子扩展freetype有关系!
你需要先判断生产环境是不是安装了freetype
//查看freetype安装目录
find / -name freetype
要是安装了, 则编写一个小php脚本来判断有没有正确加载
gdtest.php
<?php
if(extension_loaded(gd)){
echo "can use gd<br>";
foreach(gd_info() as $cate=>$value)
echo "$cate: $value<br>";
}else
echo "can not use gd";
?>
执行
php test.php
结果:
can use gd
GD Version: bundled (2.0.34 compatible)
FreeType Support: #这里冒号后面没有值,说明gd不支持freetype
T1Lib Support:
GIF Read Support: 1
GIF Create Support: 1
JPEG Support: 1
PNG Support: 1
WBMP Support: 1
XPM Support:
XBM Support: 1
JIS-mapped Japanese Font Support:
若“FreeType Support: ”后为空不为“1”则说明,gd库没有freetype支持!
问题似乎陷入了循环,明明安装了freetype, 但是gd扩展却显示不支持??!!!! 搜遍互联网,咸鱼君得到的全都是“重装freetype”,“重装gd扩展”,甚至“重装php”的“解决方案”!
最后,咸鱼君无疑中看到一篇帖子,关于介绍php7.4.4的configure编译配置参数的变化的! 如下
--with-gd 改为 --enable-gd
--with-freetype-dir 改为 --with-freetype
--with-jpeg-dir 改为 --with-jpeg
--with-webp-dir 改为 --with-webp
--with-xpm-dir 改为 --with-xpm
--with-libxml-dir 改为 --with-libxml
--enable-zip 改为 --with-zip
--enable-zlib-dir 改为 --with-zlib
大家看到
--with-freetype-dir 改为 --with-freetype
有没有眼前一亮?!! 是的,真相如此简单, 还记得我们之前
php -i | grep configure
查出的那一堆配置参数么, 我们只需要按图索骥, 把对应的旧版参数替换成新版参数, 然后重新
./configure --prefix=***
make
sudo make install
即可!!!
真是坑爹!
另外,咸鱼君罗列的可能不全, 大家可以自行查看新版的参数
//**代表你需要的扩展参数的关键字,来查看对应的配置方式
./configure -h | grep **
最后编译配置php7.4.4的命令为
./configure
--prefix=/usr/local/php
--with-config-file-path=/usr/local/php/etc
--with-php-config=/usr/local/php/bin/php-configure
--enable-fpm
--enable-bcmath
--with-mcrypt
--enable-mbstring
--enable-pdo
--with-curl
--disable-debug
--disable-rpath
--enable-inline-optimization
--with-bz2
--with-zlib
--enable-sockets
--enable-sysvsem
--enable-sysvshm
--enable-pcntl
--enable-mbregex
--with-mhash
--with-zip
--with-pcre-regex
--with-mysqli
--enable-gd
--with-jpeg
--with-freetype
--enable-calendar
--with-openssl
--with-png
--with-zlib
--with-gd-native-ttf
--with-xsl
--with-gettext
--with-pdo-mysql
大坑二: php7.4.4数组严格验证
满心关系的解决完验证码问题, 登入了项目web后台,却突然发现报错
Trying to access array offset on value of type null
当时, 吓得咸鱼君一身冷汗, 迅速ssh上生产, 先恢复了旧php!
之后,排查得到报错原因
原来php7.4.4移除了很多notice报错,也就是可能老版的php只是warning,notice级别的报错,在新版里全部以error抛出!
Trying to access array offset on value of type null
就是其中的一种, php7.4.4不允许对值为null的数组不进行判空操作直接使用!
举个例子
$arr = //api返回;
$name = $arr['name'];
这样的操作就会抛出异常,所以我们需要先判断
if(isset($arr['name'])){
$name = $arr['name'];
}
可以看出,php也在进行一些类型的严格规范了!
修改了这个问题, php就不抛错了, 咸鱼君再次上线了新版php~~
至此, 顺利从php7.1.3升级至7.4.4!!
希望咸鱼君的升级笔记能够帮到需要的小伙伴~
网友评论