or: How I Learned to Stop Worrying and Love the WWW

Coping with SELinux

There exists in-depth documentation into how SELinux works. There's even more tutorials that begin with “Step one: disable SELinux.” This is neither. I wanted to bang out a quick write-up on how I “cope” with SELinux rather than either dedicating the rest of my life into exploring the depths of it or saying fuck it and turning it off. I think my approach is better than just disabling it and is generally Good Enough™. I won't pretend to explain the mechanics of SELinux as there's not enough caffeine on the planet for me to feign that level of understanding but I do hope this will encourage more mere mortals like myself to try leaving it on and feel the fuzzy warmth of the security blanket that is SELinux.

Modes

There's 3 modes to SELinux: disabled (wrong!), permissive and enforcing. The first thing I do to a freshly installed Red Hat(-like) system is put it into permissive mode:

# ruby -i.bak -pe 'sub(/^(SELINUX=)[a-z]+$/, %q{\1permissive})' /etc/selinux/config
# setenforce 0
# getenforce 
Permissive

The Ruby one-liner there will set up SELinux to boot into permissive mode. The setenforce command next will immediately set it to permissive (no reboot required). Finally, getenforce should return “Permissive” to confirm that we're in permissive mode. What the hell is permissive mode then? Think of it as an “alert only” or “fail open” mode. SELinux is still running and will log violations as expected but will not actually intervene to prevent the violation from occurring. What I like about this is we can take our new system for a spin for a couple days and log the violations we'll inevitably trigger. This gives us the logs we need to generate new policies to exempt these things without going through the painful process of getting denied, generating a single policy to get around the denial, ah shit now there's a new denial, rinse and repeat.

Finally there's disabled and enforcing mode. Enforcing is full-blown, “fail closed” SELinux. Disabled is what you think it is: SELinux is completely disabled. Switching to/from disabled will cost you a reboot but you shouldn't be doing that anyways you BOFH!

Labels and Types

In the spirit of doing things Good Enough™ I'm not going to tell you how to write your own policies or even get into how exactly policies work. Just know that SELinux policies reference file and/or process labels to determine what is or isn't allowed to happen. You can typically expose these labels by adding the -Z flag to many of the existing tools you're familiar with:

# ls -Z /etc/selinux/config
system_u:object_r:selinux_config_t:s0 /etc/selinux/config

The important field on this label is that second-to-last one, the “type” field which is denoted with the “_t” suffix. That's all I have to say on labels, let's talk about the tools that essentially handle all this for us.

Our Tools

On a RHEL-ish system you'll want to install the audit and policycoreutils-python-utils packages if you don't have them already. With my system in permissive mode and having run my favorite programs and poked at my files, I typically have some violations logged. We can check it out like so:

# ausearch -m avc -ts boot
----
time->Sat Oct 16 13:41:00 2021
type=AVC msg=audit(1634406060.965:325): avc:  denied  { watch } for  pid=2799 comm="dbus-daemon" path="/var/lib/snapd/dbus-1/services" dev="nvme0n1p4" ino=3792304 scontext=system_u:system_r:xdm_t:s0-s0:c0.c1023 tcontext=system_u:object_r:snappy_var_lib_t:s0 tclass=dir permissive=1

Yep, looks like snapd tried to do a thing that SELinux disagreed with. And yeah you can use that -ts flag with different values but typically I just go with “boot” which goes back to the system's last boot. Let's generate a policy to allow this:

# ausearch -m avc -ts boot | audit2allow -M snapd20211016
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i snapd20211016.pp

Now before we run that semodule command it wants us to run I like to look at something first. You see this tool actually produced two files for us. There's snapd20211016.pp but there's also this snapd20211016.te:

# file snapd20211016.??
snapd20211016.pp: SE Linux modular policy version 1, 1 sections, mod version 20, MLS, module name snapd20211016\003
snapd20211016.te: ASCII text

