
Hello colleagues!
Many of us are involved in setting up working servers for web projects. I will not talk about how to configure Apache or Nginx: you know more about it than me. But one important aspect of creating frontend servers remains unlit: these are the settings of the security subsystems.
“Disable SELinux,” is the standard recommendation of most amateur tutorials. It seems to me that this is a hasty decision, because the process of setting up security subsystems in the “soft” policy mode is often very trivial.
Today I will tell you about some of the methods for configuring the SELinux security subsystem used in the Red Hat family of operating systems (CentOS). As an example, we will configure a bundle for the Apache +
mod_wsgi +
Django +
ZEO web server on CentOS version 5.8.
')
When configuring Linux security systems, we are constrained by the Discretionary Access Control (DAC) framework. We have standard rwx rights on three levels (owner, group-owner and others) and POSIX ACL. Thus, an application with user rights theoretically has access to all resources available to the corresponding user. If the application is compromised, this can lead to dire consequences.
SELinux (Security-Enhanced Linux) is a security subsystem that implements Mandatory Access Control (MAC), operating in parallel with the classic discretionary system. Access rights are determined by the system using policies. In the Red Hat (CentOS) family of operating systems, you get SELinux out of the box, as part of the kernel. For the simplest solution of the tasks we need, we need the targeted policy (“target”), within which the rules for the majority of typical applications are described. We get the basic protection of basic services without any extra effort. The policy rules are such that all applications not described in it will work without restrictions from SELinux within the DAC.
“But why, in fact, do we need this SELinux?” You ask. The answer is quite simple: in some cases, the security subsystem will at least record the unauthorized access, and ideally prevent it. In any case, the offender will have to act within the framework delineated for a particular process.

