lnmp虚拟机安全配置研究

    众所周知,虚拟主机的安全不好做,特别是防止跨站成为了重点。apache+php服务器防止跨站的方式比较简单,网上的所有成熟虚拟主机解决方案都是基于apache的,如directadmin、cpanel。

    但如今已然不是apache的时代了,在linux+nginx+mysql+php下怎么防止不同虚拟主机进行跨站?

    首先我们要清楚明白Nginx是怎么运行的,再考虑怎么具体操作吧。乌云知识库里有一篇很好的文章(http://drops.wooyun.org/tips/1323),介绍了nginx的安全配置,大家可以看看。

    nginx实际上只是一个反向代理服务器,它接收到请求以后会看当前请求是否是.php文件,如果是则转交给php-fpm来处理,获得结果后再发给用户。所以有两个权限需要考虑:第一是nginx的权限,第二是php-fpm的权限。如下图,nginx和php-fpm都要读取这个文件,所以权限分配是要考虑的重要一项。

    01.png

    防御跨站要防御的有三点,第一是防止其他用户列网站目录,防止自己的一些敏感文件名被看到及访问;第二是防止其他用户读取自己的文件,防止配置信息泄露;第三就是防止其他用户写shell在自己目录。

    php显然也考虑到了这个问题,其配置文件中的open_basedir,是一个目录列表,只允许php访问其中给出的目录。通过设置这个open_basedir我们就可以防御php读写web目录以外的文件,比如/etc/passwd之类的。

    但现在的问题是,open_basedir是写在php.ini中的一个配置文件,而所有虚拟主机使用的php是同一个php,我们可以防止php访问web目录以外的文件,但是没法防止“虚拟主机1”访问“虚拟主机2”的文件,因为二者都在web目录内。甚至还有一个更大的问题是,很多版本php的open_basedir并不靠谱,能被很容易地绕过。

    这是现在遇到的问题。解决方法就是:让每个虚拟主机用不同用户单独启动php-fpm

    为了实现上面方法,我们需要对安装好的lnmp做些修改。(我使用的就是国内用的比较广的"lnmp一键安装包")。

    比如我们服务器上有两个虚拟主机game01.com和game02.com,其目录分别是/home/wwwroot/game01/和/home/wwwroot/game02/。

    这里说一下,新版的lnmp一键安装包有自带的防跨站功能,是因为php 5.3.3以后,可以在php.ini末尾加上类似如下语句:

[HOST=www.vpser.net]
open_basedir=/home/wwwroot/www.vpser.net/:/tmp/
[PATH=/home/wwwroot/www.vpser.net]
open_basedir=/home/wwwroot/www.vpser.net/:/tmp/


    就可以给不同HOST赋予不同open_basedir。但是我们这里不用这个方法,第一其限制php版本在5.3.3以上,第二open_basedir也是有局限与漏洞的,不能完全依靠这个玩意。所以,虚拟主机创建好以后,来到/usr/local/php/etc/php.ini把这些内容注释掉。(注释符;)


    首先,让不同虚拟机用不同php-fpm运行:

    一、为每个站点创建php-fpm.pid文件

cd /usr/local/php5/var/run
touch php-fpm-game01.pid
touch php-fpm-game02.pid
    二、为每个站点创建php-fpm.conf文件
cd /usr/local/php5/etc/
cp php-fpm.conf php-fpm-game01.conf
cp php-fpm.conf php-fpm-game02.conf
    三、为每个站点建立php-cgi.sock文件
touch /tmp/php-cgi-game01.sock #建立php-cgi.sock文件
chown www.www /tmp/php-cgi-game01.sock #设置文件所有者为www(必须与nginx的用户一致)
touch /tmp/php-cgi-game02.sock
chown www.www /tmp/php-cgi-game02.sock
    四、修改相关文件
vi /usr/local/php5/etc/php-fpm-game01.conf
pid = run/php-fpm-game01.pid
listen =/tmp/php-cgi-game01.sock;

vi /usr/local/php5/etc/php-fpm-game02.conf
pid = run/php-fpm-game02.pid
listen =/tmp/php-cgi-game02.sock;

vi /etc/init.d/php-fpm
vhost=$2
php_fpm_CONF=${prefix}/etc/php-fpm-$vhost.conf
php_fpm_PID=${prefix}/var/run/php-fpm-$vhost.pid
php_opts="-d open_basedir=/home/wwwroot/$vhost/:/tmp/ --fpm-config $php_fpm_CONF"

    上述最后一行,就是php-fpm执行的参数,其中我们将open_basedir设置成了/home/wwwroot/$vhost/:/tmp/,$vhost就是我们运行时传入的第二个参数$2(game01或game02)。

    继续修改

vi /usr/local/nginx/conf/vhost/game01.com.conf # 配置文件名可能不一样,要根据实际情况改变
fastcgi_pass unix:/tmp/php-cgi-game01.sock;

vi /usr/local/nginx/conf/vhost/game02.com.conf 
fastcgi_pass unix:/tmp/php-cgi-game02.sock;

    五.增加开机启动项

    vi /home/start.sh


#!/bin/bash
auto=$1
/bin/bash /etc/rc.d/init.d/php-fpm $auto game01
/bin/bash /etc/rc.d/init.d/php-fpm $auto game02

    chmod +x /home/start.sh

    然后编辑/etc/rc.local 将start.sh加入启动项。



    到此,不同虚拟主机就会以运行不同的php-fpm。我们还需要用不同的用户身份来运行。

groupadd game01
groupadd game02
useradd game01 -M -s /sbin/nologin -g game01
useradd game02 -M -s /sbin/nologin -g game02
    添加了game01.game01和game02.game02两个用户。


    修改/usr/local/php/etc/php-fpm-game01.conf:

listen.owner = game01
listen.group = game01
user=game01
group=game01
    game02同理修改。这样我们就让php-fpm以不同用户来运行了。


    再来到/home/wwwroot/:

cd /home/wwwroot/
chown game01.game01 -R game01
chown game02.game02 -R game02
    将game01和game02文件夹分别给予用户game01和game02。


    再有,我们的nginx是默认以www用户运行的,所以是不能读取game01、game02用户文件的,如果把文件权限设置成777,又不能防止game01读取game02的文件。

    所以,我们应该将www用户加入game01、game02组,再把game01、game02的文件设置成750权限,这样就可以允许www来读取game01/game02的文件(因为在同组,而组权限是5,5就够了),又能防止game01读取game02的文件。

    linux中允许把一个用户加入多个组,所以操作如下:

usermod -aG game01 www
usermod -aG game02 www
    执行以后我们使用“groups www”就能看到www : www game01 game02,www已经在三个用户组里了。


    我们还需要将/usr/local/nginx/conf/nginx.conf里的user www www;修改成user www; 不要带用户组了。

    然后我们可以放心地将game01和game02设置成750:

chmod 750 -R game01
chmod 750 -R game02
    这时候。我们的防御其实有两层。


    01.不同php-fpm运行两个虚拟主机的php程序,他们拥有自己的open_basedir,使之不能跨目录。

    02.即使open_basedir被绕过了,以game01用户身份运行的php-fpm也无法写入、读取game02的文件,因为game02的所有文件权限都是750。其他用户没有任何权限(0)。


    一切设置好以后,说一下使用方法了。

    先kill掉已有的php-fpm,再重启一下nginx,再/home/start.sh启动新的php-fpm即可。

    /etc/init.d/php-fpm start game01 单独启动game01

    /etc/init.d/php-fpm start game02 单独启动game02

    /etc/init.d/php-fpm stop game01 单独启动game01

    /etc/init.d/php-fpm stop game02 单独启动game02

    

    Referer:

    http://drops.wooyun.org/tips/1323

    http://www.dedecms.com/knowledge/servers/linux-bsd/2012/0819/8389.html

    http://yzs.me/2198.html

赞赏

喜欢这篇文章?打赏1元

评论

Tomato 回复

师傅好棒! 学习了,我们都是用apache来做权限划分的 通过httpd-itk

wskwsk 回复

你不觉得有些问题吗

phithon 回复

@wskwsk:什么问题?

wskwsk 回复

@phithon:第四 第五步 估计哪里有问题
折腾了一天,一步步来,但php-fpm服务无法启动,你测试过没有?
如果你确实成功了,我再找一下原因; 如果没测试,我就扔下了

phithon 回复

@wskwsk:我肯定测试了……我现在手里所有vps都是这样配置的。并且我把lnmp官方提供的vhost.sh改成我自己的了。

wskwsk 回复

@phithon:请问一下 你用的lnmp 1.1 还1.0版? 还有nginx php版本,该方法各版本通用吗? 我抽时间再搞一次,谢了

phithon 回复

@wskwsk:就是最新版 http://lnmp.org/ LNMP1.1。应该跟版本没关系才对,原理分析起来很简单,版本应该是通用的,我用debian的官方nginx+php5-fpm也配置过。不同环境,具体操作跟文中可能有所不同,但过程、思路肯定是一样的。如果环境不一样,比如你是centos,很可能操作确实有不一样。

孙睿哲 回复

你怎么老不上QQ呢,,有问题得请教你了,关于server2008的

captcha