SELinux is a powerful tool for controlling what applications are allowed to do on your system. SELinux is a labeling system where every process and every object (files, directories, devices, network ports, etc.) gets a label. Then a large rules database, called policy, is loaded into the kernel. The kernel, based on the policy, controls what each process can do based on its label, and the label of the object it is trying to access. For example SELinux allows a process with the Apache label (httpd_t) to share data labeled as "read/only Apache content" (httpd_sys_content_thttpd_sys_content_rw_t). SELinux will block Apache processes from reading data labeled as user's home content (user_home_t) or database data (mysql_db_t). Apache processes can listen on ports labeled as the Apache port (http_port_t) but can not connect to the ports labeled as the mail port (smtp_port_t).
SELinux provides confinement on an application if the application has been hacked, even if the application is running as root. If policy says the Apache process is only supposed to read Apache content, then even if a hacker gets uid = 0 (the root user), he will not be able to turn it into a spam bot; he will not be able to read credit card data in your home directory; and he will not be able to destroy log files. The hacked process will only be able to act as an Apache process.
"Well that is all fine and good Dan, but Apache can be configured in a hundred different ways. Can you give me some idea of how to stop SELinux from complaining?"
A while ago I wrote an article called: What is SELinux Trying to Tell Me? The Four Key Causes of SELinux Errors. In the article I explain that the four causes of SELinux messages are:
- You have a labeling issue.
- You changed the default configuration and you need to tell SELinux about it.
- An application or SELinux Policy has a bug.
- You've been hacked.
In the rest of this article, I will explain how to deal with each of these scenarios.
You Have a Labeling Issue.
As I explained above, SELinux is a labeling system: each file/directory must have a label. By default Fedora or Red Hat expect Apache content to be stored under /var/www. SELinux policy labels (httpd_sys_content_t) content under this directory so Apache process (httpd_t) can read it. Fedora expects cgi scripts to be stored under /var/www/cgi-bin, so SELinux policy defines a label (httpd_sys_script_exec_t) for this content which Apache can execute. There are many labels associated with Apache including httpd_sys_content_rw_t and httpd_sys_content_ra_t; these labels allow the Apache process to write and append the content appropriately. You can read the httpd_selinux.8 man page for a description of all the labels associated with Apache.
Sometimes you might want to store content in a directory different from the default Apache directories. You might get an AVC1[[Footnote]] message, indicating that Apache was not allowed to write content to a /var/lib/myapp directory. Since SELinux did not expect Apache to write to this directory, the access is denied. You can change the label of the directory to a label that would allow SELinux to write to it. The best way to do this is: first, alter the SELinux labeling database by using the semanage fcontext command; after you alter the default labeling, you can use restorecon to actually put the labels on disk. In our example, we want to setup the directory tree under /var/lib/myapp as being Apache read/write content.
# restorecon -R -v /var/lib/myapp
The first command uses semanage (SELinux Manage) with the fcontext command (File Context). We tell the system to add the SELinux type httpd_sys_content_rw_t type to the /var/lib/myapp directory and all of its children using the regular expression '/var/lib/myapp(/.*)?'. Then running restorecon will actually change the labels on disk on all existing files and directories.
Be careful when you do this and make sure you are changing a system label.2[[Footnote]] If you want to share content owned by a different process type, you probably do not want to change the label, but would be better off setting a boolean or writing a little bit of custom policy, defined below.
You Changed the Default Configuration and You Need to Tell SELinux About It.
SELinux is often described as a "Least Privilege System." This means you give the least possible access to a process to get its job done, but nothing more. I refer to the policy shipped by Fedora and Red Hat as "Reasonable Privilege System." Our goal is to give access to a confined process in a way that most people would run the application. Sometimes you want to run a process with additional access, you have to "tell" SELinux about this. In the previous example we told SELinux about changes in the default labeling, now we are going to look at additional SELinux configuration. SELinux policy allows administrators to set booleans to modify the access. Booleans are if-then-else rules written in policy that can be toggled to change the way a confined application can run.
Setup Apache to Send Mail.
A great example of this is Apache sending mail. Most people setup Apache to bind to port 80 and share read/only content. In the past, Apache processes have been hacked and turned into spam bots, sending out tons and tons of spam. By default SELinux blocks the Apache process from connecting to the mail port (We actually block almost all network ports from Apache). Even if your Apache process was hacked, the hacker would be blocked from sending mail or from attacking other networks using your machine. But what if you want your Apache server to be able to send mail? You would turn on the httpd_can_sendmail boolean.
All the booleans related to Apache are documented in the httpd_selinux.8 man page. Or you can list them using
Or you could view and modify them using the system-config-selinux GUI tool.
You can use audit2allow to read AVC messages and it will report on any booleans that would have allowed the access. Setroubleshoot is a great tool for analyzing the AVC; it will also tell you if a boolean is available.
I Want Apache to Listen on Port 81
SELinux not only controls access to the file system, but also controls network access. By default the Apache process is allowed to bind the http_port_t type. This type is defined for the following tcp ports:
If you wanted to allow Apache to bind to tcp port 81, you would execute the following command:
You can use the semanage port -l command to list all port definitions, or system-config-selinux.
Note: If a port is used by a different confined domain, and you modify it to http_port_t, the other confined domain may lose access to it. For example if you modified port 25 to be used by Apache, your mail servers would probably no longer be able to access it, since they would not be allowed to bind to http_port_t.
An Application or SELinux Policy has a Bug.
If SELinux is denying access and you do not find a boolean or a way to change the labeling to allow the access, you might have found a bug in either the confined application or in SELinux policy. The best thing to do in this situation is to report the bug to your distribution. Setroubleshoot can be used to send the message. You can also use the #selinux and #fedora-selinux IRC rooms on freenode to ask SELinux experts to look at your avc messages and see if they can help. If you have to get your application working now, you can do one of following, from worst to best.
- Disable SELinux. Obviously this is something I strongly discourage.
- Put SELinux into permissive mode.
# setenforce 0
In permissive mode SELinux continues to report denials but does not actually block the access. This is a good temporary solution so that your application can run normally while you troubleshoot the AVC messages generated by SELinux denials.
- Put the process type into permissive mode.
semange permissive -a httpd_t
Modern SELinux systems allow you to put one or more process types into permissive mode without modifying the entire system.
- Write a custom policy using audit2allow and semodule. The audit2allow command can read an AVC and generate a policy module for it. Policy modules are similar to kernel modules in that they can be installed/enabled/disable/removed from a running system and modify the policy of the kernel.
# grep httpd_t /var/log/audit/audit.log | audit2allow -M myhttp
# semodule -i myhttp.pp
Those two rules would look for every SELinux denial mentioning httpd_t in the log files, and generate allow rules in policy. The semodule like will load the policy into the kernel. The audit2allow command will also generate a myhttp.te file containing a human readable file with the allow rules you are adding. Make sure you are comfortable with the rules you are adding; if you’re not sure, ask someone for help. (The beauty of open source.)
Remember: if you have to load a custom policy module, you should always report a bug so others will not face the same problem.
You've Been Hacked.
There is a fine line between this section and the previous, since they can look very much the same. Suddenly the audit logs are flooded with AVC message about httpd trying to do stuff. Try to analyze the policy and look for strange AVC's, for example: the Apache process trying to shut down selinux or read the /etc/shadow file or execute su or sudo when you did not set the server up to do this; denials connecting to the mail port; or attempts to load kernel modules. These could all be signs that your Apache server has been hacked and SELinux is blocking further access. In this case you definitely do not want to put the machine into permissive mode or add custom policy modules to allow the access. It is best to talk to someone in your security department if you suspect a hack. Or go outside for SELinux help.
Hopefully this article has helped you become more comfortable with SELinux and Apache. Apache is our most complex domain that we confine, since it is infinitely customizable. But you should be able to lock it down on your system and still be able to get the job done.