Creating local module quickly in CIL!

Welcome!

Today, I’ll show you how to create local policy module for testing purposes or workaround while issue will be fixed in our distro selinux-policy. Local modules will be written in CIL (common intermediate language) so this post concerns Fedora 23 and higher.

As an example we can look on following BZ:
https://bugzilla.redhat.com/show_bug.cgi?id=1349998

In description of BZ we can find this AVC:

type=AVC msg=audit(1466793837.663:1360): avc:  denied  { setrlimit }
for  pid=15770 comm="zabbix_agentd" scontext=system_u:system_r:zabbix_agent_t:s0
tcontext=system_u:system_r:zabbix_agent_t:s0 tclass=process permissive=0

Important parts of AVC for us:

avc:  denied  { setrlimit }
scontext=system_u:system_r:zabbix_agent_t:s0
tcontext=system_u:system_r:zabbix_agent_t:s0
tclass=process

From these parts we can create allowing rule in CIL. Template for creating allow rules:

(allow source_context target_context (tclass (permissions)))

If we apply template on our AVC it looks like this:

(allow zabbix_agent_t zabbix_agent_t(process (setrlimit)))

Now, we add this line to file:
(Note: Name of your local module must be different than any other used in distro policy!)

$ cat zabbix_setrlimit.cil 
(allow zabbix_agent_t zabbix_agent_t(process (setrlimit)))

Finally, we could load this local module into kernel:

# semodule -i zabbix_setrlimit.cil

And that’s it! Rule from AVC report is loaded into kernel! You can verify it using:

$ sesearch -A -s zabbix_agent_t -t zabbix_agent_t -c process -p setrlimit
Found 1 semantic av rules:
   allow zabbix_agent_t zabbix_agent_t : process { fork sigchld sigkill sigstop signull signal getsched setsched setpgid getcap setrlimit } ;

Pretty easy, don’t you think? 😉

How to modify SELinux module from distro policy?

Hi!

Today, I’ll show you how to modify SELinux module from distro policy without rebuilding whole selinux-policy rpm package. This can be useful during testing new features in your application or debugging SELinux policy. All current Fedora stable releases (Fedora 23, Fedora 24) support this feature.

For example, we have openwsman SELinux module containing following macro:

