I decided to write a blog related to this workshop. You can go through this blog, try it and prepare some questions. I would also recommend you to look at writing SELinux Policy blog in which Dan Walsh explains some basics of SELinux.
But now back to my blog ;-). I was thinking during my Friday's travelling which a good example I would show you. An example which really works. I chose the combination of apache, CUPS and portreserve services (btw. portreserve was my first policy written in Red Hat).
Ready to play with SELinux?
First, we need to setup our environment. Stop all intended services and remove the default portreserve policy. We also need to run the restorecon command to restoring contexts.
# for s in portreserve cups httpd; do service $s status 2>&1 >/dev/null; if [ $? -eq 0 ]; then service $s stop; fi; done;
# semodule -r portreserve
# for files in `rpm -ql portreserve | grep -E "(etc|bin|log|lib|run)"`; do restorecon -R -v $files; done;
# service portreserve start
Now you can check the context of portreserve service.
# ps -eZ | grep initrc
system_u:system_r:initrc_t:s0 3374 ? 00:00:00 portreserve
What happened while we were starting the service? Init script executing the portreserve binary labeled bin_t did not transition and stayed in the same context as the parent process.
initrc_t -> bin_t -> initrc_t
Really important to understand.
!! SELinux is all about labels!!
If we had tried to setup the cupsd_exec_t context for the portreserve binary, the service would have run in the cupsd_t domain.
initrc_t -> cupsd_exec_t -> cupsd_t
since we define this transition. So let's create a new policy which will say
initrc_t -> portrserve_exec_t -> portreserve_t
Use the sepolgen command.
# sepolgen –help
Since the portreserve is a standard init daemon.
# service portreserve stop
# sepolgen -t 0 `which portreserve`
Created the following files in:
./
portreserve.te # Type Enforcement file
portreserve.if # Interface file
portreserve.fc # File Contexts file
portreserve.sh # Setup Script
Install the portreserve policy.
# sh portreserve.sh
Do some checks.
# semodule -l | grep portreserve
portreserve 1.0.0
# ls -Z `which portreserve`
-rwxr-xr-x. root root system_u:object_r:portreserve_exec_t:s0 /sbin/portreserve
It was really easy ;-). We have now the basic portreserve policy. Why not try it?
# service portreserve start
# ps -eZ | grep portre
system_u:system_r:portreserve_t:s0 3498 ? 00:00:00 portreserve
Maybe you would like to ask me: "Is this policy really working? We have Enforcing mode and the daemon is running". Well I have two answers for you ;-).
1. You can check how is the initial policy good using
# ausearch -m avc -ts recent
Probably you will see some AVC messages which are really important for the next steps.
2. The magic there is the portreserve is running as a permissive domain.
This means while SELinux access ckecks are performed for these domains, they are not enforced. The kernel allows the access and reports it as an AVC denial. We can push out a new policy as permissive domain and simply collect AVC messages. Users don’t have to switch to permissive mode globally and they can stay in the enforcing mode.
Now we can easy to complete our policy using ausearch, audit2allow tools (which are my favourite)
# service portreserve restart
# ausearch -m avc -ts today | grep portreserve | audit2allow -R >> portreserve.te
Compile and load it:
# make -f /usr/share/selinux/devel/Makefile
# semodule -i portreserve.pp
Now we should test if the policy works without the "permissive domain" declaration.
# date_time=`date | cut -d " " -f 5`
# sed -i s/^permissive/#permissive/ portreserve.te
# make -f /usr/share/selinux/devel/Makefile
# semodule -i portreserve.pp
# service portreserve restart 2>&1 >/dev/null; echo $?;
# 0
Looks good. Maybe one additional check would fine
# ausearch -m avc -ts $date_time
no matches
CONGRATULATIONS.