Exercise 2.1 - Generating a generic policy

Return to Workshop

Exercise Description

In this exercise, we are going to automatically generate an SELinux policy framework, and learn to handle AVC messages that are generated by SELinux, when the application violates the policy.

Step 1: Generate an initial generic SELinux policy

Create a policy directory, and generate an initial policy. Use sepolicy generate to generate a policy for the app that we want to enable.

cd ~/src
mkdir policy
cd policy
sepolicy generate --init /usr/local/sbin/testapp

Note the last few lines in the output from sepolicy generate:

Created the following files:
/home/ec2-user/src/policy/testapp.te # Type Enforcement file
/home/ec2-user/src/policy/testapp.if # Interface file
/home/ec2-user/src/policy/testapp.fc # File Contexts file
/home/ec2-user/src/policy/testapp_selinux.spec # Spec file
/home/ec2-user/src/policy/testapp.sh # Setup Script

You will need all of these files to customize the policy, for the application.

SELinux Policy Source Compnents
  1. testapp.te is the base policy for the application. It sets the rules for the testapp_t domain

  2. testapp.if is the interface file. Interfaces are like public functions, in that they provide ways for other SELinux modules to interact with the one that you are writing

  3. testapp.fc is the file contexts file. It contains the labeling information for all filesystem objects that the policy references

  4. testapp.sh is a Red Hat provided script that compiles and loads the SELinux policy module

Step 2: Compile the policy framework

Now, compile and load the SELinux policy framework, by running the testapp.sh script. The script needs to be run with root privileges, since it changes the running SELinux configuration:

sudo ./testapp.sh

The first 9 lines of the output show the policy compilation, the loading of the policy into memory, and the automatic generation of a manpage for the policy:

Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile testapp.pp
Compiling targeted testapp module
/usr/bin/checkmodule:  loading policy configuration from tmp/testapp.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/testapp.mod
Creating targeted testapp.pp policy package
rm tmp/testapp.mod tmp/testapp.mod.fc
+ /usr/sbin/semodule -i testapp.pp
+ sepolicy manpage -p . -d testapp_t
./testapp_selinux.8

In addition to compiling and loading the policy, the testapp.sh script also generates an RPM spec file, and builds a package, containing the policy for the app. This makes it easy to redistribute the policy, when you have finishe dcreating it. You can see this in the second part of the script output:

+ /sbin/restorecon -F -R -v /usr/local/sbin/testapp
++ pwd
+ pwd=/home/ec2-user/src/policy
+ rpmbuild --define '_sourcedir /home/ec2-user/src/policy' --define '_specdir /home/ec2-user/src/policy' --define '_builddir /home/ec2-user/src/policy' --define '_srcrpmdir /home/ec2-user/src/policy' --define '_rpmdir /home/ec2-user/src/policy' --define '_buildrootdir /home/ec2-user/src/policy/.build' -ba testapp_selinux.spec
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.8GjK3r
+ umask 022
+ cd /home/ec2-user/src/policy
+ '[' /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64 '!=' / ']'
+ rm -rf /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64
++ dirname /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64
+ mkdir -p /home/ec2-user/src/policy/.build
+ mkdir /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64
+ install -d /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/selinux/packages
+ install -m 644 /home/ec2-user/src/policy/testapp.pp /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/selinux/packages
+ install -d /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/selinux/devel/include/contrib
+ install -m 644 /home/ec2-user/src/policy/testapp.if /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/selinux/devel/include/contrib/
+ install -d /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/man/man8/
+ install -m 644 /home/ec2-user/src/policy/testapp_selinux.8 /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/usr/share/man/man8/testapp_selinux.8
+ install -d /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64/etc/selinux/targeted/contexts/users/
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: testapp_selinux-1.0-1.el7.noarch
Provides: testapp_selinux = 1.0-1.el7
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh policycoreutils selinux-policy-base >= 3.13.1-229
Requires(postun): /bin/sh policycoreutils
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64
Wrote: /home/ec2-user/src/policy/testapp_selinux-1.0-1.el7.src.rpm
Wrote: /home/ec2-user/src/policy/noarch/testapp_selinux-1.0-1.el7.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.aqm3qv
+ umask 022
+ cd /home/ec2-user/src/policy
+ /usr/bin/rm -rf /home/ec2-user/src/policy/.build/testapp_selinux-1.0-1.el7.x86_64
+ exit 0

Step 3: Check to see your policy in action

If we reload the application, the newly compiled and loaded policy module will be attached to it:

sudo systemctl stop testapp
sudo systemctl start testapp
ps -efZ | grep testapp | grep -v grep
system_u:system_r:testapp_t:s0  root      8737     1  0 20:51 ?        00:00:00 /usr/local/sbin/testapp

Step 4: How does the application end up with the testapp_t context?

This is because of SELinux domain transition rules. The testapp service is started by systemd, which runs with a context of init_t, as it is our init service. Because of the transition rules, it changes contexts when the service is launched. The rule says that any process labeled as init_t will execute any binary labeled as testapp_exec_t, the newly-created process will be labeled as testapp_t.

To see those rules, type this:

sesearch -T -s init_t -t testapp_exec_t

Found 1 semantic te rules:
   type_transition init_t testapp_exec_t : process testapp_t;

The rule says that when any process labeled as init_t executes any binary labeled as testapp_exec_t, the newly-created process will be labeled as testapp_t.

End Result

At this point, we have a generic policy for the testapp application, which is set for permissive mode. Thus, the application can run, and SELinux will generate alerts when existing system policy is violated, but will take no action.


Workshop Details

Domain
Workshop
Student ID

Return to Workshop