When All Else Fails Check SELinux


CodeMilitant Solutions for Linux Nginx Python PHP Bash MariaDB

Wait! Wait! Before you start, check SELinux! Have you noticed that on a multitude of coding community sites, like Stack Exchange, Stack Overflow or Super User, there are always questions about ‘permission denied‘?

This topic is so common, that it can discourage and dishearten anyone trying to finish a project. Often times, the first solutions have nothing to do with the real problem, and the OP (original poster), simply gives up and doesn’t award any answer to their question.

At the time of writing this post, the ‘permission denied‘ response is a generic response to one of the most common problems, poorly configured SELinux. Take for example a typical Nginx web server. If the web root directory is placed in /var/www/html/codemilitant, then SELinux will automatically insert it into it’s global policies. Then, when trying to view the website, the response might simply be a blank page. And depending how Nginx is setup, a standard 404 error might be displayed, but the more common result will be the 403 error.

An HTTP 403 error is ‘permission denied‘ in web server speak. Tailing the log files:


sudo tail -f -n 30 /var/log/nginx/codemilitant.error.log

Will simply reveal a ‘permission denied‘ error.

So let’s fix these headaches before they begin.

First, SELinux operates under ‘root‘ access only, so be sure to have ‘sudo‘ access to the server. If there’s no ‘sudo‘ access, likely because it’s shared hosting, then just skip all the rest of this article. The ‘permission denied‘ is most likely a directory:file permission issue.

Second, type this command:


sudo getenforce

This will reveal whether SELinux is ‘Enforcing‘, ‘Permissive‘ or ‘Disabled‘. If SELinux is ‘Enforcing‘, then ensure the web root has proper permissions within the SELinux policies. No amount of changing the directory:file permissions, or modifying read/write/execute permissions will allow the web root directory to be accessed.

Finally, do all of this first! Don’t wait until there are problems, execute the ‘getenforce‘ command above the moment you login to a new server.

Now that these permissions issues are nailed down, let’s get SELinux tuned up to serve the content.

The easiest way to verify this is a SELinux policy issue, then run:


sudo setenforce 0

This will set SELinux to a ‘Permissive’ state, and allow the web server to serve content without changing any SELinux policies. To properly configure SELinux for Nginx or Apache, please read on.

To start let’s be sure that an SELinux policy is enabled for running Nginx or Apache.


sudo getsebool -a | grep -i httpd

Notice that even if running an Nginx server, the ‘httpd‘ policy looks like it applies only to Apache. The truth is, the ‘httpd‘ policy applies to both Nginx and Apache. The SELinux ‘httpd‘ policy is just that, a policy for SELinux. The name of the policy could be anything at all. It just looks like ‘httpd‘ is related to Apache because traditionally ‘httpd‘ is the default Apache web server daemon binary program.

The ‘getsebool‘ command above will return:


httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_connect_mythtv --> off
httpd_can_connect_zabbix --> off
httpd_can_manage_courier_spool --> off
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
httpd_dbus_avahi --> off
httpd_dbus_sssd --> off
httpd_dontaudit_search_dirs --> off
httpd_enable_cgi --> on
httpd_enable_ftp_server --> off
httpd_enable_homedirs --> off
httpd_execmem --> off
httpd_graceful_shutdown --> off
httpd_manage_ipa --> off
httpd_mod_auth_ntlm_winbind --> off
httpd_mod_auth_pam --> off
httpd_read_user_content --> off
httpd_run_ipa --> off
httpd_run_preupgrade --> off
httpd_run_stickshift --> off
httpd_serve_cobbler_files --> off
httpd_setrlimit --> off
httpd_ssi_exec --> off
httpd_sys_script_anon_write --> off
httpd_tmp_exec --> off
httpd_tty_comm --> off
httpd_unified --> off
httpd_use_cifs --> off
httpd_use_fusefs --> off
httpd_use_gpg --> off
httpd_use_nfs --> off
httpd_use_opencryptoki --> off
httpd_use_openstack --> off
httpd_use_sasl --> off
httpd_verify_dns --> off

In a typical web server configuration, regardless of whether it’s Nginx or Apache, the following SELinux policies must be enabled ( set to 1 or ‘on’ ):


httpd_unified --> on
httpd_can_network_connect --> on
httpd_can_sendmail --> on
httpd_can_network_connect_db --> on

To enable these policies run the following:


sudo setsebool -P httpd_unified 1
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_sendmail 1
sudo setsebool -P httpd_can_network_connect_db 1

Now these policies have been permanently set to serve website content when SELinux is in the normal ‘Enforcing‘ mode.

Now that the SELinux policies are enabled, the directories and files must be updated to allow for SELinux connections.

To discover the permissions of any file, simply type:


