SELinux Concepts

Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) system that enhances Linux security. "Mandatory" means access control is strictly enforced by predefined policy rules—users and processes cannot modify these rules at will, ensuring security is not left to individual discretion. SELinux is available in major distributions, including Amazon Linux 2023 (AL2023) and Bottlerocket. This post is the part 1 of a series on SELinux.

Labels and Contexts

SELinux labels every file and process using a quadruple: (user:role:type:level). In SELinux, labels are the only input for access control decisions. It does not matter which Linux user owns a file or started a process—only the labels matter. The terms "label" and "context" are often used interchangeably. Many Linux commands, such as ls, ps, and id, support the -Z option to display SELinux contexts:

1bash-5.2# ps -Z 52482
2LABEL                               PID TTY      STAT   TIME COMMAND
3system_u:system_r:container_t:s0  52482 ?        Ss     0:00 sh -c yum -y -q ins
  • SELinux user: system_u (distinct from the Linux user)
  • SELinux role: system_r
  • SELinux type: container_t
  • SELinux level: s0 (can also include level ranges and categories, e.g., s0-s2:c0.c1023)

In SELinux naming conventions:

  • Users, roles, and types have suffixes _u, _r, and _t respectively
  • Sensitivity levels use the prefix s, while categories use c

Type

The type is the core of SELinux. For processes, the type defines what a process can do and is why SELinux is often called a "Type Enforcement" (TE) security model. The same program can run under different types depending on how it is started. For example, when launching httpd in AL2023:

  • Launch it with systemd (sudo systemctl start httpd): the httpd process has system_u:system_r:httpd_t:s0
  • Launch it directly (sudo /usr/sbin/httpd): the httpd process has unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

For processes, "type" and "domain" are interchangeable terms.

Users and Roles

SELinux supports role-based access control (RBAC) using SELinux users and roles. The relationship between users, roles, and types can be summarized as:

  1. Users are assigned roles (userrole)
  2. Roles are allowed to access specific types (roletype)

For example, a SELinux policy could enforce that only certain SELinux users can execute a program by:

  1. Enforcing a program to be executed as certain types, e.g., hello_t
  2. Associating hello_t to hello_r
  3. Associating se_foo_u to hello_r, and se_bar_u to a role other than hello_r

Then, any Linux user that is mapped to se_bar_u cannot run the program. Learn more about roles at The purpose of SELinux roles.

Sensitivity (MLS - Multi-Level Security)

Sensitivity labels are optional in SELinux and are part of Multi-Level Security (MLS). You can check if MLS is enabled with:

1bash-5.2# sestatus | grep MLS
2Policy MLS status:              enabled

MLS implements hierarchical security levels based on the Bell-LaPadula model, a security model characterized by "no read up, no write down": a process cannot read anything with a higher confidentiality level nor write/communicate to any resource with a lower confidentiality level. Sensitivity levels are numbered from s0 (lowest) to s15 (highest) by default. A process or file can have a single level: s0, or A range of levels: s0-s3 (clearance from s0 to s3).

MLS is commonly used in government and military environments where information classification is critical. Most general-purpose Linux distributions run with MLS disabled or use only s0, as the strict access controls can interfere with normal operations.

Categories (MCS - Multi-Category Security)

Categories work like tags for processes and files, providing compartmentalization within the same sensitivity level. Unlike MLS's hierarchical levels, categories are non-hierarchical—they provide lateral separation rather than vertical classification. Categories are numbered from c0 to c1023 and can be combined in ranges (e.g., c0.c5 (categories 0 through 5)) or sets (e.g., c0,c3,c7 (specific categories 0, 3, and 7)):

A process can only access resources that share at least one category with it, or resources with no categories at all. Docker and Kubernetes use MCS categories to isolate containers. Each container gets a pair of categories and containers in the same K8s pod have the same pair. By convention, container runtimes use at least two categories per tenant to reduce the probability of category collision in multi-tenant environments. See go-selinux for implementation details.

Class and Permission

A class is a named collection of permissions. For example:

  1. The class process has permissions: fork, transition, sigchld, sigkill, etc. It looks like permissions map one-to-one to syscalls, but that's not the case
  2. The class service has permissions: start, stop, status, reload, enable, disable

Each class can have a maximum of 32 permissions since it is stored as a 32-bit field in the kernel.

Subjects, Objects, and Actions

SELinux is fundamentally about answering questions of the form "Can a subject perform an action on an object?". Often, the subject is a process, the object is a process or file, and the action is a combination of class and permission. For example, an action file:read means the permission read in the class file.

Type transition

Type transition automatically assigns security contexts to newly created objects based on defined rules. The type transition rule specifies the labeling and object creation allowed between the source_type and target_type. For example, (typetransition init_t os_t process system_t) says PID 1 starts most daemons as "system_t".

Policy

SELinux policy is a collection of rules that defines "can a subject perform an action on an object?". The compiled policy file is stored at /etc/selinux:

1ls -lh /etc/selinux/targeted/policy/policy.33
2-rw-r--r--. 1 root root 3.5M Jan 22 01:48 /etc/selinux/targeted/policy/policy.33

Policies can be thought of as a table (source: SELinux from the inside out):

Rows and columns represent types, identified by SID. Cells represent class and permission. See the structure atavc_entry.

Access Vector Cache (AVC)

SELinux caches access control decisions in the AVC. Think of SELinux as a client-server model, where AVC is the cache on the server. Permission denies caused by SELinux can be found by journalctl -k | grep -i avc.

Common Intermediate Language (CIL)

Common Intermediate Language is a human-readable policy language that sits between high-level policy languages (such as the reference language) and the low-level kernel policy representation. Bottlerocket implements SELinux using CIL only.