Shane Rainville
I.T. professional with over a decade of experience, ranging from application development to system & infrastructure administration. He's worked with small startups to large corporate companies, using unique and creative solutions to solve problems.


SELinux can be very troublesome when deploying web applications on Red Hat while not using the default Apache directories, for either content or logs. Your application may need to be installed in a separate directory or maybe, for other reasons, you want to place your content somewhere else. In these scenarios, SELinux will not allow Apache to access your content or log files.

Instead of disabling SELinux, which you should never do, though many do, you should instead create custom policies that apply the proper SELinux context types to your directories and files. Aside from allowing more freedom for placement of your application files, if something were to cause your context settings to disappear, you will be able to quickly remedy the problem by running a context restore to get your applications running again right away.


  1. Application must reside outside of the default Apache directory (/var/www/html).
  2. SELinux blocks Apache from loading content outside of default directories.
  3. Some files require for the application read and write access. By default, they cannot.
  4. Some directories require write access for uploading content. By default, they cannot.


This tutorial assumes that MySQL and Apache are already installed and configured. The scope is to show how to apply common Apache SELinux contexts to a web application’s directory structure, by creating and applying custom policies, allowing you to place your files outside of the default location (/var/www/html).

Application Directory Structure

Before designing an SELinux policy for our web application, we need to understand the directory structure our application will use. This structure is  an example only. Your structure will more than likely differ due to environmental requirements.

Our application has the following directory tree on the web server. I’m only showing a subset of directories and files, but it should be enough for the purposes of this tutorial.

   |     |---/app1
   |           |---/public_html
   |                 |---/wp-content
   |                 |---/uploads
   |                 |---/index.php
   |                 |---/wp-config.php
   |     |---/app1
Apps This directory contains the web application directories, which host the application files. Each application will be segregated into its own child app directory. All files should be read-only, unless explicit permissions allow otherwise.
Logs This directory will contain the log files for our applications. Each application will have its own child log directory.
Cache Contains the Apache cache directories for each application.

Creating SELinux Policies

Install Required Packages

Before we can create our own policies, we need to ensure SEMANAGE is installed. This application allows us to browse through the existing, default context policies, and create our own.

  1. Open a console with root privileges
  2. Install the policycoreutils-python package, which contains SEMANAGE.
    yum install -y policycoreutils-python
  3. For troubleshooting SELinux issues, download the setroubleshooting package. This step is actually optional, but you’ll be thankful for it when you are able quickly diagnose SELinux problems with it.
    yum install -y setroubleshooting

Apache Context Types

Before we can start creating our own policies for applying Apache’s context types, we need to understand which are available to us out of the box. The following table shows the ones we are primarily interested in, however there are several others available.

httpd_sys_content_t Read-only directories and files used by Apache
httpd_sys_rw_content_t Readable and writable directories and files used by Apache. Assign this to directories where files can be created or modified by your application, or assign it to files directory to allow your application to modify them.
httpd_log_t Used by Apache to generate and append to web application log files.
httpd_cache_t Assign to a directory used by Apache for caching, if you are using mod_cache.

For a complete list of context types for Apache, open the man page for Apache and SELinux.

man httpd_selinux

If you’d like to see existing policies, to better understand why default contexts are applied to your direcotires and files, list them using the semanage command.

semanage fcontext -l


Create the Policies

  1. Create a policy to assign the httpd_sys_content_t context to the /webapps directory, and all child directories and files.
    semanage fcontext -a -t http_sys_content_t "/webapps(/.*)?"
  2. Create a policy to assign the httpd_log_t context to the logging directories.
    semanage fcontext -a -t httpd_log_t "/webapps/logs(/.*)?"
  3. Create a policy to assign the httpd_cache_t context to our cache directories.
    semanage fcontext -a -t httpd_cache_t "/webapps/cache(/.*)?"

Allowing ReadWrite Access

Apache now has permission to use our custom application directories. However, it does not have readwrite access to anything, which may be desired. If your application does have files or directories that need this type of access, like WordPress does, then we need to also apply the context which allows it, but only for those files or directories that require it.

The following will be assigned the Apache readwrite context, so that they can be written to or modified.

/uploads/ Directory Allow WordPress to upload files for blog entries, such as images, videos, audio, etc.
/wp-config.php File This file is modifed by WordPress when permalinks are enabled or changed. It needs to be writable, otherwise, changes to the file will have to be done manually.
  1. Create a policy to assign the httpd_sys_rw_content_t context to the upload directory, and all child files.
    semanage fcontext -a httpd_sys_rw_content_t "/webapps/app1/public_html/uploads(/.*)?"
  2. Create a policy to assign the httpd_sys_rw_content_t context to the WordPress configuration file, wp-config.php.
    semanage fcontext -a httpd_sys_rw_content_t "/webapps/app1/public_html/wp-config.php"

Applying the SELinux Policy

Our policies are created and ready to be applied to our directory structure. We will use the restorecon command to apply them. This is the same command you will use to re-apply the contexts to the application directories, if for some reason they get removed or corrupted.

  1. Apply the SELinux policies
    restorecon -Rv /webapps
  2. Our directory structure should now have the following SELinux contexts applied.
    /webapps (httpd_sys_content_t)
       |---/apps (httpd_sys_content_t)
       |     |---/app1 (httpd_sys_content_t)
       |           |---/public_html (httpd_sys_content_t)
       |                 |---/wp-content (httpd_sys_content_t)
       |                 |---/uploads (httpd_sys_rw_content_t)
       |                 |---/index.php (httpd_sys_content_t)
       |                 |---/wp-config.php (httpd_sys_rw_content_t)
       |---/logs (httpd_log_t)
       |     |---/app1 (httpd_log_t)
       |---/cache (httpd_cache_t)
             |---/app1 (httpd_cache_t)
  3. You can verify your context types our being applied used the ls command with the -Z switch, on each of the directories. We'll check the webapps root, as an example.
    ls -lZ /webapps
  4. The output will display the context type. Notice the highlighted context types.
    drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 apps
    drwxr-xr-x. root root unconfined_u:object_r:httpd_log_t:s0 logs
    drwxr-xr-x. root root unconfined_u:object_r:httpd_cache_t:s0 cache


You now have a secure web application, with files the reside outside of the default locations. A lot of administrators disable SELinux thinking it prevents them from configuring the server based on their own requirements. Even a lot of vendors ask you to disable it, since it may be viewed as too difficult or time consuming to guide the adminitrators through it.

SELinux is a crucial layer in securing your server. And as mentioned in the beginning, it should never be disabled. It provides protections beyond what firewalls and other layers can provide. It's better to learn how easy it is to utilize, than to open security wholes.

The goal of this tutorial was to show just how easy manipulating SELinux is to fit your environments requirements. I hope the example was helpful enough to show how easy it is.

© 2014 Shane Rainville