8

I want to compartmentalize different PHP applications on my SL6.4 (RHEL 6.4 rebuild) web server so that they cannot access each others' data. It seems that SELinux might be able to do this, but I am not sure on the details. My question has two parts:

  1. How does SElinux manage PHP scripts running in the Apache process with mod_php? Does the process somehow enter the script context when running the PHP script, or does that only work when scripts are run out-of-process via CGI or FastCGI? If it transitions to a script context to run the PHP script, what keeps a PHP bug from triggering a transition back to the main httpd context? If I need an alternate PHP deployment method, that would be good to know.
  2. How can I separate scripts/applications so that e.g. TinyTinyRSS cannot access things owned by OpenCloud? It looks like I should be able to do this by turning off httpd_unified and providing separate httpd_ttrss_* and httpd_opencloud_* sets of contexts, parallel to httpd_user_foo and httpd_sys_foo. It might even be sufficient for me to use the sys/user distinction without new contexts, given the number of apps I can use. But I haven't found much documentation on exactly what the implications of turning off httpd_unified are, or how to set up different HTTP contexts. Particularly with PHP scripts run via mod_php.

I am fine with creating new SELinux policy modules, but would like some documentation pointing to what I need to make the new policy do and how to make it integrate nicely with the SELinux targeted policy.

If it is a lost cause to try to do this separation just with SELinux and I need to spin up separate httpds in different contexts, or possibly even LXC containers, that would be a useful answer as well.

  • This can be dependant on the means by which you want to run php, you tagged 'mod-php' but just wanted to confirm that is the method you want to employ. – Matthew Ife Apr 19 '13 at 18:00
  • @MIfe I am currently using mod_php, but am open to switching to a different deployment method if it makes it easier. – Michael Ekstrand Apr 19 '13 at 18:05

2 Answers2

5

The best way to acheive this level of seperation is to not use type transitions but category / MCS transitions. This acts a little like the svirt implementation in the libvirt KVM stuff.

OK, the first thing you're going to need to do is download a httpd module called mod_selinux. Its been floating around in the fedora repos for quite some time, but has never really made it into the EL6 systems unfortunately.

In any case, you can rebuild the package from fedora sources. I did this on a fedora machine but you can just download the same package from a mirror. I used F16 as a base as it runs httpd-2.2.

yumdownloader --source mod_selinux --releaserver=16
...
mod_selinux-2.2.2454-3.fc15.src.rpm                        |  23 kB   00:00

Then when downloaded, rebuild on your EL6 box.

rpmbuild --rebuild mod_selinux-2.2.2454-3.fc15.src.rpm
...
Wrote: /home/build/rpmbuild/RPMS/x86_64/mod_selinux-2.2.2454-3.el6.x86_64.rpm

Finally install the module.

rpm -i /home/build/rpmbuild/RPMS/x86_64/mod_selinux-2.2.2454-3.el6.x86_64.rpm

The RPM installs a module for httpd which you'll need and also a policy for httpd which is also necessary for this to run.

The file for this module is installed in /etc/httpd/conf.d/mod_selinux.conf.

The first stage in this process is to increase the number of categories that the main httpd process runs as, so that it can produce child threads that span the correct range. In the file change:

selinuxServerDomain     *:s0

To

selinuxServerDomain     *:s0-s0:c0.c1023

Now, you must assign each virtual host in apache a category. This is done by adding a line such as in the example below called selinuxDomainVal.

<VirtualHost *:80>
    DocumentRoot /var/www/vhosts/host1
    ServerName host1.virtual
    selinuxDomainVal *:s0:c0
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot /var/www/vhosts/host2
    ServerName host2.virtual
    selinuxDomainVal *:s0:c1
</VirtualHost>

Next, in the document root for each host, relabel their document roots to the same category as the ones labelled in the httpd config.

chcon -R -l s0:c0 /var/www/vhosts/host1
chcon -R -l s0:c1 /var/www/vhosts/host2

If you want to make the labelling get honoured if you do a system relabel, you'd better update the local policy too!

semanage fcontext -a -t httpd_sys_content_t -r s0-s0:c0 '/var/www/vhosts/host1(/.*)?'
semanage fcontext -a -t httpd_sys_content_t -r s0-s0:c1 '/var/www/vhosts/host2(/.*)?'

And thats it! Its impossible to leave your document root and go exploring in others now.

Matthew Ife
  • 22,927
  • 2
  • 54
  • 71
  • 2
    Note that `mod_selinux` does not completely protect against PHP bugs — if an attacker can perform a controlled memory corruption after exploiting a virtual host with a restricted context, he may be able to inject code into the Apache process to be run in the default httpd context, or turn off `mod_selinux` for subsequent requests handled by the same worker process. Caches like APC will also be shared between all virtual hosts. If this is a concern, full process separation is needed (using CGI or FastCGI for PHP, or even separate httpd instances if you want to protect against httpd bugs too). – Sergey Vlasov Apr 20 '13 at 16:17
  • Threads can only do 'typebounds' transitions, one cannot transition into a different confinement 'truly' so this is completely valid. Nevertheless -- even in the circumstances described above an attacker can only manipulate apache within its selinux confinement. – Matthew Ife Apr 20 '13 at 16:33
  • @SergeyVlasov Thanks for the heads up. The primary threat model I am concerned about is someone exploiting bugs in one PHP application to manipulate the data or configuration of another. Bugs in PHP itself are concerning, but in balancing configuration difficulty vs. protection, this solution seems like it will accomplish what I need. – Michael Ekstrand Apr 20 '13 at 20:06
  • @MatthewIfe very much appreciate the answer, big help. Just wanted to point out, for anyone else looking at this, we had to take the extra step of creating a custom policy to allow httpd service access to the required `setcurrent`. I've added an additional answer below with details. – oucil Jan 13 '20 at 19:56
1

I know this question is 7yrs old, however the answer to it by @MatthewIfe above was instrumental but required a few extra steps. We're using CentOS8 which still doesn't include mod_selinux out of the box, so the answer above was perfect in getting it installed.

However post-install, and after a correct setup, apache was failing AVC tests and would not start up. The errors available in the /var/log/httpd/error.log, /var/log/messages, and /var/log/audit/audit.log were not very helpful.

In the end, I had to install two additional utilities that allowed me to troubleshoot the selinux AVC errors, which indicated I had to create a custom policy to allow the httpd service access to the setcurrent command in selinux.

You can find the solution here: Apache vhost privilege separation using SELinux contexts on CentOS8

Hope that helps anyone else stumbling across this.

oucil
  • 445
  • 3
  • 16