PackagingDrafts/SELinux

From FedoraProject

Jump to: navigation, search

Contents

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:

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:

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.

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)

')
/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)
<!-- <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;
')
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:

Discussions about this approach :

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
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.
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