Home > Article > Backend Development > Detailed explanation of PHP-FPM's Chroot execution environment, detailed explanation of php-fpmchroot_PHP tutorial
Setting up chroot in PHP-FPM has a good isolation effect and improves system security, but it must It is a bit difficult to establish a reasonable PHP-FPM Chroot environment, which is more troublesome than using tools such as debootstrap. Here, by referring to relevant information, the Chroot execution environment of PHP-FPM is sorted out and shared with everyone.
This article takes Ubuntu 14.04.2 as an example. php-fpm uses the PHP5.6 version provided by ppa:ondrej/php5-5.6, which should be consistent with the php-fpm and system directory structure of the system and the Debian system. of. Please adjust CentOS yourself.
The chroot environment configuration of php-fpm is not related to the server front-end used, nor does it require Apache/Nginx to perform chroot. Of course that's safer - and more complicated.
1. Establish directory structure
The directory selection for chroot is /var/www/chroot, and the page file is placed in /var/www/chroot/public.
Execute the following command to establish the basic directory structure:
bash mkdir -p /var/www/chroot/ cd /var/www/chroot mkdir -p public bin dev tmp usr/sbin/ usr/share/zoneinfo/ var/run/nscd/ var/lib/php5/sessions var/www cp -a /dev/zero /dev/urandom /dev/null dev/ #注3 chmod --reference=/tmp tmp/ chmod --reference=/var/lib/php5/sessions var/lib/php5/sessions #注4 chown -R root:root . #注2 chown -R www-data:www-data public/ #注2 cd var/www ln -s ../.. chroot #注1
The following is the directory structure at this time, and some new things will be added later:
/var/www/chroot/ ├── bin ├── dev │ ├── null │ ├── urandom │ └── zero ├── public ├── tmp ├── usr │ ├── sbin │ └── share │ └── zoneinfo └── var ├── lib │ └── php5 │ └── sessions ├── run │ └── nscd └── www └── chroot -> ../.. #注1
Note 1: This soft link is used to solve the problem that the SCRIPT_FILENAME passed to php-fpm by Apache/nginx cannot find the file after entering the chroot (accessing the php page returns "File not found").
Taking nginx as an example, SCRIPT_FILENAME is usually set to $document_root$fastcgi_script_name, and the script path passed to php-fpm is /var/www/chroot/public/index.php. Since php-fpm is in a chroot environment, the path it actually tries to access becomes /var/www/chroot /var/www/chroot/public/index.php, which of course does not exist.
So use a soft connection to link /var/www/chroot in the chroot environment to the root directory, and you can access the script normally.
Of course you can also set SCRIPT_FILENAME to /public$fastcgi_script_name . However, such hard coding is not conducive to configuration migration and can only be used in a chroot environment. If you switch back to a non-chroot environment, you need to modify the configuration. So it is not recommended to do this. (By the way, there are many old tutorials that do not use $document_root and directly hardcode the root directory, which is of course not advisable)
Note 2: The chroot environment is not 100% safe. Since the execution permission of php-fpm in a chroot environment is www-data, it is still recommended to set the owner of non-essential directories to root to reduce unnecessary access permissions. chroot does not equal security, refer to some principles listed in chroot best practices. From a safer perspective, it is best to remove the read and write permissions of bin, lib, sbin and other directories, leaving only executable permissions, but there is no big difference...
Note 3: In addition to copying the file content, cp -a will also copy the file permissions, mode and other information. It can be easily used to directly copy the three key device files zero, urandom and null. mknod seems to be a more reliable method, but cp -a seems to be fine for me to use.
Note 4: chmod --reference=XXX will refer to XXX's permissions to set subsequent permissions. I won’t mention tmp. The key is that the following var/lib/php5/sessions is the directory where PHP stores session files, and www-data needs to have read and write permissions. It is recommended to take a look at it after setting it up. Of course there will be testing later.
2.PHP-FPM configuration
Create a new php-fpm execution pool to build a chroot environment. It is not recommended to modify php-fpm.conf directly, because this takes effect globally. If there are multiple PHP sites, they will share a chroot environment.
In fact, many php-fpm tutorials ignore the configuration of the php-fpm pool, causing many people to share a set of configurations for all sites on a server, especially a set of php.ini configurations. In fact, Unreasonable. The pool should be created separately according to the needs of the site and the parameters should be adjusted in it.
Create a new chroot.conf under /etc/php5/fpm/pool.d/ (note that it must end with .conf to be called by php-fpm.conf):
[chroot] user = www-data group = www-data listen = /var/run/php-chroot.sock listen.owner = www-data listen.group = www-data pm = dynamic pm.max_children = 5 pm.start_servers = 1 pm.min_spare_servers = 1 pm.max_spare_servers = 3 chroot = /var/www/chroot chdir = /public ;security.limit_extensions = .php php_flag[display_errors] = on php_value[date.timezone] = Asia/Hong_Kong ;php_admin_value[session.gc_probability] = 1 ;php_admin_value[open_basedir] = "/tmp/:/public/:/var/www/chroot/public/"
The previous parameters are relatively familiar. You only need to simply set chroot to the configured environment root directory to enable chroot. After testing it by executing php5-fpm -t, use service php5-fpm reload to enable the new pool. Of course, the backend must be set up in the corresponding configuration of Apache/nginx.
Mention the last few lines. The fourth to last line turns on display_errors so that you can test the function of PHP under chroot later. Remember to comment it out after testing.
Set session.gc_probability to allow the PHP process to delete and recycle the session by itself. Normally, the session is cleaned by the cron task added by PHP, but it seems that PHP will not automatically clean the session in the chroot environment. Of course, you can also add an automatically executed script under cron.d to clean it yourself, so there is no need to turn on this option.
3. Repair various functions of PHP in Chroot environment
Create a new test.php under /var/www/chroot/public and write the following content:
Copy code The code is as follows:
php
af73191af90f8418d47ecc038a27bf24 (0x00007fff779fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f165620f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f16567fc000)
第一条那个只列了个文件名,=>后面也没有文件的基本上都是不用管的。剩下的库文件基本的原则是如果列出的是/lib64,就拷贝到chroot环境下的/lib64,如果列出的是/lib,虽然有很多发行版,大部分库文件包括libc.so是在/lib/x86_64-linux-gnu/目录下的,也直接拷贝到chroot环境的/lib目录下即可,是可以正常找到的。
但是!
前面那句 “必须存在/bin/sh,一个基本的shell” 其实并不是真的,对于mail()只要有一个能接受-c参数调用后面的命令的程序就可以了。所以Kienzl写了这样一个程序:
c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define MAXARG 64 int main( int argc, char* const argv[] ) { char* args[ MAXARG ] = {}; if( argc < 3 || strcmp( argv[1], "-c" ) != 0 ) { fprintf( stderr, "Usage: %s -c <cmd>\n", argv[0] ); return 1; } { char* token; int i = 0; char* argStr = strdup( argv[2] ); while( ( token = strsep( &argStr, " " ) ) != NULL ) { if( token && strlen( token ) ) args[ i++ ] = token; if( i >= MAXARG ) return 2; } } return execvp( args[0], args ); }
保存成sh.c执行: gcc sh.c -o sh -static 然后把sh拷贝到chroot环境的/bin目录下即可。
这样一个不完全的shell从一定程度上也算是增强了chroot环境的安全性了。
方法1:使用mini_sendmail
mini_sendmail似乎专为chroot环境而生。调用mini_sendmail后,它会转而访问本机的25端口,通过本机的邮件服务来发送邮件。所以如果主环境有安装postfix/exim4等邮件服务的话可以使用mini_sendmail来在chroot环境中发送邮件,这是最简单的方法。
mini_sendmail的安装很简单:
复制代码 代码如下:
bash
wget http://www.acme.com/software/mini_sendmail/mini_sendmail-1.3.8.tar.gz
tar zxf mini_sendmail-1.3.8.tar.gz
cd mini_sendmail-1.3.8
make
cp mini_sendmail /var/www/chroot/usr/sbin/sendmail
最后一行自行修改chroot环境的目录。切记要拷贝到chroot环境的/usr/sbin目录下并且命名为sendmail。否则的话要在pool里自行设置ini参数的sendmail_path来指导php找到sendmail程序。
由于mini_sendmail默认就是静态链接,所以也无需拷贝其它的库文件了。
方法2:使用ssmtp/msmtp
对于本机没有安装邮件服务的情况,就不能使用mini_sendmail了。ssmtp和msmtp都支持把接收到的邮件发送请求转而通过其它SMTP服务器来发送。需要注意的是由于ssl支持需要更多更复杂的库文件和配置,所以不建议为两者编译ssl支持……下面以ssmtp为例介绍一下。
bash wget ftp://ftp.debian.org/debian/pool/main/s/ssmtp/ssmtp_2.64.orig.tar.bz2 tar jxf ssmtp_2.64.orig.tar.bz2 cd ssmtp_2.64 ./configure --prefix=/ #别忘了prefix make #千万别手抖make install cp ssmtp /var/www/chroot/usr/sbin mkdir -p /var/www/chroot/etc/ssmtp cp ssmtp.conf revaliases /var/www/chroot/etc/ssmtp #配置文件 cd /var/www/chroot/usr/sbin ln -s ssmtp sendmail
同样记得ldd然后把对应的库文件拷贝过去。另外ssmtp需要/etc/passwd和/etc/group,如果上面没有使用nscd需要拷贝/伪造这两个文件。
ssmtp需要配置。ssmtp.conf文件配置如下:
bash root=admin@example.com #其实这行好像可以乱写 mailhub=smtp.example.com #smtp服务器地址 hostname=myexample.com #此处的hostname似乎会用于产生默认的“root@myexample.com”形式的发件人地址 AuthUser=admin@example.com #此处使用真实的登录用户名 AuthPass=password #密码 FromLineOverride=YES #允许改写发件人
revaliases里配置每个用户在使用ssmtp时使用的“发件人”地址和smtp服务器地址。可以不配置,但是文件要有。具体格式是:
复制代码 代码如下:
bash
# 本地用户名:发件人地址:smtp服务器[:端口(默认25)]
root:admin@example.com:smtp.example.com
www-data:noreply@example.com:smtp.example.com
可以使用chroot(指真正的chroot命令)做个测试:
复制代码 代码如下:
bash
chroot /var/www/chroot /bin/sh #此时/bin/sh一定要是真正的shell
echo "Subject: test"|sendmail -v username@server.com #替换邮件地址为自己的
此时php的mail()函数应该就可用了。
4.其它问题
配置完chroot环境后记得将php的pool设置里display_error关闭。
MySQL的连接可能会遇到问题 ,因为如果填写localhost的话php会试图寻找MySQL的unix socket来访问mysqld。填写127.0.0.1通过TCP连接就没有问题了
完成后的目录结构,以我为例给大家参考一下:
/var/www/chroot/ ├── bin │ └── sh ├── dev │ ├── null │ ├── urandom │ └── zero ├── etc │ └── ssmtp │ ├── revaliases │ └── ssmtp.conf ├── lib │ └── libc.so.6 ├── lib64 │ └── ld-linux-x86-64.so.2 ├── public ├── tmp ├── usr │ ├── sbin │ │ ├── sendmail -> ssmtp | │ └── ssmtp │ └── share │ └── zoneinfo │ ├── 大量时区的目录结构 │ └── zone.tab └── var ├── lib │ └── php5 │ └── sessions ├── run │ └── nscd │ ├── nscd.pid │ └── socket └── www └── chroot -> ../..
以上就是本文的全部内容,希望大家喜欢。