optional_policy(`
     unconfined_domain(openwsman_t)
')

That means, openwsman_t domain is part of unconfined_domain_type attribute:

$ seinfo -xtopenwsman_t | grep unconfined_domain_type
unconfined_domain_type

This macro makes SELinux domain unconfined. Let’s say, we want openwsman_t domain confined, so we need to remove rule above.
First step is download openwsman policy source files from our repo.

$ ls
openwsman.fc  openwsman.if  openwsman.te

We can edit openwsman.te file:

$ diff -u openwsman.te.old openwsman.te
--- openwsman.te.old	2016-08-17 19:38:30.617111430 +0200
+++ openwsman.te	2016-08-17 19:38:41.105155383 +0200
@@ -68,7 +68,3 @@
     sblim_getattr_exec_sfcbd(openwsman_t)
 ')
 
-optional_policy(`
-    unconfined_domain(openwsman_t)
-')
-

Modified policy needs to be compiled:

$ make -f /usr/share/selinux/devel/Makefile openwsman.pp

Now, we have compiled policy in .pp format:

$ ls
openwsman.fc  openwsman.if  openwsman.pp  openwsman.te  openwsman.te.old  tmp/

Last step, we load modified policy to kernel:

# semodule -i openwsman.pp 
libsemanage.semanage_direct_install_info: Overriding openwsman module at lower priority 100 with module at priority 400.

We can check if modified policy is loaded into kernel and openwsman_t domain is part of unconfined_domain_type:

# semodule -lfull | grep openwsman
400 openwsman                pp         
100 openwsman                pp 
 
$ seinfo -xtopenwsman_t | grep unconfined_domain_type
$

Two SELinux modules called openwsman are loaded. Firt one, with priority 400 is our local modified module. Second, with priority 100, coming from distro policy. Only SELinux module with the highest priority is active on system.

This is pretty easy and quick solution for modifying distro policy modules, isn’t it? 😉

Trouble with custom SELinux modules

Hello Everyone!

I’m here with second part of series on how to create a custom SELinux policy rpm package. In this part I’ll show disadvantages of this concept and explain a possible solution for future.

 

The concept of using a custom SELinux policy for your service works fine, but it’s limited by the m4 macro language used to write SELinux policies. The limitation lies in the fact that you can’t use a macro definition from your custom policy in the distribution policy. A nice example of this problem is the following bug.

 

When you try to build the selinux-policy rpm package you’ll end up with:

 

/usr/bin/checkmodule -M -m tmp/abrt.tmp -o tmp/abrt.mod
policy/modules/contrib/abrt.te:256:ERROR 'syntax error' at token 'docker_stream_connect' on line 12012:
	docker_stream_connect(abrt_t)
#line 256
/usr/bin/checkmodule:  error(s) encountered while parsing configuration
/usr/bin/checkmodule:  loading policy configuration from tmp/abrt.tmp
Rules.modular:74: recipe for target 'tmp/abrt.mod' failed
make: *** [tmp/abrt.mod] Error 1
error: Bad exit status from /var/tmp/rpm-tmp.B2uAfi (%install)

ABRT daemon adds support for collecting information about containers. For examples it runs:

#docker inspect $container_id
to detect a new crash in a docker container. From the SELinux point of view,  the policy for ABRT daemon needs to be changed. This is expected behaviour. The solution here is pretty simple, we just need to add this rule:

 

docker_stream_connect(abrtd_t)
This allows ABRT daemon to connect with docker over a unix stream socket. The rule needs to be included in the abrt SELinux module of distribution policy. However, the definition of

 

docker_stream_connect()
is in the docker-selinux rpm package. That is very similar to the situation when you are compiling some code written in C and you forgot to include a required header file. In this situation, the rpm package of selinux-policy cannot be build due to a missing definition.
We found a solution! Before every distribution policy package build, we download the docker policy files from github. We archive these files and distribute them along with the distribution policy source files. During build, these files are extracted and the docker interface file (*.if) is merged with all the other interface files. Using this method, we could add the rule mentioned above in the distribution policy.

 

Now, I’ll show you how to make all changes to fix this issue:

We need to install these rpm packages:

# dnf install fedpkg

We create an archive with all SELinux custom module source files:

# tar -czf /tmp/myapp-selinux.tgz myapp.if myapp.te myapp.fc
Then we clone the selinux-policy rpm sources and move it to the current working directory:

 

# fedpkg clone selinux-policy
# cd selinux-policy
# mv /tmp/myapp-selinux.tgz ./
In selinux-policy.spec file we need to add the new source file and extract this archive in the %prep phase of a build:

 

 Source36: myapp-selinux.tgz
 tar -xf %{SOURCE36}

Diff looks like this:

$ git diff
diff --git a/selinux-policy.spec b/selinux-policy.spec
index f052550..9b932cf 100644
--- a/selinux-policy.spec
+++ b/selinux-policy.spec
@@ -19,7 +19,7 @@
 Summary: SELinux policy configuration
 Name: selinux-policy
 Version: 3.13.1
-Release: 161%{?dist}
+Release: 162%{?dist}
 License: GPLv2+
 Group: System Environment/Base
 Source: serefpolicy-%{version}.tgz
@@ -57,6 +57,7 @@ Source30: booleans.subs_dist
 Source33: manpages_html.tgz
 Source34: manpages_man.tgz
 Source35: docker-selinux.tgz
+Source36: myapp-selinux.tgz
 Url: http://github.com/TresysTechnology/refpolicy/wiki
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildArch: noarch
@@ -310,6 +311,7 @@ done
 %setup -n serefpolicy-contrib-%{version} -q -b 29
 %patch1 -p1
 tar -xf %{SOURCE35}
+tar -xf %{SOURCE36}
 contrib_path=`pwd`
 %setup -n serefpolicy-%{version} -q
 %patch -p1

You increment release of build and run:

 # fedpkg mockbuild
It will build a new SELinux package including the interface file from our custom module called myapp. From now on, you can add interfaces from your custom module to the distribution policy, like in abrtd example above.
This  series of blogs showed how to easily create a custom SELinux module, ship it as rpm package or subpackage and dealt with issues of current SELinux technologies.

How to create SELinux custom policy rpm package

Hi Everyone! 
This tutorial guides you on how to create SELinux custom policy rpm package for Fedora system. I will focus on creating rpm package with policy files, not on process how to create the SELinux policy. Whole process will be described in a few easy steps. This blog is output of separation of SELinux policy for docker as rpm subpackage. I’ll use basic SELinux policy as example, but this process can be used for any SELinux policy.  At the end of this tutorial you should have working rpm package with SELinux policy usable for Fedora 22.

Before start, I install following packages:

# dnf install selinux-policy-devel rpm-build

Let’s start!
I create directory with policy files.

$ mkdir myapp-selinux-0.1
$ cd myapp-selinux-0.1/

Then I create basic SELinux policy, something like this:

$cat myapp.te
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 };
 
