| |
@@ -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 <module name>.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
|
| |
+ ....
|
| |
+ ...
|
| |
+ ...
|
| |
+ ########################################
|
| |
+ ## <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;
|
| |
+ ')
|
| |
+ ')
|
| |
+ ...
|
| |
+ ...
|
| |
+ ....
|
| |
+
|
| |
+ .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 <<selinux-policy-module-priorities>> 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 <PORT>`
|
| |
+ 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.
|
| |
This is not necessary.