我正在尝试设置 php-fpm 的多个实例,以通过在 centos 6.5 上运行的 apache 2.2 运行多个版本的 php。
在未来的某个时候,这将最终出现在共享托管环境中,因此我需要尽可能严格的安全性。
因此,我尝试避免完全禁用 selinux,并尝试设置尽可能狭窄的策略。
我对 selinux 还比较陌生(我们现有的服务器只是将其禁用)。我阅读了大量有关该主题的资料,但逻辑仍然让我困惑(我相信这个问题说明了这一点)。
当调用 php 脚本时,apache 会产生此错误:
[Sun May 18 10:46:17 2014] [error] [client 192.168.163.1] (13)Permission denied: FastCGI: failed to connect to server "/fcgi-bin-php5-fpm-i10000_test-1.testtest.org": connect() failed
[Sun May 18 10:46:17 2014] [error] [client 192.168.163.1] FastCGI: incomplete headers (0 bytes) received from server "/fcgi-bin-php5-fpm-i10000_test-1.testtest.org"
包含 php-fpm 套接字的目录如下所示:
drwxr-xr-x. root root system_u:object_r:var_run_t:s0 .
drwxr-xr-x. root root system_u:object_r:var_run_t:s0 ..
srw-------. apache apache unconfined_u:object_r:var_run_t:s0 apache_default.sock
srw-------. apache apache unconfined_u:object_r:var_run_t:s0 i10000_test-1.testtest.org.sock
srw-------. apache apache unconfined_u:object_r:var_run_t:s0 i10000_test-2.testtest.org.sock
srw-------. apache apache unconfined_u:object_r:var_run_t:s0 i10000_test-3.testtest.org.sock
-rw-r--r--. root root unconfined_u:object_r:var_run_t:s0 php-fpm-5.3.pid
-rw-r--r--. root root unconfined_u:object_r:initrc_var_run_t:s0 php-fpm.pid
基于此我假设插座的类型是var_run_t
......
因此,我尝试按照以下政策运行:
policy_module(httpd_php_fpm, 1.0)
require {
type unconfined_t;
type var_run_t;
type httpd_t;
type httpd_sys_content_t;
class sock_file write;
}
#============= httpd_t ==============
allow httpd_t var_run_t:sock_file write;
#doesn't work
allow httpd_t var_run_t:unix_stream_socket connectto;
#works
#allow httpd_t unconfined_t:unix_stream_socket connectto;
但它拒绝访问插座。
说道audit.log
:
type=AVC msg=audit(1400402777.579:642): avc: denied { connectto } for pid=11068 comm="httpd" path="/var/run/php-fpm/i10000_test-1.testtest.org.sock" scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket
type=SYSCALL msg=audit(1400402777.579:642): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=7ffe42329818 a2=32 a3=0 items=0 ppid=6136 pid=11068 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
并audit2allow -a
产生:
allow httpd_t unconfined_t:unix_stream_socket connectto;
tcontext=unconfined_u:unconfined_r:unconfined_t
当目标是插座且插座已标记时,它从何而来var_run_t
?
通过更改unconfined_t
为 audit2allow 的“建议”,它可以工作(上面注释掉)。但据我所知,添加涉及的策略unconfined_t
不是一个好主意(因为它会允许访问大量不需要的套接字?)
有人能告诉我我误解了什么吗?或者,如果我完全错误地处理这个问题,我该如何处理?
更新:好的,所以'unconfined_t'来自父 php-fpm 主进程;而不是来自.sock 文件。
ps xZ | grep php-fpm
:
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 31436 ? Ss 0:00 php-fpm: master process (/etc/php-5.3/php-fpm.conf)
有没有什么合理的解释可以解释这一点呢
ls -Z
?这是否意味着问题实际上比我最初怀疑的更严重?也就是说,php-fpm 进程不受限制地运行,几乎可以做任何事情。
更新:我设法让 php-fpm 在与 apache 相同的域中运行,只需执行以下操作:
chcon system_u:object_r:httpd_exec_t:s0 /usr/php-multi/5.3.28/sbin/php-fpm
Apache 已经定义了转换规则和 selinux 策略,因此httpd_t
无论是在启动期间还是在手动service php-fpm-5.3 start
(或restart
)之后,该过程都会在启动后立即自动转换到域 - 并且 PHP 会通过 apache 顺利执行。
但是我仍然不确定这种情况(与 Apache 共享域)是否是理想的情况(仍然从安全角度来看)。我是否应该继续尝试将其放入自己的域并手动定义策略?
更新(我是新来的,所以有人告诉我继续更新问题是否不合适?):
我发现了如何创建一个新类型并让 php-fpm 守护程序在那里运行;这是我的新政策:
policy_module(httpd_php_fpm, 1.0)
require {
type httpd_t;
type var_run_t;
type locale_t;
type httpd_sys_content_t;
}
#============= httpd_t ==============
allow httpd_t var_run_t:sock_file write;
#============= php_fpm_t ==============
type php_fpm_exec_t;
files_type(php_fpm_exec_t);
type php_fpm_t;
files_type(php_fpm_t);
allow php_fpm_t httpd_sys_content_t:file { read getattr open ioctl append };
allow php_fpm_t locale_t:dir search;
allow php_fpm_t locale_t:file { read getattr open };
allow php_fpm_t self:capability { setuid chown kill setgid };
allow php_fpm_t self:process { signal sigkill };
allow php_fpm_t var_run_t:dir { write remove_name add_name };
allow php_fpm_t var_run_t:file { write create unlink open };
allow php_fpm_t var_run_t:sock_file { write create unlink setattr };
init_daemon_domain(php_fpm_t, php_fpm_exec_t)
规则是用 audit2allow 生成的,可能允许超过需要的内容,但这是有效的……当然php-fpm
二进制文件仍然需要赋予新类型:
chcon system_u:object_r:php_fpm_exec_t:s0 /usr/php-multi/5.3.28/sbin/php-fpm
我仍然不确定哪种解决方案实际上是最安全的。
我仍然愿意听取有关总体方法的任何意见,或有关该政策的潜在改进的建议……
答案1
尝试这个
policy_module(httpd_php_fpm, 1.0)
require {
type httpd_t;
type var_run_t;
}
#============= httpd_t ==============
allow httpd_t var_run_t:sock_file write_sock_file_perms;
allow httpd_t var_run_t:unix_stream_socket client_stream_socket_perms;
## not sure what this is for but..
init_stream_connect_script(httpd_t)
编辑
想想看,这php-fpm
实际上是在做网络服务器所做的事情。尝试分别将/usr/sbin/php-fpm
和设置/etc/rc.d/init.d/php-fpm
为httpd_exec_t
和httpd_initrc_exec_t
,然后看看结果如何。
如果您重写一项政策,则需要考虑以下几点:
- 您可能应该确保 tmp 文件被贴上一些特殊标签。
- 让 php-fpm 也为 var_run 套接字和 pid 文件添加特殊标签,然后更改 apache 以便能够连接到它们。
php-fpm
在执行 PHP 脚本时可能需要数据库访问。- 您可能需要允许
php-fpm
监听特定的网络端口,并让 apache 以及 unix 套接字连接回它们。 - 您需要定义一个
fc
文件来指定文件上下文。 - 确保 php-fpm 可以执行其他操作,例如 imap、pop3、smtp、http、https 等等。
- 必须能够将内容写回到允许写入的 httpd 位置,并使用正确的标签。
- 必须能够读入
user_content
httpd 的类型以及系统类型。
限制实际上的编程语言可能非常棘手,因为策略需要非常强大才能与许多不同的 Web 应用程序配合使用。
答案2
php-fpm 应该属于 httpd,因此无需创建新类型。这是我编写的可行 httpd 策略(在基于 RHEL 的 AMI Linux 上测试),它可与 php-fpm 配合使用,并且还具有授予 httpd 所需的常用权限:
module my-httpd 1.0;
require {
type httpd_t;
type httpd_log_t;
type initrc_t;
type sysctl_vm_t;
type var_lib_t;
type var_run_t;
class unix_stream_socket connectto;
class dir { search read };
class file { write unlink };
class sock_file write;
}
#============= httpd_t ==============
allow httpd_t initrc_t:unix_stream_socket connectto;
allow httpd_t sysctl_vm_t:dir search;
allow httpd_t var_lib_t:file { write unlink };
allow httpd_t httpd_log_t:dir read;
allow httpd_t var_run_t:sock_file write;
基本上,php-fpm 工作所需的是最后一行(以及它的类型和类声明)
allow httpd_t var_run_t:sock_file write;