Think of the .te file as the plain-text “source code” for the policy while the .pp file is that “source code” in its compiled, ready-to-rock form. I always first peek at the plain-text version to see if this is something I'm ready to install:

# cat snapd20211016.te

module snapd20211016 1.0;

require {
	type snappy_var_lib_t;
	type xdm_t;
	class dir watch;
}

#============= xdm_t ==============
allow xdm_t snappy_var_lib_t:dir watch;

This looks fine to me so I'd go ahead and install it like so (just like we were told by audit2allow):

# semodule -i snapd20211016.pp

Under what circumstances would I not go ahead and install the policy? One of two scenarios:

  1. The name I specified with audit2allow -M isn't accurate. If you pipe multiple alerts specifically you might have a mix of unrelated violations. In the spirit of Good Enough™ I typically just change the name to something like local20211016 and make myself a big ol' polka policy.
  2. The policy contains a comment telling me I can use some boolean to allow this. This means the policy doesn't need to be installed at all.

What's an SELinux boolean? They're additional SELinux controls that exist outside of the whole policies/labels/types dance. If you don't want to bother with this then that's fine; install your policy and be at peace. Personally I find it easier in the long run to just flip the boolean however as it means you'll likely never see violations like this ever again, so I'll say a few things on booleans for those who've made it this far into the blog post with your sanity in tact.

Booleans

Here's one I see every time I fire up Counter-Strike 1.6:

# cat hl_linux10032021.te

module hl_linux10032021 1.0;

require {
	type unconfined_t;
	class process execheap;
}

#============= unconfined_t ==============

#!!!! This avc can be allowed using the boolean 'selinuxuser_execheap'
allow unconfined_t self:process execheap;

See that comment there that audit2allow was nice enough to exclaim several times for me? Whatever execheap is, I love it and I want some more of it. Let's not worry about installing this policy and instead flip the boolean to enable it for good:

# semanage boolean --modify --on selinuxuser_execheap

I don't think we'll ever be seeing that violation ever again.

Let's Get This Over With

Alright once we've got our policies installed and/or booleans flipped, let's complete our journey by going back into enforcing mode:

# cd /etc/selinux/
# cp config.bak config
# setenforce 1

Some Thoughts on SELinux Administration

I figured I'd just share some observations I've made after coping with SELinux for the past several years.

Typically I'm a huge believer in the RTFM philosophy but the man pages for tools like semanage are simply bad. Almost none of the command-line options are documented here. Oddly enough I have better luck using the bash completion to see what options are available but this isn't always available depending on your shell environment and even still there's plenty of command-line options missing. Best bet if you want to explore the tools more would be to create yourself a free Red Hat developer account to be granted full access to their online documentation. There's also a book written by the SELinux maintainer for Gentoo. There's a third edition out now as of 2020 but I couldn't find it anywhere except Amazon at the time I'm writing this. This could be an interesting read but if you get this far I think you're beyond Good Enough™ and probably know way too much to be bothered with this post.

Finally it's probably a good idea to use ausearch every now and then to check for any new violations. Particularly after installing/running a new program or performing a system upgrade. Speaking of system upgrade, I like to go ahead and just throw it back into permissive mode before doing stuff like this. Just keeps those potential violations out of our way while we run all kinds of stuff and poke all kinds of things in the process. Once in a blue moon I'll be working on some fun things and get opaque errors. Sometimes it'll say permission denied even though the file permissions look fine. Sometimes it'll be much more opaque. Sometimes I won't even get any apparent error message. Before you pull your hair out troubleshooting every damn thing, remember to check ausearch! I tend to forget SELinux exists on my servers where I don't have to think about it so often as things don't change so frequently on my servers and I end up troubleshooting everything but SELinux.

Don't be afraid to revert to permissive mode for a bit if you find yourself flabbergasted and need to come back to it later to get things sorted and back into enforcing mode.

Godspeed,

Dan

#fedora
#redhat
#rhel
#selinux