ls -la /var/www/html/codemilitant

This will show the directories, files and the permissions of everything in the designated directory. It might look like this:


[root@localhost codemilitant]$ ls -la
total 200
drwxr-xr-x.  5 nginx nginx  4096 Apr 12 09:43 .
drwxr-xr-x. 21 root  root   4096 Feb 11 13:55 ..
-rw-r--r--.  1 nginx nginx   405 Apr  6 16:57 index.php
-rw-r--r--.  1 nginx nginx  7165 Apr  6 16:57 wp-activate.php
drwxr-xr-x.  9 nginx nginx  4096 Apr  6 16:51 wp-admin
-rw-r--r--.  1 nginx nginx   351 Apr  6 16:57 wp-blog-header.php
-rw-r--r--.  1 nginx nginx  2338 Apr  6 16:57 wp-comments-post.php
-rw-r--r--.  1 nginx nginx  4261 Apr 21 09:34 wp-config.php
-rw-r--r--.  1 nginx nginx  3001 Apr  6 16:57 wp-config-sample.php
drwxr-xr-x.  8 nginx nginx  4096 Apr 26 13:20 wp-content
-rw-r--r--.  1 nginx nginx  3939 Apr  6 16:57 wp-cron.php
drwxr-xr-x. 26 nginx nginx 12288 Apr  6 16:51 wp-includes
-rw-r--r--.  1 nginx nginx  2496 Apr  6 16:57 wp-links-opml.php
-rw-r--r--.  1 nginx nginx  3900 Apr  6 16:57 wp-load.php
-rw-r--r--.  1 nginx nginx 47916 Apr  6 16:57 wp-login.php
-rw-r--r--.  1 nginx nginx  8582 Apr  6 16:57 wp-mail.php
-rw-r--r--.  1 nginx nginx 23025 Apr  6 16:57 wp-settings.php
-rw-r--r--.  1 nginx nginx 31959 Apr  6 16:57 wp-signup.php
-rw-r--r--.  1 nginx nginx  4747 Apr  6 16:57 wp-trackback.php
-rw-r--r--.  1 nginx nginx  3236 Apr  6 16:57 xmlrpc.php

In a typical WordPress installation, like the list shown above, the directory permissions are 755 and the file permissions are 644, except for the wp-config.php file. This should be set to 600 for security. Some have this file set to 444 for extreme security.

To get the SELinux file permissions for any directory or file, type in:


ls -Za /var/www/html/codemilitant

This will give:


[root@localhost codemilitant]$ ls -Za .
drwxr-xr-x. nginx nginx unconfined_u:object_r:etc_runtime_t:s0 .
drwxr-xr-x. root  root  system_u:object_r:etc_runtime_t:s0 ..
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-activate.php
drwxr-xr-x. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-admin
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-blog-header.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-comments-post.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-config.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-config-sample.php
drwxr-xr-x. nginx nginx unconfined_u:object_r:httpd_sys_rw_content_t:s0 wp-content
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-cron.php
drwxr-xr-x. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-includes
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-links-opml.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-load.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-login.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-mail.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-settings.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-signup.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 wp-trackback.php
-rw-r--r--. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 xmlrpc.php

In these results, the SELinux policies are shown for each directory and file after the user:group assignment.


unconfined_u:object_r:httpd_sys_content_t:s0
unconfined_u:object_r:httpd_sys_rw_content_t:s0

Notice this has the ‘httpd‘ SELinux policy assigned to all of these directories and files. Let’s get these policies assigned to the web root.

For this example, let’s use the WordPress instance as an example of the web root. Use the following command to enable the correct ‘SELinux Policy Management‘ directory:file permissions:


sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/html/codemilitant(/.*)?"

This will enable read permissions for all of the directories and files in the entire webroot WordPress instance as well as all sub directories and files. The ‘fcontext‘ file mapping context definitions will be added ( -a ) to all directories and files in the full web root environment. But what about write permissions?

For this use:


sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/codemilitant/wp-content(/.*)?"

Notice the ‘rw‘ found in the SELinux ‘type‘ ( -t ) seen in httpd_sys_rw_content_t. This designates ‘read/write‘ for this directory and all sub directories:files.

All these SELinux directory:file policies are called ‘labels‘ and any time there’s a correction to any SELinux label, the ‘restorecon‘ command must be run to update SELinux policies. That looks like:


sudo restorecon -Rv /var/www/html/codemilitant/

Congratulations! Now the directory:file permissions for the web root have been properly set and SELinux can be safely enabled.


sudo setenforce 1

SELinux is by far one of the most powerful tools for securing any server. It takes basic directory:file permissions to the extreme and delivers peace of mind.

Have any questions? Please leave questions and comments below.

, ,

Leave a Reply