Setting up SELinux policies
Save the Kittens
Following a public appeal [1], you should not resolve problems with SELinux by simply disabling the security mechanism. A better approach is to analyze the problem and write an appropriate ruleset. This approach is also recommended if you want to place your own program under the SELinux shield. Again, some analysis of the application is necessary before you set about developing an appropriate policy.
For the first of these cases, it makes sense to run ausearch
and audit2allow
to analyze the SELinux-related problems that occur in an application and then to bundle the necessary rulesets into your own policy module. Depending on the nature of the problem, the existing policy for the application might contain a bug. In this case, you should file a bug report with the appropriate policy distributor. In the short term, of course, you can fix the problem using your own policy module, but that would be like advocating reinvention of the wheel.
My Policies
The second case, in which no policy yet exists for the application, requires some extra work. To begin, you need to design a suitable framework for the new policy. You can do this manually or with the aid of a tool, such as sepolgen
. In an iterative process, the policy then needs to be optimized. The topic of policy development is well beyond the scope of this article, however, especially considering that plenty of literature exists on the subject [2]. Instead, I will be looking at the options for distributing a new policy.
In both of the cases I've mentioned, the development process results in a new policy package. The package uses a binary format and must be loaded into the kernel security server on the target systems using semodule
. A global policy file is composed from all the active modules and a basic policy and is then loaded into the security server. This process is transparent for the user who only needs to load the module to enable the changes defined in it.
On a Fedora system, the existing SELinux policy modules reside in the directory /usr/share/selinux/targeted/
. If you are using the MLS policy, instead of the usual targeted policy, its modules are, of course, located in the /usr/share/selinux/mls/
directory. If you look closely at the size of the modules, you can see that the base module is by far the largest, weighing in at more than 200KB.
When a module is loaded, it is copied to /etc/selinux/targeted/modules/active/modules/
. When the system is rebooted, the modules in this directory are reloaded into the kernel. The above-mentioned global policy, which is composed of the individual modules, resides in the /etc/selinux/targeted/policy/
directory. You can see from the file's Modify timestamp that it changes whenever a module is loaded or unloaded.
The process for generating the new file, and then uploading the modified version to the security server kernel, looks like this:
- Generate policy files:
policy.{te,fc,if}
. - Compile policy.
- Load the policy module using:
semodule -i
.
If this process needs to take place on a large number of systems, copying the binary policy file multiple times and loading it manually is very inconvenient. Instead, the modules should be implemented as RPM packages on the systems. This method gives you a clean approach to installing and uninstalling modules and also offers the advantage of versioning.
As I mentioned previously, you need to distinguish between two types when developing RPM packages for policy modules. For modules that resolve problems with an existing policy, you might want to develop your own RPM for this local ruleset. For a completely new policy that belongs to a specific application, you can either distribute the policy with the application RPM or create a standalone RPM. Both have advantages and disadvantages. In my experience, a single specfile that contains a main RPM for the application itself and a sub-RPM for the associated policy is the best approach.
The specfile for the RPM must be built so that all the necessary steps for generating the binary policy happen automatically when generating the RPM. Additionally, you need to write some scriptlets to make sure that the module really is loaded on the target system when the package is installed and to ensure that it's really unloaded during the uninstall. After that, you just need to add a digital signature to the RPM before distributing it to the desired systems. Various tools are available for doing so. For example, Pulp, Fedora's Spacewalk, Red Hat's Satellite Server, or even a standalone software repository are all useful choices.
Packaged
Listing 1 shows an example of an RPM specfile for an SELinux policy module that adds more rules to an existing policy. If you want to package the policy along with an application, you need to extend the specfile accordingly. If you want to make sure the new module works with the targeted policy and is also capable of running under the MLS policy, for example, you would need to expand the selinux_pol
macro in the specfile and then modify the specfile itself so the module is copied to the correct directories at the appropriate places by iterating through a loop with the macro.
Listing 1: custom-selinux-policy.spec
01 %global selinux_pol targeted 02 03 Name: custom-selinux-policy 04 Version: 1.0 05 Release: 1%{?dist} 06 License: GPL v2 or later 07 Source0: local.te 08 Source1: local.fc 09 Source2: local.if 10 Group: Development/Tools 11 Summary: Custom SELinux policy module 12 BuildRoot: %{_tmppath}/%{name}-%{version}-build 13 BuildArch: noarch 14 BuildRequires: checkpolicy, selinux-policy 15 Requires: selinux-policy-targeted 16 17 %description 18 Custom SELinux policy module. 19 20 %prep 21 if [ ! -d custom-selinux-policy ]; then 22 mkdir custom-selinux-policy 23 fi 24 cp -p %{SOURCE0} %{SOURCE1} %{SOURCE2} custom-selinux-policy 25 26 %build 27 cd custom-selinux-policy 28 make -f /usr/share/selinux/devel/Makefile 29 30 %install 31 install -d %{buildroot}%{_datadir}/selinux/%{selinux_pol} 32 install -p -m 644 custom-selinux-policy/local.pp %{buildroot}%{_datadir}/selinux/%{selinux_pol}/local.pp 33 34 %post 35 /usr/sbin/semodule -i \ 36 %{_datadir}/selinux/%{selinux_pol}/local.pp &> /dev/null || : 37 38 %postun 39 if [ $1 -eq 0 ] ; then 40 /usr/sbin/semodule -r local &> /dev/null || : 41 fi 42 43 %clean 44 rm -rf %buildroot 45 46 %files 47 %defattr(-,root,root) 48 %{_datadir}/selinux/*/local.pp 49 50 %changelog 51 * Mon May 20 2013 Thorsten Scherf <tscherf@redhat.com> 52 - initial version
Running
rpmbuild -ba custom-selinux-policy
finally gives you the source and binary RPMs. You need the appropriate development tools in place on the build host. Scriptlets in the RPM specfile ensure that the policy module is loaded automatically when you install the RPM, and unloaded again if the latest version of the RPM is deleted.
I hope this article will help reduce the SELinux switch-off quota. If not, just remember: Every time someone sets setenforce 0
, one kitten dies and Daniel Walsh cries [3].