7

I'm attempting to set up multiple instances of php-fpm to run multiple versions of php through apache 2.2 running on centos 6.5.

At some point in the future this will end up in a shared hosting environment, so I need the tightest security possible.

Therefore I'm trying to avoid disabling selinux altogether, and trying to set policies as narrow as possible.

I am relatively new to selinux (our existing servers simply has it disabled). I have done a lot of reading on the topic, but the logic still escapes me (as I'm sure this question shows).

When calling a php-script apache produces this error:

[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"

The directory containing the php-fpm sockets looks like this:

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

Based on that I would assume the type of the sockets was var_run_t...

So I'm trying to run under this policy:

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;

But it denies access to the sockets.

The audit.log says:

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)

And audit2allow -a produces:

allow httpd_t unconfined_t:unix_stream_socket connectto;

Where does that tcontext=unconfined_u:unconfined_r:unconfined_t come from when the target is the socket, and the socket is labeled var_run_t?

By changing to unconfined_t as "suggested" by audit2allow it works (commented out above). But as far as I understand adding policies involving unconfined_t is a bad idea (as it would allow access to a lot of sockets that aren't needed?)

Can anyone tell me what I've misunderstood - or how I should be aproaching the issue if I'm simply going about it completely wrong?

Update: Ok, so the 'unconfined_t' comes from the parent php-fpm master process; not from the .sock file.

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)
  • Is there any logical explanation this isn't reflected in ls -Z?

  • And does this mean that the issue is actually worse (than I first suspected)? Ie. the php-fpm process is running unconfined, allowing it to do nearly anything.

Update: I managed to get php-fpm to run in the same domain as apache by simply:

chcon system_u:object_r:httpd_exec_t:s0 /usr/php-multi/5.3.28/sbin/php-fpm

Apache already has transition rules and selinux policies defined, so the process transitions automatically to the httpd_t domain as soon as it is launched, whether during boot or after a manual service php-fpm-5.3 start (or restart) - and PHP is executed through apache without a hiccup.

However I am still not sure this (sharing the domain with apache) a desirable situation (still from a security perspective). Should I continue trying to get it into it's own domain and defining policies for that manually?

Update (I'm new here, so someone tell me if it's inappropriate to keep updating the question?):

I found out how to create a new type and get the php-fpm daemon to run there; here is my new policy:

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)

The rules are generated with audit2allow and may allow more than needed, but this works... of course the php-fpm binary still needs to be given the new type:

chcon system_u:object_r:php_fpm_exec_t:s0 /usr/php-multi/5.3.28/sbin/php-fpm

I am still unsure which solution is actually best security wise.

I am also still open to any comments about the general approach, or suggestions on potential improvements to this policy...

Mikk3lRo
  • 183
  • 1
  • 7
  • I assume that the `unconfined` is from the domain that `php-fpm` is running in. Can you check what the confinement of the `php-fpm` process is in `ps -Z`? – Matthew Ife May 18 '14 at 14:24
  • Yes, you are right! (question updated) – Mikk3lRo May 18 '14 at 15:06
  • Are you using the php-fpm that comes from PHP? How are you starting the process? Via init or some other method? – Matthew Ife May 18 '14 at 15:41
  • I compiled php with the `--enable-fpm` parameter (PHP 5.3.28 for this test). I also have php 5.6 installed (via yum from remi). The 5.3 installation is started with a copy of the init.d script that came with the yum-install that I have modified. I can update the question with the full scripts / compile commands if that would be useful. If I reboot (after `chkconfig php-fpm-5.3 on`) the process runs with `initrc_t`, if I restart it with `service php-fpm-5.3 restart` it runs as `unconfined_t`. Is it possible to force it into it's own domain (maybe through the `init.d/php-fpm-5.3` script)? – Mikk3lRo May 18 '14 at 16:29
  • The policy is a good start, its missing a number of things however. Too many to comment on, if I get some time i'll produce a policy which is possibly more flexible. – Matthew Ife May 18 '14 at 22:13
  • Thought of a more cleaner way to do it and edited my original answer. – Matthew Ife May 18 '14 at 22:28

2 Answers2

1

Try this

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)

EDIT

Thinking about it, php-fpm is practically doing what the webserver does. Try setting /usr/sbin/php-fpm and /etc/rc.d/init.d/php-fpm to httpd_exec_t and httpd_initrc_exec_t respectively, then see how that holds out.

If you rewrite a policy for it you need to consider a number of things:

  • You should probably make sure tmp files get labeled something special.
  • Make php-fpm also label var_run sockets and pid files something special, then alter apache to be able to connect to them.
  • php-fpm will probably want database access when executing PHP scripts.
  • You'll likely need to allow php-fpm to listen on specific network ports, and let apache connect back to them as well as unix sockets.
  • You need to define an fc file for specifying file contexts.
  • Make sure php-fpm can do other things like imap, pop3, smtp, http, https to name at least a few.
  • Must be able to write back into httpd content where write is permitted, and using the correct label.
  • Must be able to read into user_content types of httpd as well as system ones.

Confining what is in effect a programming language can be pretty tricky because of how robust the policy needs to be to work with many different web applications.

Matthew Ife
  • 22,927
  • 2
  • 54
  • 71
  • I think the last line is a leftover from earlier attempts (months ago), I'll edit it out from the question as it has no relevance. The results are unfortunately completely unchanged with the revised policy you suggest though. – Mikk3lRo May 18 '14 at 12:09
0

php-fpm sould fall into httpd so there's no need to create a new type. Here's a working httpd policy i wrote (tested on AMI Linux, which is RHEL based) that works with php-fpm and also has commonly needed permissions to give to 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;

Basically what's needed for php-fpm to work is last line (along with it's types and class declaration)

allow httpd_t var_run_t:sock_file write;
Fabius
  • 101
  • 2