PHP项目chroot相关内容
# 1,PHP 的 chroot 配置
有时候开启了 chroot 的项目,偶尔请求外部域名,会有报错,反馈 77 的 code,一般就是里边依赖库的问题,重新执行一波下边的操作即可。
一些目录的创建
mkdir -p /data/www
mkdir /data/www/usr/
mkdir /data/www/usr/local/
mkdir /data/www/etc/
mkdir /data/www/usr/share/zoneinfo/Asia/
rsync -al /dev/null /data/www/dev/
rsync -al /dev/random /data/www/dev/
rsync -al /dev/urandom /data/www/dev/
rsync -al /dev/zero /data/www/dev/
rsync -al /lib /data/www/
rsync -al /lib64 /data/www/
rsync -al /usr/lib /data/www/usr/
rsync -al /usr/lib64 /data/www/usr/
rsync -al /usr/local/lib /data/www/usr/local/
rsync -al /usr/local/lib64 /data/www/usr/local/
rsync -al /etc/pki /data/www/etc/
rsync -al /etc/ssl /data/www/etc/
rsync -al /etc/resolv.conf /data/www/etc/
rsync -al /usr/share/zoneinfo/Asia/Shanghai /data/www/usr/share/zoneinfo/Asia/
mkdir /data/www/tmp
chmod 1777 /data/www/tmp
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
另外一种方式可通过本机挂载来设置:
原来:
rsync -al /dev/null /data/www/dev/
rsync -al /dev/random /data/www/dev/
rsync -al /dev/urandom /data/www/dev/
rsync -al /dev/zero /data/www/dev/
rsync -al /lib /data/www/
rsync -al /lib64 /data/www/
rsync -al /usr/lib /data/www/usr/
rsync -al /usr/lib64 /data/www/usr/
rsync -al /usr/local/lib /data/www/usr/local/
rsync -al /usr/local/lib64 /data/www/usr/local/
rsync -al /etc/pki /data/www/etc/
rsync -al /etc/ssl /data/www/etc/
rsync -al /etc/resolv.conf /data/www/etc/
rsync -al /usr/share/zoneinfo/Asia/Shanghai /data/www/usr/share/zoneinfo/Asia/
现在:
mkdir -p /data/www/dev
mkdir -p /data/www/lib
mkdir -p /data/www/lib64
mkdir -p /data/www/usr/lib
mkdir -p /data/www/usr/lib64
mkdir -p /data/www/etc
mkdir -p /data/www/usr/share/zoneinfo/Asia
mount --bind -o ro /dev /data/www/dev/
mount --bind -o ro /lib /data/www/lib
mount --bind -o ro /lib64 /data/www/lib64
mount --bind -o ro /usr/lib /data/www/usr/lib
mount --bind -o ro /usr/lib64 /data/www/usr/lib64
mount --bind -o ro /etc /data/www/etc
mount --bind -o ro /usr/share/zoneinfo/Asia /data/www/usr/share/zoneinfo/Asia
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
然后是 PHP 的配置文件更改 / usr/local/php/etc/php-fpm.d/www.conf
[chroot]
listen = 127.0.0.1:9000
user = www
group = www
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 100
request_terminate_timeout = 120
request_slowlog_timeout = 2
slowlog = /var/log/php-fpm/chroot-slow.log
chroot = /data/www
php_admin_value[error_log] = /var/log/php-fpm/chroot-error.log
php_admin_flag[log_errors] = on
php_value[session.save_path] = /var/lib/php/session
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
此时 PHP 配置完成之后,可以创建一个 PHP 文件进行验证。
当我进行验证的时候发现,死活不能得到想要的结果,一直会报一个 "File not found"
内容,看 NGINX 的错误日志内容如下:
2019/08/02 15:59:18 [error] 19008#0: *341 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 115.238.43.130, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "115.159.112.87", referrer: "http://115.159.112.87/"
# 2,NGINX 配置。
这个时候我的 NGINX 配置为:
server {
listen 80;
server_name localhost;
index index.html index.htm index.php default.html default.htm default.php;
root /data/www/typecho;
location ~ .*\.php(\/.*)*$ {
root /data/www/typecho;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
include pathinfo.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如上配置在 PHP 不开启 chroot 的情况下,访问是没有问题的,只要一开启,就会报刚刚的错误。
后来查资料,了解到,当我设置了 chroot,就相当于把配置的目录定义为了根目录,于是,在 NGINX 层面,就不能再像原来那样配置了。
也就是 chroot 目录定义为了 /data/www
,那么在 php 解析的时候,就需要将真实的项目目录定义为根目录才行,因此使用如下配置即可解决:
server {
listen 80;
server_name localhost;
index index.html index.htm index.php default.html default.htm default.php;
root /data/www/typecho;
location ~ .*\.php(\/.*)*$ {
root /data/www/typecho;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /typecho$fastcgi_script_name;
include fastcgi_params;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
其中 fastcgi_param SCRIPT_FILENAME /typecho$fastcgi_script_name;
的 /typecho
就是当前项目的根目录,然后再去访问,就没有问题了。
网上有说把配置定义为 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
也可以,但经过我的测试,发现并不行。
# 3,通过 mount 挂载扩展
这是一个有意思的尝试方案,仅作为扩展眼界,不太推荐生产使用。
我们可以通过 mount –bind 命令来将两个目录连接起来,mount –bind 命令是将前一个目录挂载到后一个目录上,所有对后一个目录的访问其实都是对前一个目录的访问,如下所示:
# 将test1挂载到test2
mount --bind test1 test2
2
当 mount –bind 命令执行后,Linux 将会把被挂载目录的目录项(也就是该目录文件的 block,记录了下级目录的信息)屏蔽,即 test2 的下级路径被隐藏起来了(注意,只是隐藏不是删除,数据都没有改变,只是访问不到了)。同时,内核将挂载目录(test1)的目录项记录在内存里的一个 s_root 对象里,在 mount 命令执行时,VFS 会创建一个 vfsmount 对象,这个对象里包含了整个文件系统所有的 mount 信息,其中也会包括本次 mount 中的信息,这个对象是一个 HASH 值对应表(HASH 值通过对路径字符串的计算得来),表里就有 /test1 到 /test2 两个目录的 HASH 值对应关系。
命令执行完后,当访问 /test2 下的文件时,系统会告知 /test2 的目录项被屏蔽掉了,自动转到内存里找 VFS,通过 vfsmount 了解到 /test2 和 /test1 的对应关系,从而读取到 /test1 的 inode,这样在 /test2 下读到的全是 /test1 目录下的文件。
- mount –bind 连接的两个目录的 inode 号码并不一样,只是被挂载目录的 block 被屏蔽掉,inode 被重定向到挂载目录的 inode(被挂载目录的 inode 和 block 依然没变)。
- 两个目录的对应关系存在于内存里,一旦重启挂载关系就不存在了。
在固件开发过程中常常遇到这样的情况:为测试某个新功能,必需修改某个系统文件。而这个文件在只读文件系统上(总不能为一个小小的测试就重刷固件吧),或者是虽然文件可写,但是自己对这个改动没有把握,不愿意直接修改。这时候 mount –bind 就是你的好帮手。 假设我们要改的文件是 / etc/hosts,可按下面的步骤操作:
- 把新的 hosts 文件放在 / tmp 下。当然也可放在硬盘或 U 盘上。
- mount –bind /tmp/hosts /etc/hosts 此时的 / etc 目录是可写的,所做修改不会应用到原来的 / etc 目录,可以放心测试。测试完成了执行 umount /etc/hosts 断开绑定。
解决重启挂载失效的问题:
当然是写入到自动挂载咯,写法如下:
$ cat /etc/fstab
/etc /data/www/etc none bind 0 0
/dev /data/www/dev/ none bind 0 0
/lib /data/www/lib none bind 0 0
/lib64 /data/www/lib64 none bind 0 0
/usr/lib /data/www/usr/lib none bind 0 0
/usr/lib64 /data/www/usr/lib64 none bind 0 0
/usr/share/zoneinfo/Asia /data/www/usr/share/zoneinfo/Asia none bind 0 0
2
3
4
5
6
7
8
或者使用另外一种方案,将这波挂载集成为一个服务,并把服务做成开机自启,并记得 PHP 的启动在此服务之后即可。
创建启动文件:
$ cat /etc/init.d/chroot_mount
#!/bin/sh
### BEGIN INIT INFO
# Provides: php7-fpm-chroot-setup
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Bind-mounts needed running before php-fpm.service
### END INIT INFO
CHROOT=/data/www
DIRS="/dev /lib /lib64 /usr/lib /usr/lib64 /etc /usr/share/zoneinfo/Asia"
case "$1" in
start)
$0 stop 2>/dev/null
for d in $DIRS; do
mkdir -p "${CHROOT}${d}"
mount --bind -o ro "${d}" "${CHROOT}${d}"
done
;;
stop)
for d in $DIRS; do
umount "${CHROOT}${d}"
done
;;
*)
echo "Usage: $N {start|stop}" >&2
exit 1
;;
esac
exit 0
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
添加为系统服务并启动,启动之前保证 /data/www
将要挂载的几个目录干净。
chmod +x chroot_mount
chkconfig --add chroot_mount
systemctl start chroot_mount
systemctl enable chroot_mount
2
3
4
然后调整一下 php 的启动文件:
$ vim php-fpm.service
[Unit]
Description=The PHP FastCGI Process Manager
After=network.target chroot_mount
[Service]
Type=simple
PIDFile=/var/run/php-fpm.pid
ExecStart=/usr/local/php/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID
[Install]
WantedBy=multi-user.target
2
3
4
5
6
7
8
9
10
11