Want more like this?

SELinux Concepts - but for humans

This is your SELinux dictionary!

In our previous guide we looked at how to setup modern ngnix configuration without disabling SELinux. In this post, we're going to look at a few more terms and concepts that SELinux uses, so that we can later do more advanced SELinux tricks.

SELinux is about labels

This is the core part of SELinux: labels. Everything from ports to files are all labeled a SELinux label. We can use the -Z flag on most command line utilities to view the label:

$ ls -lhZ
  dr-xr-xr-x.   6 root root system_u:object_r:boot_t:s0       5.0K Jan 27 08:41 boot/
  drwxr-xr-x.  22 root root system_u:object_r:device_t:s0     4.1K Feb  6 14:01 dev/
  drwxr-xr-x.   1 root root system_u:object_r:etc_t:s0        5.5K Feb  6 14:01 etc/
  drwxr-xr-x.   1 root root system_u:object_r:home_root_t:s0    48 Jul 14  2016 home/
  dr-xr-x---.   1 root root system_u:object_r:admin_home_t:s0  354 Jan 30 19:37 root/
  drwxrwxrwt.  14 root root system_u:object_r:tmp_t:s0         300 Feb  6 14:38 tmp/
  drwxr-xr-x.   1 root root system_u:object_r:usr_t:s0         174 Nov 16 20:58 usr/

$ ps -eZ
  LABEL                             PID TTY          TIME CMD
  system_u:system_r:init_t:s0         1 ?        00:00:05 systemd
  system_u:system_r:kernel_t:s0       2 ?        00:00:00 kthreadd
  system_u:system_r:syslogd_t:s0    655 ?        00:00:05 systemd-journal
  system_u:system_r:policykit_t:s0 1155 ?        00:00:36 polkitd

$ netstat -Z
  Active Internet connections (w/o servers)
  Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name     Security Context
  tcp        0      0 vogon-constructor:48378 ec2-54-172-133-71:https ESTABLISHED 6146/firefox         fined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  tcp        1      0 vogon-constructor:52280      CLOSE_WAIT  23503/rhythmbox      fined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Labels are purely cosmetic. On their own, they don't mean anything. But they all policies to target the thing (process/port/file).

Analogy time

Let's pretend that a SELinux label is a class on a HTML element. You add a class to an element:

<!-- SELinux HTML :) -->
<div process="/run/systemd/journal/" class="system_u object_r syslogd_var_run_t s0"></div>
<div process="/usr/bin/systemd-journald" class="system_u system_r syslogd_t s0"></div>
  allow-file-access: /run/systemd/log/;

Once we have our SE-HTML, we could write a selinux policy (which is like the CSS in this analogy):

