From Fedora Project Wiki

Revision as of 20:04, 23 May 2012 by Jsteffan (talk | contribs) (Update Requires for correct package name to provide semanage.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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-python
Requires(postun): policycoreutils-python

...

%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-python will add some 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-python

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

  • 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 :

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 ?