Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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...

share|improve this question
    
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 at 14:24
    
Yes, you are right! (question updated) –  Mikk3lRo May 18 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 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 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 at 22:13

1 Answer 1

up vote 1 down vote accepted

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.

share|improve this answer
    
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 at 12:09

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.