.syslogd_t {
  allow-file-rw-access: syslog_var_run_t;

Just a disclaimer, the syntax for SELinux policies is not this pretty. We'll be covering that in the next post in the series, so make sure to subscribe:

Follow our SELinux series

Up next: Now to write your own policy for a custom app
Awesome! Please check your inbox and confirm your email.
We'll email you our latest posts plus special past content. Change your settings any time.

Dissecting a label

Here's a label from systemd-journal:


Labels can look very confusing. But they are really quite simple. We just break it into 4 parts:

  • system_u - the user (hence the _u)
  • system_r - the role (hence the _r)
  • syslogd_t - the type (hence the _t). This is the most important part - it tells SELinux to use the policy for syslog_t processes - which usually contains the bulk of the enforcement rules
  • s0 - the MLS security code. By default, SELinux doesn't use MLS, so this is meaningless (see the section below for more)

As you can see, the only important part of this label is the type (something_t). That is a common pattern in SELinux labels. For example, her is the label for /etc/systemd/system/

  • unconfined_u - see below
  • object_r - common for many configuration files, meaningless to us
  • systemd_unit_file_t - this is a systemd unit file. There is probably a policy that targets this type, allowing systemd to read unit files
  • s0 - the MLS security code. By default, SELinux doesn't use MLS, so this is meaningless

So it is easy: just read the typename_t section and you are good!


On your server, there are probably many things running as either unconfined_u, unconfined_r or unconfined_t. We saw that above with the systemd unit file.

The unconfined_X labels are reserved labels that have no policy attached. And with SELinux, no policy means there is no restriction. So if you don't want something to be policed, use unconfined. Unconfined doesn't override any other policies; so something with unconfined_u and systemd_unit_file_t will still be restricted per systemd_unit_file_t policy.

Unconfined is the default for many processes on the desktop for that exact reason. I am typing now from gnome-terminal running unconfined and inside vim, unconfined again. You often don't want to restrict what desktop users can do, hence the user of unconfined.

What is "Targeted Enforcement"?

Above we touched on the MLS codes. This is a very interesting part of SELinux.

SELinux comes with 2 modes; targeted (the default on every distro) and MLS (Multi-Level Security). Targeted policies mean that things are un-restricted by default, and only restricted if a policy is written for them (when they are targeted).

With MLS, everything is restricted by default. MLS is used by three letter agencies, such as the NSA (SELinux's initial developer). RedHat has a small section on MLS in their docs, if you are interested. MLS also uses the s0 part of the label to specify the security classification of the file.

Labeling files

With normal unix permissions, files permissions are set at file creation time. This means that every app has to think about what the correct permissions for files are.

In SELinux, things are labeled based on their characteristics. Ports are labeled based on their port number. Processes are labeled based on their executable path. And files are similar; they are labeled based on their path.

We can see the list of path labeling rules by running:

$ sudo semanage fcontext --list
  SELinux fcontext                                   type               Context

  /                                                  directory          system_u:object_r:root_t:s0
  /.*                                                all files          system_u:object_r:default_t:s0
  /bin                                               all files          system_u:object_r:bin_t:s0
  /bin/.*                                            all files          system_u:object_r:bin_t:s0
  /bin/alsaunmute                                    regular file       system_u:object_r:alsa_exec_t:s0
  /bin/bash                                          regular file       system_u:object_r:shell_exec_t:s0
  /bin/bash2                                         regular file       system_u:object_r:shell_exec_t:s0
  /bin/d?ash                                         regular file       system_u:object_r:shell_exec_t:s0
  /bin/dbus-daemon                                   regular file       system_u:object_r:dbusd_exec_t:s0
  /bin/dmesg                                         regular file       system_u:object_r:dmesg_exec_t:s0
  /bin/fish                                          regular file       system_u:object_r:shell_exec_t:s0
  /bin/hostname                                      regular file       system_u:object_r:hostname_exec_t:s0

As you can see, it is just a simple map from path regex to label. We how to add a new rule to this fcontext map in our previous guide. Adding custom fcontexts is a very important part of SELinux policy development and usage.


SELinux is conceptually simple: we label some things, then apply policies to them. Hopefully this post helps you understand how SELinux works on a 1000-foot level. If not, make sure to check Red Hat's SELinux coloring book for a different explanation. Make sure to subscribe as we continue our journey to secure a normal web-app server with a custom SELinux policy!

Get More Great Content

The intersection of marketing, design and machine learning, delivered straight to your inbox:
Awesome! Please check your inbox and confirm your email.
We'll email you our latest posts plus special past content. Change your settings any time.

Keeping Python projects secure on GitLab

Pinning projects to the very latest

Testing GraphQL with Graphene Django

The missing guide

Local Politicians Meet InfoSec - a Wordpress Disaster

The article that I didn't want to have to write

PGP for Every Email

Join us in our PGP journey

A new way of writing Gtk+ applications

Introducing Pyract - my weekend hack

Stop Disabling SELinux: A Real-World guide

Be safe from software vulnerabilities AND run your webserver

Plotinus and the quest for searchable menus

The underdog challenges a 30 year old UI convention

DMARC Secured Your Email Identity, But See How it Ruined Mailing Lists

Why people aren't posting on your mailing list

How they track you: Email Service Provider Edition

A summary of how major email marketers track their emails

Blender for Hackers - 3D modeling is just like using VIM

A very brief introduction to Blender

Edge of the World - What Open-World Games Can Teach Us About Design

Spoiler: It's all about the illusions

When fictional worlds are an accurate representations of IoT security

Ok, a little dramatized. But still truthful.

How I Destroyed my Blog's Performance with CSS Background-Blend-Modes

Just because a browser has a feature doesn't mean you should use it

Help Us Answer: The Email Signup Popup - where is it from?

Who is behind the latest wave of popups?

My WATCH runs GNU/Linux And It Is Amazing

Lennart Poettering would love it!

6 Stunning Email SignUp Form Designs with Free HTML

I've spent way to much time on dribbble researching these!
View all our content