Creating Own Product Policies
In Fedora, there is a lot of applications and daemons which require customized SELinux security policy. The former approach with providing all policies only as a part of the system has been enhanced by the option to create own product policy.
With the possibility to create own product policy, required changes in a policy can be implemented immediately, so the product package maintainer does not need to wait for another SELinux policy package release. In other words, a product SELinux policy is always synchronized with a product.
This chapter is dedicated to shipping an own SELinux security module as a subpackage for a daemon or an application.
Independent SELinux Policy
While considering own product policy, a product maintainer has two options:
- Write own SELinux policy from scratch and ask SELinux team for policy review. Note that a guide how to write an SELinux policy from the scratch is not a part of this chapter (See
sepolicy generate
tool) . - Extract an SELinux policy from a distribution policy package. The Git repository with distribution policies is located on github.com/fedora-selinux/selinux-policy and github.com/fedora-selinux/selinux-policy-contrib.
Extraction Process
Agreement workflow
Before you start with shipping own product policies, let the Red Hat SELinux team know about your intentions. To do this, use Fedora mailing list or contact SELinux policy maintainer:
Git Repository setup
A product maintainer should prepare a Git repository for SELinux policy sources. Red Hat recommends, to set up custom Git repository for the policy.
# Create directory to contain the project $ mkdir myapp-selinux $ cd myapp-selinux # initialize git repository $ git init # Push git repository to remote e.g. to github.com $ git remote add origin git@github.com:username/myapp-selinux $ git push -u origin master
After created Git repository for custom policy sources, SELinux policy maintainer makes a pull request with the customized policy. If there is no policy for a product, a new policy should be created in this step and added to the mentioned repository.
When a git repository with already contains a product SELinux policy, the product maintainer should create a subpackage for it. See Preparing sources for the Policy Git Repository
The final step of an extraction process is removing the product policy from the distribution Git repository. This should be done when the independent SELinux subpackage for the product is ready. See Removing an Own Product Policy from the System Policy
Preparing sources for the Policy Git Repository
License
A Git repository should not contain only SELinux policy source files, but also a license
. For more information how to add an open source license in your repository, see the Adding a license to a repository article on the GitHub Help. Distribution policies have GPL license, so any policy extracted from Distribution policy must have GPL compatible license.
Makefile
To compile a product policy, you also need a makefile
, for example:
TARGET?=myapp MODULES?=${TARGET:=.pp.bz2} SHAREDIR?=/usr/share all: ${TARGET:=.pp.bz2} %.pp.bz2: %.pp @echo Compressing $^ -\> $@ bzip2 -9 $^ %.pp: %.te make -f ${SHAREDIR}/selinux/devel/Makefile $@ clean: rm -f *~ *.tc *.pp *.pp.bz2 rm -rf tmp *.tar.gz man: install-policy sepolicy manpage --path . --domain ${TARGET}_t install-policy: all semodule -i ${TARGET}.pp.bz2 install: man install -D -m 644 ${TARGET}.pp.bz2 ${DESTDIR}${SHAREDIR}/selinux/packages/${TARGET}.pp.bz2 install -D -m 644 ${TARGET}.if ${DESTDIR}${SHAREDIR}/selinux/devel/include/contrib/${TARGET}.if install -D -m 644 ${TARGET}_selinux.8 ${DESTDIR}${SHAREDIR}/man/man8/
Policy source examples
For the purpose of this example, we create a policy named myapp:
$ cat myapp.te policy_module(myapp,1.0) type myapp_t; type myapp_exec_t; init_daemon_domain(myapp_t, myapp_exec_t) # Grant myapp_t the signal privilege allow myapp_t self:process { signal }; $ cat myapp.fc /sbin/myapp -- gen_context(system_u:object_r:myapp_exec_t,s0) $ cat myapp.if ## My app service.
The SELinux policy Git repository should contain the following files (replace myapp with a name of your product):
$ ls Makefile myapp.fc myapp.if myapp.te LICENSE
Compiling custom policy
To compile an own and already-prepared policy, use the make
command:
$ make make -f /usr/share/selinux/devel/Makefile myapp.pp make[1]: Entering directory '/home/lvrabec/devel/documentations/examples' Compiling targeted myapp module /usr/bin/checkmodule: loading policy configuration from tmp/myapp.tmp /usr/bin/checkmodule: policy configuration loaded /usr/bin/checkmodule: writing binary representation (version 17) to tmp/myapp.mod Creating targeted myapp.pp policy package rm tmp/myapp.mod.fc tmp/myapp.mod make[1]: Leaving directory '/home/lvrabec/devel/documentations/examples' Compressing myapp.pp -> myapp.pp.bz2 bzip2 -9 myapp.pp
After a succesful compilation, make an archive containing your policy:
$ cd ../ $ tar -czf myapp-selinux.tar.gz myapp-selinux/
Using custom interfaces
It's important to note that distribution policies should not use interfaces from removable policy modules, and stub interfaces should be used instead. Stub interface is defined in distribution module and requires types from the removable module as opposed to the normal approach which would cause the distribution module to be dependant on the removable module.
$ cat distribution_module.if ... ... ######################################## ## <summary> ## DBUS stub interface. No access allowed. ## </summary> ## <param name="domain" unused="true"> ## <summary> ## Domain allowed access ## </summary> ## </param> # interface(`distro_stub',` gen_require(` type dystro_t; ') ') ... ... $ cat myapp.te ... ... optional_policy(` distro_stup() allow distro_t myapp_log_t:file read_file_perms; ') ... ...
Creating the Spec File
When a Git repository with SELinux policy sources is ready, create your product .spec file (rpmbuild configuration file).
The Preamble
First of all an SELinux policy type, a module type, and a module name should be defined:
# defining macros needed by SELinux %global selinuxtype targeted %global selinux_policyver VERSION # e.g: 3.13.1-212 %global moduletype contrib %global modulename myapp
Then it is necessary to fill in all the information about the subpackage such as a name, a version, a license, and so on.
Name: myapp-selinux Version: 1.0 Release: 1%{?dist} License: GPLv2 URL: # URL to git repository with policy source files Summary: SELinux policies for product Source0: # archive with SELinux policy sources. e.g: myapp-selinux.tar BuildArch: noarch Requires: selinux-policy >= %{selinux_policyver} BuildRequires: git BuildRequires: pkgconfig(systemd) BuildRequires: selinux-policy BuildRequires: selinux-policy-devel Requires(post): selinux-policy-base >= %{selinux_policyver} Requires(post): libselinux-utils Requires(post): policycoreutils %if 0%{?fedora} Requires(post): policycoreutils-python-utils %else Requires(post): policycoreutils-python %endif %description SELinux policy modules for product.
The %prep and %install Section
The following part of the .spec file describes the way a product policy is compiled and installed:
%prep %setup -q %build make %pre %selinux_relabel_pre -s %{selinuxtype} %install # install policy modules install -d %{buildroot}%{_datadir}/selinux/packages install -d -p %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} install -p -m 644 %{modulename}.if %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} install -m 0644 %{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages %check
After this step, a product policy is installed on your system.
The %post Section
Next step is loading a product policy into the kernel in the RPM post-install process. This step also contains the post-uninstall process to remove a product policy properly during a product uninstallation.
%post %selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{modulename}.pp.bz2 %postun if [ $1 -eq 0 ]; then %selinux_modules_uninstall -s %{selinuxtype} %{modulename} fi %posttrans %selinux_relabel_post -s %{selinuxtype}
The %files Section
The end of the .spec file contains the %files
section. This section declares which files and directories are owned by the package. Last part of spec file is changelog.
%files %defattr(-,root,root,0755) %attr(0644,root,root) %{_datadir}/selinux/packages/%{modulename}.pp.bz2 %attr(0644,root,root) %{_datadir}/selinux/devel/include/%{moduletype}/%{modulename}.if %changelog * Mon Jan 01 2017 Author Name <Author@mail-example.com> - 0.1.0-1 - First Build
Building a Package with an SELinux Product Policy
Setting Booleans During an Product Policy Installation
In some cases, it is nescessaryto enable or disable some booleans defined in a system security policy. This change should be done during an installation of an SELinux product package and it should also follow a couple of rules.
To change system booleans, use the following steps:
Find a boolean that fits your needs best. Try to avoid generic booleans, which allow many things and their change could bring security holes to the system.
Specify booleans in the following format in the .spec file:
# default boolean values need to be changed due to product policy # the change is performed by "%selinux_set_booleans" macro in %post phase %global selinuxbooleans booleanname=1 booleanname2=0
In Preamble section policycoreutils-python package should be required:
%if 0%{?fedora} Requires(post): policycoreutils-python-utils %else Requires(post): policycoreutils-python %endif
It is necessary to use special macro %selinux_set_booleans during "%post" phase of rpmbuild to make sure that the specified boolean values are set.See following example:
%post %selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{modulename}.pp.bz2 %selinux_set_booleans -s %{selinuxtype} %{selinuxbooleans} %postun %selinux_modules_uninstall -s %{selinuxtype} %{modulename} %selinux_unset_booleans -s %{selinuxtype} %{selinuxbooleans}
Usage of booleans in a .spec file follows these rules:
- If a boolean mentioned in the product .spec file is not set by user previously, it will be changed in the %post install phase and during the %post uninstall phase will be reverted.
- If a boolean mentioned in the product .spec file was set by user previously, it will be changed to a value from this file. However, during the uninstallation of a product SELinux subpackage, it will not be reverted.
Port Labeling
If your product policy does not define port labels (such as "product_port_t"), you can skip this section.
You should assign a port number and a port type for every port label. Assigning a port label should be done in %post install phase. For example, for the TCP 1111 port, the semanage port -a -t product_port_t -p tcp 1111
command should be added to the if statement in the .spec file:
if %{_sbindir}/selinuxenabled ; then %{_sbindir}/load_policy %relabel_files %{_sbindir}/semanage port -a -t product_port_t -p tcp 1111 fi
Where the a
, t
, and p
of the semanage
command mean the following:
-a Add a record of the specified object type -t SELinux type for the object from product policy -p Protocol for the specified port (tcp|udp)
For the %post uninstall phase, the port assignment should be removed. To do this, add the semanage port -d -t <PORT>
command in your .spec file, for example:
if %{_sbindir}/selinuxenabled ; then %{_sbindir}/load_policy %relabel_files %{_sbindir}/semanage port -d -p tcp -t product_port_t fi
Building a Package with an Own Product SELinux Policy
Move your SELinux product policy sources to the proper destination:
$ cp myapp-selinux.tar.gz ~/rpmbuild/SOURCES/
Build your product (sub)package with an own SELinux policy:
# rpmbuild -ba myapp-selinux.spec
After a successful build, your package is ready in the ~/rpmbuild/RPMS/noarch/
directory:
Removing an Own Product Policy from the System Policy
When is your own product SELinux subpackage ready for a release, contact the SELinux policy maintainer. He should remove a product policy from the SELinux distribution policy and update the package. A product maintainer should add dependency for the selinux-policy package:
# Version of selinux-policy when product policy was removed %global selinux_policyver POLICY_VERSION Requires: selinux-policy >= %{selinux_policyver}
If the released policy was not part of the distribution policy, there is no need to add version dependency to your .spec file.
Now is your SELinux subpackage ready to release. It is recommended to create a group update together with selinux-policy package to ensure that the updating process will be successful.
Resources
SELinux in general
- https://docs.fedoraproject.org/en-US/Fedora/25/html/SELinux_Users_and_Administrators_Guide/index.html
- https://mgrepl.wordpress.com/2016/11/01/selinux-security-policy-part1-is-it-a-magic/
- https://mgrepl.wordpress.com/2016/11/29/selinux-security-policy-part2-labels/
- https://mgrepl.wordpress.com/2016/12/13/selinux-security-policy-part-3-lables-in-action/
Why is SELinux useful
- http://danwalsh.livejournal.com/71396.html
- https://www.youtube.com/watch?v=Ysshrh4aGOs&t=3s
- https://mgrepl.wordpress.com/2015/11/04/cve-2015-5602-and-selinux/
Writing own Policy module
- https://mgrepl.fedorapeople.org/Presentations/WritingSELinuxPolicy.pdf
- https://mgrepl.wordpress.com/2015/05/20/how-to-create-a-new-initial-policy-using-sepolicy-generate-tool/
Shipping own SELinux module
- https://lukas-vrabec.com/index.php/2016/09/19/creating-local-module-quickly-in-cil/
- https://lukas-vrabec.com/index.php/2016/08/17/how-to-modify-selinux-module-from-distro-policy/
- https://lukas-vrabec.com/index.php/2015/12/01/trouble-with-custom-selinux-modules/
- https://lukas-vrabec.com/index.php/2015/07/07/how-to-create-selinux-custom-policy-rpm-package/
- https://plautrba.fedorapeople.org/blok/Fedora-SELinux-module-packaging.html
- https://mgrepl.wordpress.com/2015/07/31/cil-part2-module-priorities/