To add our own settings, we will operate with contexts, domains and access vectors. Events that are important from a security point of view are intercepted by SELinux at the kernel level. Security subsystem mechanisms take effect after DAC rules. SELinux provides RBAC (Role-Based Access Control), TE (Type Enforcement) and, optionally, MLS (Multi-Level Security) capabilities. Each system object has a specific context (type). Based on the policy rules, the security subsystem either allows the operation to be performed or blocks it, and the process receives an error message. All decisions taken by SELinux are cached in Access Vector Cache (AVC).
The SELinux context contains user, role, type and level information. We will operate on a type that is an attribute of Type Enforcement. It is defined as a domain for processes and a type for files. SELinux rules describe allowed interaction types. Access is allowed only if there is a corresponding rule.
Separately, I would like to mention the technology of domain transitions. In SELinux, the transition of an application from one domain to another is possible if the process from the source domain runs an application that runs from a file with the entrypoint type of the new domain.
The targeted policy created and described contexts, domains and access rules for more than 200 applications. You have the opportunity to both expand the policy and act within the proposed contexts. When developing basic policies, almost all main usage scenarios were taken into account. To create standard solutions you practically will not have to change anything.
So, when implementing template solutions, the refusal to use the SELinux protection mechanism is at least unjustified. Some difficulties in its use arise when installing additional software. In the context of the task, these are the mod_wsgi module and ZEO. To keep SELinux running, we need to make changes to its settings.
In my example, I use CentOS 5.8 (kernel 2.6.18-308.1.1.el5) with the Apache web server (httpd-2.2.3-63.el5.centos.1). It is additionally installed from the source codes of Python (2.7.2), Django (1.4), mod_wsgi (3.3) and Zope (3.4.0). (The simple installation process of this software does not deserve a separate description.)
First we need to expand the SELinux policy for httpd. The default settings are intended for reliable isolation of the process in case of its compromise. However, for httpd access to your project you will need to make some changes. The authors of the policy laid the complete logic of the application with restrictions on the context. A simple command will help you get familiar with the markup of files on your system:
semanage fcontext -l | grep httpd
The policy regulates access to each of the presented types. You can
view the full list of contexts on the corresponding man page (
man httpd_selinux ). We are interested in the type httpd_sys_content_t, which allows the daemon and scripts to access files. Thus, in addition to the standard DAC rights, you must set the context for the directory and files of your project. This can be done at the
same time using the
chcon command .
chcon -R -t "httpd_sys_content_t" / your / project
However, I recommend setting the type using a rule. This will ensure the subsequent automatic type assignment when adding new files.
semanage fcontext -a -t httpd_sys_content_t "/your/ project (/.*)?"
restorecon -R / your / project
My demo project uses Django with a ZoDB database. ZEO is used as a means of communication with the database. Since this is a standalone software, it is necessary to ensure its functioning within SELinux. To ensure isolation, it is advisable, I believe, to launch ZEO with apache user rights in the
httpd_t domain. To do this, we define the startup init script in daemon mode. I will not list here the entire script because of its large size. The main thing will be enough for us:
/ usr / local / bin / zeoctl -d -s / var / run / zeo / zsock -C / etc / zeo / zeoctl.conf start
Do not forget that your initialization script must be brought to the appropriate context in order to avoid problems during the subsequent type transition in SELinux.
chcon –t "initrc_exec_t" / etc / init.d / your_init_script
In the configuration file, you must specify the desired user.
< runner >
program / usr / local / bin / runzeo -a / var / run / zeo / zeo.socket -f / var / your_db_path / db.fs
daemon true
user apache
</ runner >
A socket is used as a link between ZEO and Django. Since httpd works in the
httpd_t domain, you need to reconcile the types and DAC rights so that the application can connect to it. To do this, we will prepare the / var / run / zeo directory and set the necessary context for it. We use the -f -s switch to limit the automatic assignment of context to sockets only, and also -f -d - so that the context is installed on the directory.
semanage fcontext -a -f -d -t 'httpd_sys_script_rw_t' '/var/run/zeo(/.*)?'
semanage fcontext -a -f -s -t 'httpd_sys_script_rw_t' '/var/run/zeo(/.*)?'
restorecon –R / var / run
In the ZEO configuration file, you must specify the forced location of the communication socket.
< zeo >
address / var / run / zeo / zeo.socket
</ zeo >
Since we are planning to launch the application on behalf of the apache user, it is necessary to take into account the type transitivity. We need the running process to get the type
httpd_t . By default, the files / usr / local / bin / zeoctl and / usr / local / bin / runzeo will have the context
bin_t . Since they will be called from the
unconfined_t domain, it is necessary to trace the chain of context transitions. First of all, the script from /etc/init.d/ will be called, to which we assigned the type
initrc_exec_t . Find a chain of transitions for this situation.
sesearch -T -s unconfined_t -t initrc_exec_t | grep "initrc_exec_t"
The transition chain found looks like
unconfined_t initrc_exec_t: process initrc_t . We see that the process will receive the
initrc_t context. Accordingly, now we need to find a chain of transitions that will lead us to the required type of
httpd_t .
sesearch -T -s initrc_t | grep "process httpd_t"
The search will result in a link
initrc_t httpd_exec_t: process httpd_t . In order for this transition to occur, we need to set the
httpd_exec_t context to executable files.
semanage fcontext -a -t httpd_exec_t "/ usr / local / bin / zeoctl"
semanage fcontext -a -t httpd_exec_t "/ usr / local / bin / runzeo"
restorecon -R / usr / local / bin
Now we need to add permissions to connect to the socket for httpd in the SELinux policy. This can be done in several ways. The simplest from the user's point of view is the audit2allow utility, which allows you to form modules for a policy based on AVC messages from system logs. The utility should be used carefully, since it only creates permissions for certain actions - but no more than that (a
detailed guide is presented on the developers' website ).
The second way is to create the module manually, compile it and install it into the current policy. Since this method gives the best visualization of the process, we will manufacture modules for ZEO just like that.
Let's grant
httpd_t the rights to create and work with a socket of the
httpd_sys_script_rw_t type. To do this, create the file /tmp/httpdAllowDjangoZEO.te with the following content:
module httpdAllowDjangoZEO 1.0;
require {
type httpd_t;
type httpd_sys_script_rw_t;
class sock_file link;
class sock_file setattr;
class sock_file create;
class sock_file unlink;
class sock_file write;
}
# ============= httpd_t ==============
allow httpd_t httpd_sys_script_rw_t: sock_file link;
allow httpd_t httpd_sys_script_rw_t: sock_file setattr;
allow httpd_t httpd_sys_script_rw_t: sock_file create;
allow httpd_t httpd_sys_script_rw_t: sock_file unlink;
allow httpd_t httpd_sys_script_rw_t: sock_file write;
Next we need to create and compile the module. To do this, use the commands
checkmodule and
semodule_package . To install the module in the current policy, we need the
semodule utility.
checkmodule -M -m -o / tmp / httpdAllowDjangoZEO.mod / tmp / httpdAllowDjangoZEO.te
semodule_package - outfile / tmp / httpdAllowDjangoZEO.pp --module / tmp / httpdAllowDjangoZEO.mod
semodule -i httpdAllowDjangoZEO.pp
The final action will be setting the context of the storage location of the ZoDB database and the ZEO configuration file. In the course of our work, we will need to create technical .lock files. Therefore, the database storage location must be marked with the appropriate context, which will allow the creation of files. “
Httpd_sys_script_rw_t ” is well suited for this.
semanage fcontext –a –t "httpd_sys_script_rw_t" “ / var / your_db_path ( / . * ) ?”
For configuration files there is a specialized type "httpd_config_t".
semanage fcontext –a –t "httpd_config_t" “ / etc / zeo ( / . * ) ?”
Now it is enough to restart the services to complete the configuration process.
The rules we have written will allow the whole bundle to work without problems. At the same time, we provide the server and its services with additional SELinux protection. If any of the Django or ZEO components are compromised, the attacker will be restricted and will not be able to reach the system, since it will act within the
httpd_t domain.
So, we got a working custom httpd configuration without disabling SELinux. Similarly, you can create regulatory policies for any of your applications. It does not take much time and does not require serious theoretical preparation. So maybe you should not turn off SELinux?
For a more holistic understanding of the SELinux technology, I recommend that you familiarize yourself with the
Russian-language SELinux description for Fedora 13 .