From 908899528ebedaf76e70477b6908e7dba311e335 Mon Sep 17 00:00:00 2001 From: Vit Mojzis Date: Jun 30 2021 08:32:29 +0000 Subject: Add SELinux Independent Policy Guidelines Signed-off-by: Vit Mojzis --- diff --git a/guidelines/modules/ROOT/examples/selinux/interface-compatibility-block.if b/guidelines/modules/ROOT/examples/selinux/interface-compatibility-block.if new file mode 100644 index 0000000..29a0211 --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/interface-compatibility-block.if @@ -0,0 +1,31 @@ +######################################## +# +# Interface compatibility blocks +# +# The following definitions ensure compatibility with distribution policy +# versions that do not contain given interfaces (epel, or older Fedora +# releases). +# Each block tests for existence of given interface and defines it if needed. +# + +######################################## +## +## Allow caller to signull sssd. +## Backport from RHEL8 +## +## +## +## Domain allowed access. +## +## +# +ifndef(`sssd_signull',` + interface(`sssd_signull',` + gen_require(` + type sssd_t; + ') + + allow $1 sssd_t:process signull; + ') +') + diff --git a/guidelines/modules/ROOT/examples/selinux/myapp-selinux-edit.spec b/guidelines/modules/ROOT/examples/selinux/myapp-selinux-edit.spec new file mode 100644 index 0000000..ed04368 --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/myapp-selinux-edit.spec @@ -0,0 +1,108 @@ +%global with_selinux 1 +%global modulename mypolicy +%global selinuxtype targeted + +---------------------------------------- + +Source2: %{modulename}.te +Source3: %{modulename}.if +Source4: %{modulename}.fc + +---------------------------------------- + +%if 0%{?with_selinux} +# This ensures that the *-selinux package and all it’s dependencies are not pulled +# into containers and other systems that do not use SELinux +Requires: (%{name}-selinux if selinux-policy-%{selinuxtype}) +%endif + +---------------------------------------- + +%if 0%{?with_selinux} +# SELinux subpackage +%package selinux +Summary: Myapp SELinux policy +BuildArch: noarch +Requires: selinux-policy-%{selinuxtype} +Requires(post): selinux-policy-%{selinuxtype} +BuildRequires: selinux-policy-devel +%{?selinux_requires} + +%description selinux +Custom SELinux policy module +%endif + +-------- %build section ---------------- + +%if 0%{?with_selinux} +# SELinux policy (originally from selinux-policy-contrib) +# this policy module will override the production module +mkdir selinux +cp -p %{SOURCE2} selinux/ +cp -p %{SOURCE3} selinux/ +cp -p %{SOURCE4} selinux/ + +make -f %{_datadir}/selinux/devel/Makefile %{modulename}.pp +bzip2 -9 %{modulename}.pp +%endif + +-------- %install section -------------- + +%if 0%{?with_selinux} +install -D -m 0644 %{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 +install -D -p -m 0644 selinux/%{modulename}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{modulename}.if +%endif + +---------------------------------------- + +%if 0%{?with_selinux} +# SELinux contexts are saved so that only affected files can be +# relabeled after the policy module installation +%pre selinux +%selinux_relabel_pre -s %{selinuxtype} + +%post selinux +%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 + +%postun selinux +if [ $1 -eq 0 ]; then + %selinux_modules_uninstall -s %{selinuxtype} %{modulename} +fi + +%posttrans selinux +%selinux_relabel_post -s %{selinuxtype} +# if with_selinux +%endif + +-------- version for daemons/services -------- + +%if 0%{?with_selinux} +# SELinux contexts are saved so that only affected files can be +# relabeled after the policy module installation +%pre selinux +%selinux_relabel_pre -s %{selinuxtype} + +%post selinux +%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 +%selinux_relabel_post -s %{selinuxtype} + +if [ "$1" -le "1" ]; then # First install + # the daemon needs to be restarted for the custom label to be applied + %systemd_postun_with_restart %{modulename}.service +fi + +%postun selinux +if [ $1 -eq 0 ]; then + %selinux_modules_uninstall -s %{selinuxtype} %{modulename} + %selinux_relabel_post -s %{selinuxtype} +fi +%endif + +---------------------------------------- + +%if 0%{?with_selinux} +%files selinux +%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.* +%{_datadir}/selinux/devel/include/distributed/%{modulename}.if +%ghost %verify(not md5 size mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename} +%endif diff --git a/guidelines/modules/ROOT/examples/selinux/myapp-selinux.spec b/guidelines/modules/ROOT/examples/selinux/myapp-selinux.spec new file mode 100644 index 0000000..8d125e0 --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/myapp-selinux.spec @@ -0,0 +1,58 @@ +# defining macros needed by SELinux +%global selinuxtype targeted +%global modulename myapp + +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 +Requires: selinux-policy-%{selinuxtype} +Requires(post): selinux-policy-%{selinuxtype} +BuildRequires: selinux-policy-devel +BuildArch: noarch +%{?selinux_requires} +%description +SELinux policy modules for product. + +%prep +%setup -q + +%build +make + +%pre +%selinux_relabel_pre -s %{selinuxtype} + +%install +# install policy modules +install -D -m 0644 %{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 +install -D -p -m 0644 selinux/%{modulename}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{modulename}.if +%check + +%post +%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 +%selinux_relabel_post -s %{selinuxtype} + +if [ "$1" -le "1" ]; then # First install + # the daemon needs to be restarted for the custom label to be applied + %systemd_postun_with_restart %{modulename}.service +fi + +%postun +if [ $1 -eq 0 ]; then + %selinux_modules_uninstall -s %{selinuxtype} %{modulename} + %selinux_relabel_post -s %{selinuxtype} +fi + +%files +%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.* +%{_datadir}/selinux/devel/include/distributed/%{modulename}.if +%ghost %verify(not md5 size mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename} +%license COPYING + +%changelog +* Mon Jan 01 2017 Author Name - 0.1.0-1 +- First Build diff --git a/guidelines/modules/ROOT/examples/selinux/myapp.fc b/guidelines/modules/ROOT/examples/selinux/myapp.fc new file mode 100644 index 0000000..b4fb10d --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/myapp.fc @@ -0,0 +1 @@ +/sbin/myapp -- gen_context(system_u:object_r:myapp_exec_t,s0) diff --git a/guidelines/modules/ROOT/examples/selinux/myapp.if b/guidelines/modules/ROOT/examples/selinux/myapp.if new file mode 100644 index 0000000..ce61965 --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/myapp.if @@ -0,0 +1,2 @@ +## +My app service. \ No newline at end of file diff --git a/guidelines/modules/ROOT/examples/selinux/myapp.te b/guidelines/modules/ROOT/examples/selinux/myapp.te new file mode 100644 index 0000000..23828f2 --- /dev/null +++ b/guidelines/modules/ROOT/examples/selinux/myapp.te @@ -0,0 +1,8 @@ +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 }; \ No newline at end of file diff --git a/guidelines/modules/ROOT/pages/SELinuxDecentralizedPolicy.adoc b/guidelines/modules/ROOT/pages/SELinuxDecentralizedPolicy.adoc new file mode 100644 index 0000000..a98dc9c --- /dev/null +++ b/guidelines/modules/ROOT/pages/SELinuxDecentralizedPolicy.adoc @@ -0,0 +1,487 @@ += Decentralized SELinux Policy Guidelines + +In Fedora, there is a lot of applications and daemons +which require customized SELinux security policy. +The former approach with providing all policies as a part of the system +has been enhanced by the option to create and ship a custom policy. + +This enables releasing updated security policy when needed +as opposed to waiting for next release of SELinux policy package. +In other words custom SELinux policy is always synchronized +with the corresponding product (package). + +This guideline is dedicated to shipping custom SELinux security module +as a subpackage for a daemon or an application. + + +[[agreement-workflow]] +== Agreement workflow + +Before you start shipping custom policies, +let the SELinux team know about your intentions. +To do this, use SELinux Fedora mailing list +or contact SELinux policy maintainer: + +* mailto:selinux-policy-owner@fedoraproject.org[SELinux Policy maintainer] +* mailto:selinux@lists.fedoraproject.org[selinux@lists.fedoraproject.org] + + +[[policy-sources]] +== Policy sources + +Maintainer should base the custom policy +on a policy extracted from distribution policy package +when possible. +The Git repository with distribution policies is located on +https://github.com/fedora-selinux/selinux-policy +and +https://github.com/fedora-selinux/selinux-policy-contrib. +This way the custom policy can override the original policy -- +as long as all definitions (types, attributes, aliases, etc.) are kept. + +CAUTION: Distribution policies have GPL license, +so any policy extracted from Distribution policy +must have a GPL compatible license. + + +If new policy was created +(no suitable policy existed, or the original policy was significantly altered) +it has to be submitted to SELinux team for review before shipping. +See `sepolicy generate` tool when writing a policy from scratch. + +Please make sure the policy sources follow +https://github.com/TresysTechnology/refpolicy/wiki/StyleGuide[SELinux policy Style Guide]. + + +[[policy-source-examples]] +=== Policy source examples + +For the purpose of this example, we create a policy named myapp: + +.myapp.te +[source] +---- +include::{examplesdir}/selinux/myapp.te[] +---- +.myapp.fc +[source] +---- +include::{examplesdir}/selinux/myapp.fc[] +---- +.myapp.if +[source] +---- +include::{examplesdir}/selinux/myapp.if[] +---- + +The custom policy repository must contain the following files +(replace myapp with a name of your package): + +.... +$ ls +myapp.fc myapp.if myapp.te COPYING +.... + +It is also recommended to include a Makefile for ease of use +(can be generated using `sepolicy generate` tool) +Alternatively the following command (selinux-policy-devel package), +can be used to compile the policy source: +.... +make -f /usr/share/selinux/devel/Makefile .pp +.... + + +[[using-custom-interfaces]] +=== Using custom interfaces + +Interfaces (content of .if file) allow other policy modules to gain access +to resources of confined package when needed. Whenever a new interface is defined, +it is necessary to ship the updated interface file as part of the policy package (rpm) +so that other policy modules can use it. + +.... +%install +install -D -p -m 644 selinux/%{modulename}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{modulename}.if + +%files +%{_datadir}/selinux/devel/include/distributed/%{modulename}.if +.... + +CAUTION: All custom interfaces +(that is, interfaces that are not part of distribution policy) +_must_ be prefixed with "_ipp__" +not to be confused with distribution interfaces. + +Changes to interfaces of the original module +can only be delivered via distribution _selinux-policy-*_ packages. +If such a change is necessary, please contact the SELinux team, +or submit a pull request. +Please bear in mind that such changes will influence other policy modules +that use given interface. + +=== Backwards compatibility + +The most common problem with using custom policies +on older distributions is undefined interfaces. + +.... +Compiling targeted nagios module +selinux/nagios.te:374:ERROR 'syntax error' at token 'sssd_signull' on line 19406: + sssd_signull(nrpe_t) +.... + +This issue can be resolved by conditionally defining the missing interface. +To do this, find definition of the missing interface in +https://github.com/fedora-selinux/selinux-policy-contrib[SELinux-policy-contrib] +or +https://github.com/fedora-selinux/selinux-policy[SELinux-policy] +repository, copy it to your interface file and enclose in an _ifndef_ +statement. + +Example using _sssd_signull_ (necessary to use this interface in epel8): + +.myapp.if +[source] +---- +include::{examplesdir}/selinux/interface-compatibility-block.if[] +---- + +=== Moving type/attribute/alias definitions + +Whenever a type,attribute or alias definition is moved between modules +(this is usually done when two modules are merged together, +or some distinct part of a policy is moved to a separate module) +maintainers should include the following steps in the custom policy installation: + +* Disable the distribution version of affected module(s) before calling +`%selinux_modules_install` +** `semodule -d &> /dev/null || true;` +* Re-enable the original policy modules after +`%selinux_modules_uninstall` +** `semodule -e &> /dev/null || true;` + +These steps are necessary to avoid type, attribute or alias redefinition errors, +which may cause the custom package installation to fail. +Example of such error: + +.... +Running scriptlet: freeipa-selinux-4.8.6-1.fc33.noarch 2/4 +Re-declaration of type ipa_custodia_t +Failed to create node +Bad type declaration at /var/lib/selinux/targeted/tmp/modules/100/ipa_custodia/cil:1 +/usr/sbin/semodule: Failed! +.... + +[[file-contexts]] +=== File contexts and equivalency rules + +File context can be specified either by mapping labels to specific paths +in ".fc" policy files +(eg. `+/usr/bin/cdcc -- gen_context(system_u:object_r:cdcc_exec_t,s0)+`), +or by setting file path equivalency +(eg. `+semanage fcontext -a -e /home /export/home+`). +The latter approach mirrors labelling structure of the source directory to the target. +There is only a handful of file path equivalencies in the distribution policy, +but it is important to take them into consideration +whenever a file context rule is edited or added. + +File context rules must not reference paths +that are labelled according to an equivalency +(the new context must be assigned to the original path -- +source of the equivalency, not to the target). + +[[custom-policy-modules-and-distribution-policy]] +=== Custom policy modules and distribution policy + +It's important to note that distribution policies _should not_ use +interfaces from removable policy modules. + +When using types from custom policy modules +_stub_ interfaces should be used instead of directly requiring given type. +Stub interface is defined and used in distribution module as follows. + +.distribution_module.if +.... +... +... +######################################## +## +## DBUS stub interface. No access allowed. +## +## +## +## Domain allowed access +## +## +# +interface(`distro_stub',` + gen_require(` + type dystro_t; + ') +') +... +... +.... + +.myapp.te +.... +... +... +optional_policy(` + distro_stub() + allow distro_t myapp_log_t:file read_file_perms; +') +... +... + +.... + +As with any type defined outside of _SELinux policy base modules_, +_optional_policy_ block must be used +when using types from removable modules in distribution policy. + + +[[spec-file]] +== Spec File + +When a repository with SELinux policy sources is ready, +create your .spec file (rpmbuild configuration file). + +IMPORTANT: Please note that this guideline focuses on _targeted_ SELinux mode. +In case you intend to use the custom policy in multiple SELinux modes, +please see +https://fedoraproject.org/wiki/SELinux/IndependentPolicy_MLS[custom +product policies targeting multiple SELinux modes]. + +You can use the following example as a template (separate myapp-selinux package): + +.myapp-selinux.spec +[source] +---- +include::{examplesdir}/selinux/myapp-selinux.spec[] +---- + +Or the following example as a guide how to edit your existing spec file: + +.myapp.spec changes +[source] +---- +include::{examplesdir}/selinux/myapp-selinux-edit.spec[] +---- + +[[naming]] +=== Naming conventions + +The (sub)package containing custom SELinux policy must be named after +the package that uses given policy, followed by _-selinux_ +(eg. _myapp-selinux_). + +For intefrace naming and other policy conventions see +https://github.com/SELinuxProject/refpolicy/wiki[Reference Policy wiki pages]. + + +[[adding-dependency-to-the-spec-file-of-corresponding-package]] +=== Adding dependency to the spec file of corresponding package + +The *-selinux package should only be required on SELinux enabled systems. +Therefore the following rich dependency syntax should be used: + +.... +Requires: (%{name}-selinux if selinux-policy-%{selinuxtype}) +.... + +This ensures that the *-selinux package and all its dependencies +are not pulled into containers and other systems that do not use SELinux. + + +[[selinux-policy-module-Install]] +=== SELinux Policy module installation +SELinux modules are not installed by the spec file _install_ command, +but using the _semodule_ tool. +Maintainers should use _%selinux_modules_install_ macro, +which calls _semodule_ with all the necessary parameters. + +The actual module data are then stored in +`%{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}` +directory (you can see it specified in the _files_ section of the spec file as _%ghost_). +The "_200_" refers to the priority at which the module is installed +(see <> for more deatils). + +The only binary file used in this process is the _.pp_ archive, +which is architecture independent. +Therfore the resulting subpackage should be created as _noarch_ (`BuildArch: noarch`). + +[[selinux-policy-module-priorities]] +=== SELinux Policy module priorities + +IMPORTANT: Policy modules can be installed with different priorities. +When multiple modules of the same name exist in the system, +only the module with the highest priority takes effect. + +Distribution policy modules are installed with priority of 100. +Custom policy should always be shipped with priority of 200 +to override distribution policy. +This value is contained inside the _%selinux_modules_install_ macro +and should not be changed. + +Note that _semodule_ installs policy modules with priority of 400 by default. + +See +https://plautrba.fedorapeople.org/selinux-modules-and-priority.html[SELinux modules and priority] +for more details about module priority. + + +[[setting-booleans-during-installation]] +=== Setting Booleans During Installation + +WARNING: Setting generic booleans can open security holes in the system! + +In some cases, it is necessary to enable or disable booleans +defined in the system security policy. +Maintainers should use the following steps to do so: + +* Find a boolean that best fits your needs while avoiding generic booleans if possible (additional access in the custom policy module is preferred to switching a boolean that impacts other policy modules). + +* Specify booleans in the following format in the .spec file: ++ +.... +# default boolean values need to be changed due to the custom policy +# the change is performed by "%selinux_set_booleans" macro in %post phase +%global selinuxbooleans booleanname=1 booleanname2=0 + +.... +* 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 the following example: + +.... +%post +%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2 +%selinux_set_booleans -s %{selinuxtype} %{selinuxbooleans} + +%postun +%selinux_modules_uninstall -s %{selinuxtype} %{modulename} +%selinux_unset_booleans -s %{selinuxtype} %{selinuxbooleans} + +.... + +The boolean macros mentioned above behave as follows: + +* The value of each boolean set using _%selinux_set_booleans_ is recorded +and will be reset to the original value when "%selinux_unset_booleans" is called + +* Number of calls to _%selinux_set_booleans_ and _%selinux_unset_booleans_ +has to match in order for this mechanism to work properly + +[[port-labeling]] +=== Port Labeling + +If your custom 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 to 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}/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 port +-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 ` +command in your .spec file. +For example: + +.... +if %{_sbindir}/selinuxenabled ; then + %{_sbindir}/semanage port -d -p tcp -t product_port_t +fi +.... + +[[testing]] +=== Testing + +Place a copy of +https://pagure.io/DSP_test/blob/master/f/example_tests-DSP.yml[tests-DSP.yml] +into the resulting package distgit _tests_ directory +to test for potentially dangerous policy issues +(we recommend leaving the name at _tests-DSP.yml_, +but anything that fits _tests*.yml_ will work). +The environment section needs to be configured to your package +and the package also needs to be added to required_packages. + +Example environment configurations: + +https://src.fedoraproject.org/rpms/zabbix/pull-request/4[Zabbix] +- policy sources are placed directly in +https://src.fedoraproject.org/rpms/zabbix/tree/master[distgit] +(hence `POLICY_TAR: ''`) +.... +TEST_RPM: zabbix-selinux +TEST_POLICY: zabbix +POLICY_TAR: '' +POLICY_PATH: . +.... + +https://src.fedoraproject.org/rpms/freeipa/tree/master[Freeipa] +- policy sources live in +https://github.com/freeipa/freeipa/tree/master/selinux[freeipa upstream] +(in _selinux_ directory) +.... +TEST_RPM: freeipa-selinux +TEST_POLICY: ipa +POLICY_TAR: 'freeipa-*.tar.gz' +POLICY_PATH: 'freeipa-*/selinux' +.... + +https://src.fedoraproject.org/rpms/usbguard/tree/master[USBGuard] +- policy sources are stored in a +https://github.com/USBGuard/usbguard-selinux[separate repository] +(separate tar.gz) +.... +TEST_RPM: usbguard-selinux +TEST_POLICY: usbguard +POLICY_TAR: 'usbguard-selinux*.tar.gz' +POLICY_PATH: 'usbguard-selinux*' +.... + + +[[removing-your-custom-policy-from-the-system-policy]] + +== Removing your Custom Policy from the System Policy + +When your SELinux subpackage is ready for a release, +contact the SELinux policy maintainer. +He should remove the corresponding policy module from the SELinux distribution policy +and update the package. +You should then add a dependency on the new selinux-policy package: + +.... +# Version of selinux-policy when custom 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 a version dependency to your .spec file. + +Now your SELinux subpackage is ready for release. +It is recommended to create a group update +together with selinux-policy package +to ensure that the updating process will be successful.