$ cat myapp.fc
/sbin/myapp --  gen_context(system_u:object_r:myapp_exec_t,s0)
 
$ cat myapp.if
##
 My app service.

Now, we can see following files in myapp-selinux-0.1 directory:

$ ls
myapp.fc  myapp.if  myapp.te

We also need Makefile to build this policy:

TARGETS?= myapp
MODULES?=${TARGETS:=.pp.bz2}
 
all: ${TARGETS:=.pp.bz2}
 
%.pp.bz2: %.pp
	@echo Compressing $^ -\ $@
	bzip2 -9 $^
 
%.pp: %.te
	make -f /usr/share/selinux/devel/Makefile $@
 
clean:
	rm -f *~ *.tc *.pp *.pp.bz2
	rm -rf tmp

Run:

$ make

This should create following file myapp.pp.bz2.

$ ls
Makefile  myapp.fc  myapp.if  myapp.pp.bz2  myapp.te  tmp

Create archive from directory with SELinux policy files:

$ cd ..
$ tar -czf myapp-selinux-0.1.tar.gz myapp-selinux-0.1/

Create spec file for rpm package.

%global selinuxtype	targeted
%global moduletype	services
%global modulenames	myapp
 
# Usage: _format var format
#   Expand 'modulenames' into various formats as needed
#   Format must contain '$x' somewhere to do anything useful
%global _format() export %1=""; for x in %{modulenames}; do %1+=%2; %1+=" "; done;
 
# Relabel files
%global relabel_files() \ # ADD files in *.fc file
 
 
# Version of distribution SELinux policy package 
%global selinux_policyver 3.13.1-128.6.fc22
 
# Package information
Name:			myapp-selinux
Version:		0.1
Release:		1%{?dist}
License:		GPLv2
Group:			System Environment/Base
Summary:		SELinux Policies for Docker
BuildArch:		noarch
URL:			https://HOSTNAME
Requires(post):		selinux-policy-base >= %{selinux_policyver}, selinux-policy-targeted >= %{selinux_policyver}, policycoreutils, policycoreutils-python libselinux-utils
BuildRequires:		selinux-policy selinux-policy-devel
 
Source:			%{name}-%{version}.tar.gz
 
%description
SELinux policy modules for use with myapp
 
%prep
%setup -q
 
%build
make SHARE="%{_datadir}" TARGETS="%{modulenames}"
 
%install
 
# Install SELinux interfaces
%_format INTERFACES $x.if
install -d %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype}
install -p -m 644 $INTERFACES \
	%{buildroot}%{_datadir}/selinux/devel/include/%{moduletype}
 
