diff --git a/doc/90-SELinux.md b/doc/90-SELinux.md new file mode 100644 index 000000000..520eb0dfd --- /dev/null +++ b/doc/90-SELinux.md @@ -0,0 +1,119 @@ +# SELinux + +## Introduction + +SELinux is a mandatory access control (MAC) system on Linux which adds a fine granular permission system for access +to all resources on the system such as files, devices, networks and inter-process communication. + +The most important questions are answered briefly in the [FAQ of the SELinux Project](http://selinuxproject.org/page/FAQ). +For more details on SELinux and how to actually use and administrate it on your systems have a look at +[Red Hat Enterprise Linux 7 - SELinux User's and Administrator's Guide](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/index.html). +For a simplified (and funny) introduction download the [SELinux Coloring Book](https://github.com/mairin/selinux-coloring-book). + + +## Policy + +Icinga Web 2 is providing its own SELinux policy for Red Hat Enterprise Linux 7 and its derivates running the targeted +policy which confines Icinga Web 2 with support for all its modules. All other distributions will require some tweaks. +It is not upstreamed to the reference policies yet. + +The policy for Icinga Web 2 will also require the policy for Icinga 2 which provides access to its interfaces. +It covers only the scenario running Icinga Web 2 in Apache HTTP Server with mod_php. + +## Installation + +There are two ways to install the SELinux Policy for Icinga Web 2 on Enterprise Linux 7. +Either install it from the provided package which is the preferred option or intall the policy manually, if you need +fixes which are not yet released. + +Verify that the system runs in enforcing mode. + + sestatus + # SELinux status: enabled + # SELinuxfs mount: /sys/fs/selinux + # SELinux root directory: /etc/selinux + # Loaded policy name: targeted + # Current mode: enforcing + # Mode from config file: enforcing + # Policy MLS status: enabled + # Policy deny_unknown status: allowed + # Max kernel policy version: 28 + +If problems occur, you can set icinga2 or httpd to run to run its domain in permissive mode. +You can change the configured mode by editing `/etc/selinux/config` and the current mode by executing `setenforce 0`. + +### Package installation + +Simply add the `selinux` subpackage to your installation. + + yum install icingaweb2-selinux + +### Manual installation + +This section describes the manual installation to support development and testing. + +As a prerequisite install the `git`, `selinux-policy-devel` and `audit` package. Enable and start the audit daemon +afterwards. + + yum install git selinux-policy-devel audit + systemctl enable auditd.service + systemctl start auditd.service + +To create and install the policy package run the installation script from the Icinga Web 2 source which also labels the +resources. + + cd packages/selinux/ + ./icingaweb2.sh + +Verify that Apache runs in its own domain `httpd_t` and the Icinga Web 2 configuration has its own context +`icingaweb2_config_t`. + + ps -eZ | grep http + # system_u:system_r:httpd_t:s0 9785 ? 00:00:00 httpd + ls -ldZ /etc/icingaweb2/ + # drwxrws---. root icingaweb2 system_u:object_r:icingaweb2_config_t:s0 /etc/icingaweb2/ + +## General + +When the SELinux policy package for Icinga Web 2 is installed, it creates its own type of apache content and labels its +configuration `icingaweb2_config_t` to allow confining access to it. + +## Types + +The configuration is labeled `icingaweb2_config_t` and other services can request access to it by using the interfaces +`icingaweb2_read_config` and `icingaweb2_manage_config`. +Files requiring read access are labeled `icingaweb2_content_t`. Files requiring write access are labeled +`icingaweb2_rw_content_t`. + +## Booleans + +SELinux is based on the least level of access required for a service to run. Using booleans you can grant more access in +a defined way. The Icinga Web 2 policy package provides the following booleans. + +**httpd_can_manage_icingaweb2_config** + +Having this boolean enabled allows httpd to write to the configuration labeled `icingaweb2_config_t`. This is enabled by +default. If not needed, you can disable it for more security. But this will disable all web based configuration of +Icinga Web 2. + +## Bugreports + +If you experience any problems while running SELinux in enforcing mode try to reproduce it in permissive mode. If the +problem persists, it is not related to SELinux because in permissive mode SELinux will not deny anything. + +When filing a bug report please add the following information additionally to the +[common ones](https://www.icinga.org/icinga/faq/): +* Output of `semodule -l | grep -e icinga2 -e icingaweb2 -e nagios -e apache` +* Output of `semanage boolean -l | grep icinga` +* Output of `ps -eZ | grep httpd` +* Output of `audit2allow -li /var/log/audit/audit.log` + +If access to a file is blocked and you can tell which one, please provided the output of `ls -lZ /path/to/file` and the +directory above. + +If asked for full audit.log, add `-w /etc/shadow -p w` to `/etc/audit/rules.d/audit.rules` and restart the audit daemon. +Reproduce the problem and add `/var/log/audit/audit.log` to the bug report. The added audit rule includes +the path of files where access was denied. + +If asked to provide full audit log with dontaudit rules disabled, execute `semodule -DB` before reproducing the problem. +After that enable the rules again to prevent auditd spamming your logfile by executing `semodule -B`. diff --git a/icingaweb2.spec b/icingaweb2.spec index 3096aaf47..692791d7a 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -45,10 +45,11 @@ Requires: %{name}-vendor-JShrink = 1.1.0-1%{?dist} Requires: %{name}-vendor-lessphp = 0.4.0-1%{?dist} Requires: %{name}-vendor-Parsedown = 1.6.0-1%{?dist} - -%description -Icinga Web 2 - +%if "%{_vendor}" == "redhat" && !(0%{?el5} || 0%{?rhel} == 5 || "%{?dist}" == ".el5" || 0%{?el6} || 0%{?rhel} == 6 || "%{?dist}" == ".el6") +%define selinux 1 +%define selinux_variants mls targeted +%{!?_selinux_policy_version: %define _selinux_policy_version %(sed -e 's,.*selinux-policy-\\([^/]*\\)/.*,\\1,' /usr/share/selinux/devel/policyhelp 2>/dev/null)} +%endif %define basedir %{_datadir}/%{name} %define bindir %{_bindir} @@ -59,6 +60,10 @@ Icinga Web 2 %define docsdir %{_datadir}/doc/%{name} +%description +Icinga Web 2 + + %package common Summary: Common files for Icinga Web 2 and the Icinga CLI Group: Applications/System @@ -100,6 +105,20 @@ Requires: php-Icinga = %{version}-%{release} Icinga CLI +%if 0%{selinux} +%package selinux +Summary: SELinux policy for Icinga Web 2 +BuildRequires: checkpolicy, selinux-policy-devel, /usr/share/selinux/devel/policyhelp, hardlink +%if "%{_selinux_policy_version}" != "" +Requires: selinux-policy >= %{_selinux_policy_version} +%endif +Requires: %{name} = %{version}-%{release} + +%description selinux +SELinux policy for Icinga Web 2 +%endif + + %package vendor-dompdf Version: 0.7.0 Release: 1%{?dist} @@ -175,8 +194,22 @@ Icinga Web 2's fork of Zend Framework 1 %prep %setup -q +%if 0%{selinux} +mkdir selinux +cp -p packages/selinux/icingaweb2.{fc,if,te} selinux +%endif %build +%if 0%{selinux} +cd selinux +for selinuxvariant in %{selinux_variants} +do + make NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile + mv icingaweb2.pp icingaweb2.pp.${selinuxvariant} + make NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile clean +done +cd - +%endif %install rm -rf %{buildroot} @@ -192,6 +225,16 @@ cp -pv packages/files/bin/icingacli %{buildroot}/%{bindir} cp -pv packages/files/public/index.php %{buildroot}/%{basedir}/public cp -prv etc/schema %{buildroot}/%{docsdir} cp -prv packages/files/config/modules/{setup,translation} %{buildroot}/%{configdir}/modules +%if 0%{selinux} +cd selinux +for selinuxvariant in %{selinux_variants} +do + install -d %{buildroot}%{_datadir}/selinux/${selinuxvariant} + install -p -m 644 icingaweb2.pp.${selinuxvariant} %{buildroot}%{_datadir}/selinux/${selinuxvariant}/icingaweb2.pp +done +cd - +/usr/sbin/hardlink -cv %{buildroot}%{_datadir}/selinux +%endif %pre getent group icingacmd >/dev/null || groupadd -r icingacmd @@ -250,6 +293,34 @@ exit 0 %attr(0755,root,root) %{bindir}/icingacli +%if 0%{selinux} +%post selinux +for selinuxvariant in %{selinux_variants} +do + /usr/sbin/semodule -s ${selinuxvariant} -i %{_datadir}/selinux/${selinuxvariant}/icingaweb2.pp &> /dev/null || : +done +/sbin/restorecon -R %{basedir} || : +/sbin/restorecon -R %{configdir} || : +/sbin/restorecon -R %{logdir} || : + +%postun selinux +if [ $1 -eq 0 ] ; then + for selinuxvariant in %{selinux_variants} + do + /usr/sbin/semodule -s ${selinuxvariant} -r icingaweb2 &> /dev/null || : + done + [ -d %{basedir} ] && /sbin/restorecon -R %{basedir} &> /dev/null || : + [ -d %{configdir} ] && /sbin/restorecon -R %{configdir} &> /dev/null || : + [ -d %{logdir} ] && /sbin/restorecon -R %{logdir} &> /dev/null || : +fi + +%files selinux +%defattr(-,root,root,0755) +%doc selinux/* +%{_datadir}/selinux/*/icingaweb2.pp +%endif + + %files vendor-dompdf %defattr(-,root,root) %{basedir}/library/vendor/dompdf diff --git a/packages/selinux/icingaweb2.fc b/packages/selinux/icingaweb2.fc new file mode 100644 index 000000000..ca5e62012 --- /dev/null +++ b/packages/selinux/icingaweb2.fc @@ -0,0 +1,7 @@ +/etc/icingaweb2(/.*)? gen_context(system_u:object_r:icingaweb2_config_t,s0) + +/usr/share/icingaweb2(/.*)? gen_context(system_u:object_r:icingaweb2_content_t,s0) + +/var/log/icingaweb2(/.*)? gen_context(system_u:object_r:icingaweb2_rw_content_t,s0) +/var/cache/icingaweb2(/.*)? gen_context(system_u:object_r:icingaweb2_rw_content_t,s0) +/var/lib/icingaweb2(/.*)? gen_context(system_u:object_r:icingaweb2_rw_content_t,s0) diff --git a/packages/selinux/icingaweb2.if b/packages/selinux/icingaweb2.if new file mode 100644 index 000000000..056ec6741 --- /dev/null +++ b/packages/selinux/icingaweb2.if @@ -0,0 +1,45 @@ +######################################## +## +## Allow the specified domain to read +## icingaweb2 configuration files. +## +## +## +## Domain allowed access. +## +## +## +# +interface(`icingaweb2_read_config',` + gen_require(` + type icingaweb2_config_t; + ') + + files_search_etc($1) + list_dirs_pattern($1, icingaweb2_config_t, icingaweb2_config_t) + read_files_pattern($1, icingaweb2_config_t, icingaweb2_config_t) + read_lnk_files_pattern($1, icingaweb2_config_t, icingaweb2_config_t) +') + +######################################## +## +## Allow the specified domain to read +## and write icingaweb2 configuration files. +## +## +## +## Domain allowed access. +## +## +## +# +interface(`icingaweb2_manage_config',` + gen_require(` + type icingaweb2_config_t; + ') + + files_search_etc($1) + manage_dirs_pattern($1, icingaweb2_config_t, icingaweb2_config_t) + manage_files_pattern($1, icingaweb2_config_t, icingaweb2_config_t) + manage_lnk_files_pattern($1, icingaweb2_config_t, icingaweb2_config_t) +') diff --git a/packages/selinux/icingaweb2.sh b/packages/selinux/icingaweb2.sh new file mode 100755 index 000000000..bbe2a2e74 --- /dev/null +++ b/packages/selinux/icingaweb2.sh @@ -0,0 +1,52 @@ +#!/bin/sh -e + +DIRNAME=`dirname $0` +cd $DIRNAME +USAGE="$0 [ --update ]" +if [ `id -u` != 0 ]; then +echo 'You must be root to run this script' +exit 1 +fi + +if [ $# -eq 1 ]; then + if [ "$1" = "--update" ] ; then + time=`ls -l --time-style="+%x %X" icingaweb2.te | awk '{ printf "%s %s", $6, $7 }'` + rules=`ausearch --start $time -m avc --raw -se icinga2` + if [ x"$rules" != "x" ] ; then + echo "Found avc's to update policy with" + echo -e "$rules" | audit2allow -R + echo "Do you want these changes added to policy [y/n]?" + read ANS + if [ "$ANS" = "y" -o "$ANS" = "Y" ] ; then + echo "Updating policy" + echo -e "$rules" | audit2allow -R >> icingaweb2.te + # Fall though and rebuild policy + else + exit 0 + fi + else + echo "No new avcs found" + exit 0 + fi + else + echo -e $USAGE + exit 1 + fi +elif [ $# -ge 2 ] ; then + echo -e $USAGE + exit 1 +fi + +echo "Building and Loading Policy" +set -x +make -f /usr/share/selinux/devel/Makefile icingaweb2.pp || exit +/usr/sbin/semodule -i icingaweb2.pp + +# Generate a man page off the installed module +#sepolicy manpage -p . -d icingaweb2_t +# Fixing the file context on /etc/icingaweb2 +/sbin/restorecon -F -R -v /etc/icingaweb2 +# Fixing the file context on /var/log/icingaweb2 +/sbin/restorecon -F -R -v /var/log/icingaweb2 +# Fixing the file context on /usr/share/icingaweb2 +/sbin/restorecon -F -R -v /usr/share/icingaweb2 diff --git a/packages/selinux/icingaweb2.te b/packages/selinux/icingaweb2.te new file mode 100644 index 000000000..e7c8f9df7 --- /dev/null +++ b/packages/selinux/icingaweb2.te @@ -0,0 +1,29 @@ +policy_module(icingaweb2, 0.0.1) + +######################################## +# +# Declarations +# + +require { + type httpd_t; +} + +## +##

+## Allow Apache to manage icingaweb2 configuration +##

+##
+gen_tunable(httpd_can_manage_icingaweb2_config, true) + +type icingaweb2_config_t; +files_config_file(icingaweb2_config_t) + +optional_policy(` + apache_content_template(icingaweb2) + icingaweb2_read_config(httpd_t) + tunable_policy(`httpd_can_manage_icingaweb2_config',` + icingaweb2_manage_config(httpd_t) + ') +') +