Mugo Web main content.

Enabling SELinux to keep your eZ Publish / eZ Platform website secure

By: Ernesto Buenrostro | May 3, 2018 | eZ Publish development tips, Site performance, and Web solutions

Dealing with permissions and security systems such as Security-Enhanced Linux (SELinux) is an issue that seems to challenge many developers. Some enterprise Linux distributions like Red Hat and CentOS come with SELinux enabled by default, but not knowing how SELinux works can lead developers to disable it. This is a mistake.

SELinux is a Linux kernel security module that implements a Mandatory Access Control mechanism that confines a system process or a user, restricting the access to resources such as files, devices, networks and inter-process communication (IPC). It allows the administrator of a system to limit the privileges to the minimum required to work. This will eliminate the ability of these programs to cause any harm in a compromised application (via buffer overflows or misconfigurations, for example).

The SELinux confinement mechanism operates separately from the traditional Linux (discretionary) access control mechanisms, which are modifiable by the user and the applications that the user runs. SELinux access controls are determined by a policy loaded on the system; SELinux policies may not be changed by careless users or misbehaving applications. 

If an attacker can take advantage of vulnerable application code that allows him or her to run arbitrary commands, SELinux can help mitigate this by preventing Apache (the process, not the user) from accessing files or resources.

SELinux has finer granularity than access controls and is able to specify who can read, write, and execute a file, as well as who can unlink, append only, move a file, and more.

SELinux is not a firewall. A firewall controls the flow of traffic to and from a computer to a network; SELinux restricts access to programs within a computer.

Why is SELinux frequently disabled?

One of the biggest surprises developers get while deploying an eZ Publish / eZ Platform site to a new server with one of the latest releases of CentOS or Red Hat 7 is that the site doesn't work.