# Install policy modules
%_format MODULES $x.pp.bz2
install -d %{buildroot}%{_datadir}/selinux/packages
install -m 0644 $MODULES \
	%{buildroot}%{_datadir}/selinux/packages
 
%post
#
# Install all modules in a single transaction
#
%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2
%{_sbindir}/semodule -n -s %{selinuxtype} -i $MODULES
if %{_sbindir}/selinuxenabled ; then
    %{_sbindir}/load_policy
    %relabel_files
fi
 
 
%postun
if [ $1 -eq 0 ]; then
	%{_sbindir}/semodule -n -r %{modulenames} &> /dev/null || :
	if %{_sbindir}/selinuxenabled ; then
		%{_sbindir}/load_policy
		%relabel_files
	fi
fi
 
%files
%defattr(-,root,root,0755)
%attr(0644,root,root) %{_datadir}/selinux/packages/*.pp.bz2
%attr(0644,root,root) %{_datadir}/selinux/devel/include/%{moduletype}/*.if
 
%changelog
* Fri Mar 06 2015 Lukas Vrabec <lvrabec@redhat.com> - 0.1.0-1
- First Build

Note: If you create custom SELinux policy that is included in distribution SELinux policy package use following spec file:

%global selinuxtype	targeted
%global moduletype	services
%global modulenames	myapp
 
# Usage: _format var format
#   Expand 'modulenames' into various formats as needed
#   Format must contain '$x' somewhere to do anything useful
%global _format() export %1=""; for x in %{modulenames}; do %1+=%2; %1+=" "; done;
 
# Relabel files
%global relabel_files() \ # ADD files in *.fc file
 
 
# Version of distribution SELinux policy package 
%global selinux_policyver 3.13.1-128.6.fc22
 
# Package information
Name:			myapp-selinux
Version:		0.1.0
Release:		1%{?dist}
License:		GPLv2
Group:			System Environment/Base
Summary:		SELinux Policies for Docker
BuildArch:		noarch
URL:			https://HOSTNAME
Requires(post):		selinux-policy-base >= %{selinux_policyver}, selinux-policy-targeted >= %{selinux_policyver}, policycoreutils, policycoreutils-python libselinux-utils
BuildRequires:		selinux-policy selinux-policy-devel
 
Source:			%{name}-%{version}.tar.gz
 
%description
SELinux policy modules for use with myapp
 
%prep
%setup -q
 
%build
make SHARE="%{_datadir}" TARGETS="%{modulenames}"
 
%install
 
# Install policy modules
%_format MODULES $x.pp.bz2
install -d %{buildroot}%{_datadir}/selinux/packages
install -m 0644 $MODULES \
	%{buildroot}%{_datadir}/selinux/packages
 
%post
#
# Install all modules in a single transaction
#
%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2
%{_sbindir}/semodule -n -s %{selinuxtype} -i $MODULES
if %{_sbindir}/selinuxenabled ; then
    %{_sbindir}/load_policy
    %relabel_files
fi
 
 
%postun
if [ $1 -eq 0 ]; then
	%{_sbindir}/semodule -n -r %{modulenames} &> /dev/null || :
	if %{_sbindir}/selinuxenabled ; then
		%{_sbindir}/load_policy
		%relabel_files
	fi
fi
 
%files
%defattr(-,root,root,0755)
%attr(0644,root,root) %{_datadir}/selinux/packages/*.pp.bz2
 
%changelog
* Fri Mar 06 2015 Lukas Vrabec <lvrabec@redhat.com> - 0.1.0-1
- First Build

Just change name and add SELinux module name on line 3.

Copy archive with selinux files to ~/rpmbuild/SOURCES like:

$ cp myapp-selinux-0.1.tar.gz ~/rpmbuild/SOURCES/

Finally, run:

$ rpmbuild -ba myapp-selinux.spec

After these steps, you have new rpm package ready for install. Package is stored in:

ls ~/rpmbuild/RPMS/noarch/

This package can be shipped separately or as subpackage. If you have any question or idea, feel free to comment this post.