Too many SELinux tutorials are too basic or too complicated. This tutorial aims to meet a middle ground. If you don’t know how to tweak booleans in SELinux, or about permissive/enforced mode, you might want to follow another tutorial and come back here later. This tutorial is for when you have some basic SELinux knowledge and want to create your own policy!

In our policy, we will secure a folder using a brand new policy. The policy will make the /secret folder read-only.

Step 0: Create /secret

This folder will be where some secret data is stored:

mkdir -m 777 /secret #create a secret folder where EVERYONE has access. SELinux will override this later. 
tee /secret/data.txt <<'EOF'
Here is some secret information which we don't want attackers to know.... We attack at dawn! Don't let anyone know!
EOF

Step 1: Create a base “deny all” policy

First, let’s go ahead and make a base “deny all” policy. This policy is going to deny anything which happens in or to /secret/.

mkdir ~/secret_policy/ #create a directory for our policy. note, this is for our policy, not our secret data.
cd ~/secret_policy/
tee secret.te > /dev/null <<'EOF' #create our base policy
policy_module(secret, 1.0)

require {
	type unconfined_t; #we need to reference this type later.
	type fs_t; #we need to reference this type later, too. 
}

type secret_t; #define our policy's label name: secret_t
EOF

Step 2: Create the Makefile, compile, install base “deny all” policy, and label /secret as ‘secret_t’

First, we need to create our Makefile. This Makefile is used to compile our type enforcement policy file (secret.te) into a policy which SELinux can import.

ln -s /usr/share/selinux/devel/Makefile . #create the Makefile which will be used to compile the .te type enforcement policy. This needs only be done once.

Next, we need to compile our policy.

make secret.pp #create a binary output named secret.pp (the compiled version of secret.te)

Next, we need to install our policy:

NOTE: If you have an existing policy named “secret”, you may need to remove it by running semodule -r secret

semodule -i secret.pp

Our policy is installed, we need to label our /secret directory. This is so SELinux can tie our policy to the directory. We need to run in permissive mode to change this label.

NOTE: This step is not possible until the policy is installed.

NOTE: You will want to re-enable SELinux Enforcement at some point, it is a security hazard to run in permissive mode.

[root@localhost secret_policy]# setenforce 0 #run in permissive mode
[root@localhost secret_policy]# chcon -t secret_t /secret
[root@localhost secret_policy]# chcon -t secret_t /secret/data.txt
[root@localhost secret_policy]# ls -lZ / | grep secret
drwxrwxrwx. root      root      unconfined_u:object_r:secret_t:s0 secret
[root@localhost secret_policy]# #yay, our label was applied to the dir. 

Step 3: Test access, set SELinux to Permissive and test application/access

First, let’s set SELinux to enforcement mode to see if we get blocked:

[root@localhost secret_policy]# setenforce 1 #run in enforcement mode
[root@localhost secret_policy]# ls -l /secret
ls: cannot access /secret: Permission denied

Cool, we got blocked! Even though /secret is set to 777! Our deny all policy works!

Now, let’s set SELinux into Permissive mode. This essentially disables SELinux from denying any access, but it will still log actions which are performed.

NOTE: You will want to re-enable SELinux Enforcement at some point, it is a security hazard to run in permissive mode.

setenforce 0

Finally, we can test out our application. Let’s say all our application needs to do is list the files in /secret/ and read data.txt.

[root@localhost secret_policy]# ls -l /secret
total 4
-rwxrwxrwx. 1 root root 19 Jul 15 21:47 data.txt
[root@localhost secret_policy]# cat /secret/data.txt
Here is some secret information which we don't want attackers to know.... We attack at dawn! Don't let anyone know!

Finally, we need to remember to re-enable SELinux. We will run into problems down the road with our label thinking it’s permissive later on if we don’t now!!!

setenforce 1

Step 4: Review required access, add access to policy

Now, we can run ausearch to find recent access denys. We can then feed that information into audit2allow to automagically generate a policy for the access needed!

[root@localhost secret_policy]# ausearch -m avc -ts recent | audit2allow

#============= secret_t ==============
allow secret_t fs_t:filesystem associate;

#============= unconfined_t ==============
allow unconfined_t secret_t:dir { getattr open read search };
allow unconfined_t secret_t:file { getattr open read relabelto };

Step 5: Compile, remove existing policy, install updated policy, and label /secret as ‘secret_t’

Let’s add those 3 relevant lines to our policy:

echo "allow secret_t fs_t:filesystem associate;" >> secret.te #tell SELinux that secret_t is a label to be used on files. 
echo "allow unconfined_t secret_t:dir { getattr open read search };" >> secret.te #tell SELinux that anything labelled unconfined_t has read access to directories labelled "secret_t"
echo "allow unconfined_t secret_t:file { getattr open read relabelto };" >> secret.te #tell SELinux that anything labelled unconfined_t has read access to files labelled "secret_t"

Now we can compile the new policy, remove the old policy, install the new policy, and refresh our labels:

**NOTE: To install the updated policy, we need to remove the existing policy. We may also need to have SELinux in Permissive mode. **

**NOTE: After the policy is removed, the labels will become invalidated. Hence, we will need to reapply our labels, as below. **

[root@localhost secret_policy]# getenforce #make sure we're in enforcing mode, otherwise we will install a permissive label by accident. 
Enforcing
[root@localhost secret_policy]# make secret.pp
Compiling targeted secret module
/usr/bin/checkmodule:  loading policy configuration from tmp/secret.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/secret.mod
Creating targeted secret.pp policy package
rm tmp/secret.mod tmp/secret.mod.fc
[root@localhost secret_policy]# semodule -r secret
libsemanage.semanage_direct_remove_key: Removing last secret module (no other secret module exists at another priority).
[root@localhost secret_policy]# semodule -i secret.pp
[root@localhost secret_policy]# chcon -t secret_t /secret #DON'T FORGET TO APPLY YOUR LABELS! THEY ARE INVALIDATED BECAUSE WE REMOVED THE MODULE EARLIER!
[root@localhost secret_policy]# chcon -t secret_t /secret/data.txt #DON'T FORGET TO APPLY YOUR LABELS! THEY ARE INVALIDATED BECAUSE WE REMOVED THE MODULE EARLIER!

Step 6: Verify intended access permissions and restrictions

[root@localhost secret_policy]# getenforce
Enforcing
[root@localhost secret_policy]# ls -lZ / | grep secret
drwxrwxrwx. root      root      unconfined_u:object_r:secret_t:s0 secret
[root@localhost secret_policy]# ls -lZ /secret/
-rwxrwxrwx. root root unconfined_u:object_r:secret_t:s0 data.txt
[root@localhost secret_policy]# cat /secret/data.txt 
Here is some secret information which we don't want attackers to know.... We attack at dawn! Don't let anyone know!
[root@localhost secret_policy]# echo "Just kidding, we are not attacking at dawn guys. That was a drill." >> /secret/data.txt #verify read only
bash: /secret/data.txt: Permission denied
[root@localhost secret_policy]# rm -rf /secret/data.txt #verify cannot delete
rm: cannot remove ‘/secret/data.txt’: Permission denied
[root@localhost secret_policy]# touch /secret/newfile.txt #verify cannot create new files in directory
touch: cannot touch ‘/secret/newfile.txt’: Permission denied

Special thanks to theurbanpenguin on YouTube for providing a tutorial which this is based off of: https://www.youtube.com/watch?v=gwrOKv79sVk