Some common errors are:

  • 403 Forbidden: Permission denied: [client…
  • Can’t write to directories and files; logs are full of entries like: PHP Warning: fopen(/var/www/html/…

After spending time debugging and doing lots of Stack Overflow reading, you'll discover the issue is that CentOS 7 / Red Hat 7 has SELinux enabled by default, and the easiest way to make the site work again is to disable SELinux.

This is a big mistake because you are disabling an important security layer. The correct way is to configure SELinux properly.

SELinux supports different modes:

  • Enforcing: This is the default mode, which enables and enforces SELinux on the system. It will deny and log actions.
  • Permissive: In this mode, SELinux is enabled. It will not enforce the policies but it will log the actions.
  • Disabled: SELinux is disabled and provides no enhanced security at all.

You can find out SELinux's status by running the sestatus command.

Setting context policies

When SELinux is enabled, everything is denied by default and a policy needs to be applied to grant the required access.

SELinux is composed of hundreds of booleans and context policies; we are going to concentrate on the ones related to Apache using mod_php and mainly on the following context policies:

  • httpd_sys_content_t Grants read-only access to Apache so it can serve these contents.
  • httpd_sys_rw_content_t Grants read and write access to Apache.

First, install the policycoreutils packages (if they're not already installed), which will help us to manage SELinux.

yum install policycoreutils-python

Setting SELinux's status as "permissive" will help us to identify issues without blocking the calls made by Apache. When SELinux denies an action it will cache the decision and log the denial to the file /var/log/audit/audit.log. The SELinux denial entries are categorized as type AVC (Access Vector Cache).

We can look at this log file to help us identify what context policies are missing.

An entry in the logs might look like this:

type=AVC msg=audit(1522276036.050:55378): avc: denied { write } for pid=10229 comm="httpd" name="ini" dev="dm-0" ino=113344638 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:httpd_sys_content_t:s0 tclass=dir

In this entry, we can see that SELinux is denying write access to the ini directory because the context of the directory is set to httpd_sys_content_t and this policy only grants read access. The context policy should be set to httpd_sys_rw_content_t.

To start fixing issues, we need to label our app directory correctly. This will depend on what version of eZ Publish / eZ Platform we are deploying.

For this post, we are going to assume our eZ Publish / eZ Platform is installed in "/web" directory.

We are going to make sure our directory structure is labeled correctly and run the following command to grant Apache access to everything underneath the web directory.

sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"

Now, we need to grant the necessary write permissions according to the eZ Publish / eZ Platform version we are deploying:

eZ Publish legacy needs write permissions on the following directories:

  • design
  • extension
  • settings
  • var

To grant the necessary permissions, we will issue the following command:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/web/(design|extension|settings|var)(/.*)?"

An eZ Publish 5.x instance will need write permissions on the following directories:

  • ezpublish/cache
  • ezpublish/logs
  • ezpublish/config
  • ezpublish/sessions
  • ezpublish_legacy/design
  • ezpublish_legacy/extension
  • ezpublish_legacy/settings
  • ezpublish_legacy/var
  • web

To grant the necessary permissions, we will issue the following commands:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/web/ezpublish/(cache|logs|config|sessions)(/.*)?"
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/web/ezpublish_legacy/(design|extension|settings|var)(/.*)?"

eZ Platform needs write permissions on the following directories:

  • var
  • web/var

To grant the necessary permissions we will issue the following command:

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/web/var/(/.*)?"
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/web/web/var/(/.*)?"

To make these changes go live, we need to run the following command:

sudo restorecon -Rv /web

This will help us to set the correct labels to the files and directories, so the files can be accessed and written by Apache.

Using SELinux booleans

There are some extra permissions Apache will need to work properly, depending on their usage by our instance; those permissions are set using SELinux booleans. SELinux booleans are configurable settings that can be enabled / disabled at run time. Booleans control when SELinux should allow a process to perform or not an action; for example, to allow Apache to create HTTP connections to third-party servers.

To get a list of the existing booleans, we can run the following command:

getsebool -a

But, again, our interest is set on those related to Apache. To filter these we can use the grep tool:

getsebool -a | grep -P 'httpd'

We can check the current state of a boolean like so:

getsebool httpd_can_sendmail

To search through the audit entries we will use the command ausearch:

sudo ausearch -m AVC -c httpd -ts recent

Of the options we are passing -m AVC to get messages of type AVC, -c httpd to only get messages related to Apache, and the option -ts recent to get the most recent entries.

The output will look like this:

----
time->Wed Mar 28 17:21:54 2018
type=PROCTITLE msg=audit(1522282914.396:55787): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44
type=SYSCALL msg=audit(1522282914.396:55787): arch=c000003e syscall=42 success=no exit=-13 a0=13 a1=7fff68f0b970 a2=10 a3=2 items=0 ppid=20023 pid=24337 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=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1522282914.396:55787): avc: denied { name_connect } for pid=24337 comm="httpd" dest=8983 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket

This is kind of hard to read, but some of the important information we can get from there is that httpd made a TCP connection using the port 8983 (where the Solr search engine typically resides).

We can pipe the output from ausearch to audit2allow.

sudo ausearch -m AVC -c httpd -ts recent|audit2allow

This will suggest to us what booleans can be configured to allow Apache to communicate with Solr.

The output will look like:

#============= httpd_t ==============

#!!!! This avc can be allowed using one of these booleans:
# httpd_can_network_connect, nis_enabled
allow httpd_t unreserved_port_t:tcp_socket name_connect;

Again, if you have integrations to external APIs and you need to do cURL calls to other servers, or if you are using Solr to search the content, it will be necessary to let Apache create network connections.

To grant Apache the necessary permissions, we need to enable a boolean httpd_can_network_connect, and to make the change permanent we will use the setsebool command.

sudo setsebool -P httpd_can_network_connect=1

Here are some other important booleans we may want to make sure we set. Of course, it should be based on your needs.

If eZ Publish / eZ Platform is sending e-mails using sendmail, there is a boolean to let Apache use sendmail:

sudo setsebool -P httpd_can_sendmail=1

If you are using memcached in your instance -- at least until you start using Redis! -- you will need to grant Apache the necessary permissions:

sudo setsebool -P httpd_can_network_memcache=1

For more information on SELinux, check out: https://selinuxproject.org

 

loading form....
Thanks for reaching out. We will get back to you right away.