Adding SELinux support to your package
This is a collection of common cases for solving SELinux issues at the packaging level. This page is only valid for Fedora Core 5 and newer.
File contexts
Problem: SELinux prevents your application from running because some files don't have the required context type.
Solution: There are two things to do in your package's scriptlets:
- Label the files with the correct type
- Save this configuration into the current SELinux policy, to keep your types in place after relabeling
Example: The awstats application is a CGI-based logfiles analyser. The CGI needs to have the httpd_sys_script_exec_t
type to run with SELinux enabled. What needs to be added to the spec file:
Requires(post): policycoreutils Requires(postun): policycoreutils ... %post semanage fcontext -a -t httpd_sys_script_exec_t '%{_datadir}/awstats/wwwroot/cgi-bin(/.*)?' 2>/dev/null || : restorecon -R %{_datadir}/awstats/wwwroot/cgi-bin || : %postun if [ $1 -eq 0 ] ; then # final removal semanage fcontext -d -t httpd_sys_script_exec_t '%{_datadir}/awstats/wwwroot/cgi-bin(/.*)?' 2>/dev/null || : fi
BEWARE: The Requires: policycoreutils
will add huge dependencies. To avoid dependency bloat it should be tried :
a. to work without manual scripts (e.g. file bug against selinux-policy and request that your package will be handled), or
a. split the SELinux related part into an own subpackage in a way like
%package selinux Requires: %name = %version-%release Requires(...): policycoreutils %post selinux ...
or
a. move current main package (without the SELinux stuff) into e.g. a -core
subpackage, keep SELinux in main and require -core
by main. Unexperienced people will get the expected result (correctly labeled program) by "yum install <package>
" while people without SELinux can install only -core
.
Discussions about this approach:
- http://www.redhat.com/archives/fedora-selinux-list/2006-April/msg00094.html
- http://www.redhat.com/archives/fedora-extras-list/2006-April/msg00852.html
An alternative approach, which might be more manageable in the long term, would be to create a policy module for the application containing only file context definitions. The reasoning behind this approach is described here: http://www.redhat.com/archives/fedora-selinux-list/2006-May/msg00108.html
Creating new types
Problem: You need to define new types for your application
Solution: You need to create an SELinux module, defining the new types and how they should be dealt with.
Example: The mock application makes a chroot to build packages. The content of the chroot needs a special type to identify files that may need the execmod feature, and mock itself needs to run in its own domain, which is allowed to do execmem and execheap when building packages for mono or java applications.
- Make a directory to create and build the policy module for the package, e.g.
/usr/share/selinux/packages/mock
and change to that directory. - Create a file called
mock.te
, with the following content:
policy_module(mock, 0.7.1) <!--###################################### --> # # type mock_t; domain_type(mock_t) type mock_exec_t; domain_entry_file(mock_t,mock_exec_t) type mock_var_lib_t; files_type(mock_var_lib_t) <!--###################################### --> # # ifdef(<code>targeted_policy',</code> allow mock_t self:process { execheap execmem }; unconfined_domain_noaudit(mock_t) role system_r types mock_t; allow mock_t mock_var_lib_t:file execmod; mock_domtrans(unconfined_t) ')
- Create a file called
mock.fc
, with the following content:
/usr/bin/mock -- gen_context(system_u:object_r:mock_exec_t,s0) /var/lib/mock(/.*)? gen_context(system_u:object_r:mock_var_lib_t,s0)
- Create a file called
mock.if
, with the following content:
<!-- <summary>Build packages in a chroot environment.</summary> --> <!--###################################### --> <!-- <summary> --> <!-- Execute the mock program in the mock domain. --> <!-- </summary> --> <!-- <param name="domain"> --> <!-- <summary> --> <!-- Domain allowed access. --> <!-- </summary> --> <!-- </param> --> # interface(<code>mock_domtrans',</code> gen_require(<code> type mock_t, mock_exec_t; ') corecmd_search_bin($1) domain_auto_trans($1, mock_exec_t, mock_t) allow $1 mock_t:fd use; allow mock_t $1:fd use; allow mock_t $1:fifo_file rw_file_perms; allow mock_t $1:process sigchld; ') <!--###################################### --> <!-- <summary> --> <!-- Create objects in the /var/lib/mock directory --> <!-- </summary> --> <!-- <param name="domain"> --> <!-- <summary> --> <!-- Domain allowed access. --> <!-- </summary> --> <!-- </param> --> <!-- <param name="file_type"> --> <!-- <summary> --> <!-- The type of the object to be created --> <!-- </summary> --> <!-- </param> --> <!-- <param name="object_class"> --> <!-- <summary> --> <!-- The object class. --> <!-- </summary> --> <!-- </param> --> # interface(<code>files_var_lib_mock_filetrans',</code> gen_require(<code> type var_t, var_lib_t, mock_var_lib_t; ') allow $1 var_t:dir search_dir_perms; allow $1 var_lib_t:dir search_dir_perms; allow $1 mock_var_lib_t:dir rw_dir_perms; type_transition $1 mock_var_lib_t:$3 $2; ')
- run
make -f /usr/share/selinux/devel/Makefile
- as root, run
semodule -i mock.pp
Some applications might not need one or more of the type enforcement (*.te
), file context (*.fc
), or interface (*.if
) files; these can be created as empty files if nothing is needed for them.
You can ship the *.{te,fc,if}
files as additional SOURCE files in your package, do the compiling in %install, and load the module with the semodule
command in %post (see the Pure-FTPd example below).
The preferred place for the module is in the directory /usr/share/selinux/packages/<package name>
.
Any place could potentially be used, though semodule
has restricted rights to read files itself, which makes a location under /usr
preferable.
We also need to standardize on some location, and this location makes it easy to find all the available modules.
You will need:
BuildRequires: selinux-policy-devel
(to build the SELinux module in %install)Requires(post): policycoreutils
(to load the module)
Discussions about this approach :
- http://www.redhat.com/archives/fedora-selinux-list/2006-April/msg00138.html
- http://www.redhat.com/archives/fedora-selinux-list/2006-April/msg00062.html
A more detailed guide to bundling policy modules within RPM packages can be found in ["PackagingDrafts/SELinux/PolicyModules"]
Adding an existing SELinux policy to an application
Problem: There are applications in Extras that perform the same task as applications in Core. They should be protected by SELinux in the same way.
Solution: Label the new binaries the same way as the already-protected ones. Check that all the functionalities still work and that you have no AVC messages. If you do, extend the policy with an SELinux module.
Example: Pure-FTPd is an FTP server available in Extras. It should be protected is the same manner that vsftpd is in Core. Thus the file /usr/sbin/pure-ftpd
should be labelled ftpd_exec_t
. But Pure-FTPd has additional features: it is capable of taking its user list from MySQL, PostgreSQL or LDAP. We have to write a module to allow it to connect to these servers.
To do that, we ship the file pureftpd.te
(the filename cannot contain dashes) in the package, containing this:
policy_module(pureftpd, 1.0) require { type ftpd_t; }; init_read_utmp(ftpd_t) init_dontaudit_write_utmp(ftpd_t) <!--# Allow connect to mysql --> corenet_tcp_connect_mysqld_port(ftpd_t) mysql_stream_connect(ftpd_t); mysql_rw_db_sockets(ftpd_t) <!--# Allow connect to postgresql --> corenet_tcp_connect_postgresql_port(ftpd_t) postgresql_stream_connect(ftpd_t) sysnet_use_ldap(ftpd_t)
Then we compile this policy in the %install step, together with the file_contexts:
mkdir selinux; cd selinux echo "%{_sbindir}/pure-ftpd system_u:object_r:ftpd_exec_t:s0" > pureftpd.fc echo '%{_localstatedir}/log/pureftpd.log system_u:object_r:xferlog_t:s0' >> pureftpd.fc touch pureftpd.if make -f %{_datadir}/selinux/devel/Makefile install -p -m 644 -D pureftpd.pp $RPM_BUILD_ROOT%{_datadir}/selinux/packages/%{name}/pureftpd.pp
- To build the policy, we need to add
BuildRequires: selinux-policy-targeted, checkpolicy
.
The build requirement of selinux-policy-targeted
is necessary because of Bug #190561 ; once this is fixed, it will be possible to use a build requirement of selinux-policy
instead.
- It is also better to package all SELinux-related files and scriptlets in a separate rpm, named <package>-selinux. This way, the administrator can choose to enable SELinux protection or not.
- Scriptlets: The -selinux package should load the policy on install, and set the file contexts. It should unload the policy on uninstall, set back the file contexts, and it should replace the policy module on upgrades. The daemon needs to be restarted when it is relabeled, but not when the policy module is replaced. We chose a set of scriptlets very similar to the ones we use for init scripts :
Requires(post): policycoreutils, initscripts, %{name} Requires(preun): policycoreutils, initscripts, %{name} Requires(postun): policycoreutils ... %post selinux if [ "$1" -le "1" ] ; then # First install semodule -i %{_datadir}/selinux/packages/%{name}/pureftpd.pp 2>/dev/null || : fixfiles -R pure-ftpd restore /sbin/service pure-ftpd condrestart > /dev/null 2>&1 fi %preun selinux if [ "$1" -lt "1" ] ; then # Final removal semodule -r pureftpd 2>/dev/null || : fixfiles -R pure-ftpd restore /sbin/service pure-ftpd condrestart > /dev/null 2>&1 fi %postun selinux if [ "$1" -ge "1" ] ; then # Upgrade semodule -i %{_datadir}/selinux/packages/%{name}/pureftpd.pp 2>/dev/null || : fi
TODO
- Do all above scriptlets work with SELinux disabled ?
- Do all above scriptlets work with SELinux not installed ?