Loading content...
If SELinux policies are the laws, security contexts are the identities that those laws apply to. Every process, file, socket, port, and kernel object in an SELinux-enabled system carries a security context—a structured label that defines its security properties.
When a process attempts to access a file, SELinux doesn't ask 'what user owns this?' It asks: 'what is the context of this process, what is the context of this file, and does the policy permit this interaction?'
Understanding contexts is essential because:
This page demystifies SELinux contexts, from their internal structure to practical management.
By the end of this page, you will understand the four-part structure of SELinux contexts, how contexts are assigned to processes and files, how domain transitions work, how to view and modify contexts, and how to diagnose and fix labeling issues.
An SELinux security context consists of four fields separated by colons:
user:role:type:level
Let's examine each component in depth.
The user field represents an SELinux identity, distinct from Linux system users. SELinux users are mapped to Linux users but serve a different purpose:
SELinux users restrict which roles a user can assume. This creates identity-based boundaries.
12345678910111213141516171819202122232425262728
# View SELinux user mappings$ semanage login -l Login Name SELinux User MLS/MCS Range Service __default__ unconfined_u s0-s0:c0.c1023 *root unconfined_u s0-s0:c0.c1023 *admin staff_u s0-s0:c0.c1023 * # The Linux user "admin" maps to SELinux user "staff_u"# All other users (__default__) map to "unconfined_u" # View available SELinux users$ semanage user -l Labeling MLS/ MLS/ SELinux User Prefix MCS Level MCS Range SELinux Roles guest_u user s0 s0 guest_rroot user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_rstaff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_rsysadm_u user s0 s0-s0:c0.c1023 sysadm_rsystem_u user s0 s0-s0:c0.c1023 system_r unconfined_runconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_ruser_u user s0 s0 user_rxguest_u user s0 s0 xguest_r # Note: Each SELinux user can only assume certain rolesThe role field determines which process types (domains) a user can enter. Roles act as an intermediate layer between users and domains:
The role hierarchy creates a Role-Based Access Control (RBAC) layer on top of SELinux's type enforcement.
The type field is the core of SELinux security. It determines what rules apply:
httpd_t, sshd_t)httpd_content_t, shadow_t)httpd_t to read httpd_content_t"Conventions:
*_t suffix for all types*_exec_t for executable files that trigger domain transitions*_log_t for log files*_var_run_t for runtime (PID, socket) files*_config_t or *_etc_t for configuration filesThe level field encodes Multi-Level Security (MLS) or Multi-Category Security (MCS) information:
s0 # Sensitivity level only
s0-s15 # Sensitivity range (s0 to s15)
s0:c0 # Sensitivity with single category
s0:c0.c255 # Sensitivity with category range
s0:c1,c5,c99 # Sensitivity with specific categories
In the targeted policy, MCS is used for container and VM isolation (each gets unique categories). Full MLS is used in military/classified systems.
| Field | For Processes | For Files | Example Values |
|---|---|---|---|
| User | Identity originator | Creator identity | system_u, unconfined_u, user_u |
| Role | Roles process can assume | Always object_r | system_r, user_r, object_r |
| Type | Domain (determines permissions) | File type (for rule matching) | httpd_t, httpd_content_t |
| Level | Security level/categories | Security level/categories | s0, s0:c0.c1023 |
In day-to-day SELinux work, the type field is what matters most. Most denials involve type mismatches. Most troubleshooting involves checking and fixing types. The user/role/level fields become relevant in specialized RBAC or MLS configurations.
SELinux contexts can be viewed using standard commands with the -Z flag (think 'Z' for 'SELinux'):
123456789101112131415161718192021222324252627
# List files with SELinux contexts$ ls -Z /var/www/html/-rw-r--r--. root root unconfined_u:object_r:httpd_content_t:s0 index.html # Parse the context: unconfined_u:object_r:httpd_content_t:s0# User: unconfined_u (creator was unconfined)# Role: object_r (always object_r for files)# Type: httpd_content_t (web content type - Apache can read)# Level: s0 (base sensitivity) # View contexts of /etc files$ ls -Z /etc/ | head -5-rw-r--r--. root root system_u:object_r:etc_t:s0 adjtime-rw-r--r--. root root system_u:object_r:etc_t:s0 aliasesdrwxr-xr-x. root root system_u:object_r:etc_t:s0 alternatives-rw-r--r--. root root system_u:object_r:locale_t:s0 bashrcdrwxr-xr-x. root root system_u:object_r:bin_t:s0 cron.d # View specific file context$ ls -Z /etc/shadow----------. root root system_u:object_r:shadow_t:s0 /etc/shadow# Type shadow_t is highly restricted - only password utilities can access # View context of executable$ ls -Z /usr/sbin/httpd-rwxr-xr-x. root root system_u:object_r:httpd_exec_t:s0 /usr/sbin/httpd# Type httpd_exec_t triggers domain transition to httpd_t upon execution1234567891011121314151617181920212223242526272829303132
# View process contexts$ ps -eZ | head -10LABEL PID TTY TIME CMDsystem_u:system_r:init_t:s0 1 ? 00:00:02 systemdsystem_u:system_r:kernel_t:s0 2 ? 00:00:00 kthreaddsystem_u:system_r:kernel_t:s0 3 ? 00:00:00 rcu_gp... # Filter for specific process$ ps -eZ | grep httpdsystem_u:system_r:httpd_t:s0 1234 ? 00:00:05 httpdsystem_u:system_r:httpd_t:s0 1235 ? 00:00:01 httpdsystem_u:system_r:httpd_t:s0 1236 ? 00:00:01 httpd # The httpd processes run in httpd_t domain# Policy rules for httpd_t determine what Apache can do # View your current process context$ id -Zunconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 # Running in unconfined_t = minimal SELinux restrictions# This is typical for user shells in targeted policy # View context of PID$ cat /proc/self/attr/currentunconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 # Detailed process info with context$ ps -Z -p 1234 -o pid,label,comm PID LABEL COMMAND 1234 system_u:system_r:httpd_t:s0 httpd12345678910111213141516171819202122232425262728
# Network port contexts$ semanage port -l | head -10SELinux Port Type Proto Port Number afs3_callback_port_t tcp 7001afs3_callback_port_t udp 7001afs_bos_port_t udp 7007afs_fs_port_t tcp 2040...http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000ssh_port_t tcp 22 # Port 80 has type http_port_t# Only domains allowed to bind http_port_t can listen on port 80 # User contexts at login$ semanage login -lLogin Name SELinux User MLS/MCS Range Service__default__ unconfined_u s0-s0:c0.c1023 *root unconfined_u s0-s0:c0.c1023 * # File system contexts (what type for new files)$ mount | grep -o 'context=[^,)]*' | headcontext=system_u:object_r:removable_t:s0 # Socket contexts$ ls -Z /run/*.sock 2>/dev/null | headsrw-rw-rw-. root root system_u:object_r:docker_var_run_t:s0 /run/docker.sockFiles receive their SELinux labels through several mechanisms. Understanding these is crucial for managing file contexts correctly.
The primary mechanism is file context definitions—regex patterns that map file paths to contexts. These are defined in .fc files and compiled into the file_contexts database:
/var/www(/.*)? system_u:object_r:httpd_content_t:s0
/var/log/httpd(/.*)? system_u:object_r:httpd_log_t:s0
/usr/sbin/httpd system_u:object_r:httpd_exec_t:s0
When a file is created or relabeled, SELinux matches its path against these patterns to determine the correct context.
1234567891011121314151617181920212223
# View file context definitions for a path$ matchpathcon /var/www/html/index.html/var/www/html/index.html system_u:object_r:httpd_content_t:s0 $ matchpathcon /etc/shadow /etc/shadow system_u:object_r:shadow_t:s0 # List all file context definitions$ semanage fcontext -l | head -20SELinux fcontext type Context/.* all files system_u:object_r:default_t:s0/[^/]+ regular file system_u:object_r:etc_runtime_t:s0/a]?quota\.group regular file system_u:object_r:quota_db_t:s0.../var/www(/.*)? all files system_u:object_r:httpd_content_t:s0 # Search for contexts matching a pattern$ semanage fcontext -l | grep httpd/usr/sbin/httpd all files system_u:object_r:httpd_exec_t:s0/var/www(/.*)? all files system_u:object_r:httpd_content_t:s0/var/log/httpd(/.*)? all files system_u:object_r:httpd_log_t:s0/etc/httpd(/.*)? all files system_u:object_r:httpd_config_t:s0...When files are created, they inherit context based on the following rules:
Type transitions in policy — If a rule exists like type_transition httpd_t var_log_t:file httpd_log_t, files created by httpd_t in var_log_t directories get type httpd_log_t
Directory default type — Without a transition rule, new files inherit the type of the parent directory
Explicit context — Programs can explicitly set file context using setfscreatecon() or context-aware tools
Package managers (RPM, dpkg) apply labels during installation using the file context definitions. This is why freshly installed packages have correct labels.
Critical distinction:
| Operation | Context Behavior | Why |
|---|---|---|
mv (same filesystem) | Preserves original context | Rename operation; same inode |
cp | Gets new context from destination path | New inode; inherits from parent |
cp -a or cp --preserve=context | Preserves original context | Explicit preservation requested |
cp -Z or cp --context=CTX | Uses specified context | Explicit context set |
rsync | Preserves if -a, destination if not | Depends on flags |
tar (with --selinux) | Preserves contexts in archive | Requires SELinux support |
Moving a file from one location to another preserves its original context, even if the destination has different context rules. Moving a file from /tmp to /var/www/html leaves it with tmp_t instead of httpd_content_t. Apache cannot read it. Always restorecon after mv to apply correct labels.
When files have incorrect labels, you need to fix them. SELinux provides several tools for context management.
The restorecon command applies the contexts defined in the file context database:
12345678910111213141516171819202122
# Restore context for a single file$ sudo restorecon /var/www/html/index.html # Restore contexts recursively$ sudo restorecon -R /var/www/html/ # Verbose mode - show what's being changed$ sudo restorecon -v /var/www/html/misfile.htmlRelabeled /var/www/html/misfile.html from unconfined_u:object_r:tmp_t:s0 to unconfined_u:object_r:httpd_content_t:s0 # Force relabeling even if context seems correct$ sudo restorecon -F /var/www/html/ # Preview changes without applying (dry-run)$ sudo restorecon -nv /var/www/html/Would relabel /var/www/html/file.txt from tmp_t to httpd_content_t # Restore entire filesystem (at boot or after major changes)$ sudo touch /.autorelabel$ sudo reboot# System will relabel all files on next bootFor temporary or one-off context changes, chcon sets contexts directly:
12345678910111213141516
# Change complete context$ sudo chcon system_u:object_r:httpd_content_t:s0 /path/to/file # Change only the type (most common)$ sudo chcon -t httpd_content_t /path/to/file # Reference another file's context$ sudo chcon --reference=/var/www/html/existing.html /path/to/newfile # Recursive change$ sudo chcon -R -t httpd_content_t /path/to/dir/ # Change user, role, or level individually$ sudo chcon -u system_u /path/to/file$ sudo chcon -r object_r /path/to/file$ sudo chcon -l s0 /path/to/fileContexts set with chcon don't update the file context database. Any subsequent restorecon (including system relabeling) will reset them to the database default. For permanent changes, use semanage fcontext.
For permanent labeling changes, modify the file context database:
1234567891011121314151617181920212223242526272829
# Add a new file context rule# Syntax: semanage fcontext -a -t <type> '<path_regex>'$ sudo semanage fcontext -a -t httpd_content_t '/srv/www(/.*)?' # The pattern uses regex; common patterns:# /path/to/file - Exact file# /path/to/dir(/.*)? - Directory and all contents# /path/to/file.* - Files starting with 'file'# /path/.*.html - All .html files in /path # After adding the rule, apply it$ sudo restorecon -R /srv/www/ # View your custom rules (local modifications)$ sudo semanage fcontext -l -CSELinux fcontext type Context/srv/www(/.*)? all files system_u:object_r:httpd_content_t:s0 # Modify an existing custom rule$ sudo semanage fcontext -m -t httpd_sys_rw_content_t '/srv/www(/.*)?'$ sudo restorecon -R /srv/www/ # Delete a custom rule$ sudo semanage fcontext -d '/srv/www(/.*)?'$ sudo restorecon -R /srv/www/ # Reverts to default (likely default_t) # Equivalency rules (treat /srv/www like /var/www)$ sudo semanage fcontext -a -e /var/www /srv/www# Now /srv/www inherits all context rules from /var/wwwA domain transition is the mechanism by which a process changes from one security domain (type) to another. This is how SELinux implements separation—instead of all processes inheriting their parent's domain, certain executions trigger transitions to appropriate domains.
For a domain transition to occur, three conditions must be satisfied:
The source domain has permission to transition to the target domain
allow init_t httpd_t : process transition;
The source domain has permission to execute the file type
allow init_t httpd_exec_t : file { execute };
The file type is an entry point for the target domain
allow httpd_t httpd_exec_t : file entrypoint;
Additionally, a type_transition rule typically exists:
type_transition init_t httpd_exec_t : process httpd_t;
This specifies: when init_t executes httpd_exec_t, the new process becomes httpd_t.
Domain Transition: init_t → httpd_t ┌─────────────────────────────────────────────────────────────┐ │ POLICY RULES │ ├─────────────────────────────────────────────────────────────┤ │ allow init_t httpd_t:process transition; # Can transition │ │ allow init_t httpd_exec_t:file execute; # Can execute │ │ allow httpd_t httpd_exec_t:file entrypoint; # Entry point │ │ type_transition init_t httpd_exec_t:process httpd_t; │ └─────────────────────────────────────────────────────────────┘ ┌──────────────────┐ execve() ┌──────────────────────────────────┐│ │ ───────► │ ││ init process │ │ httpd process ││ │ /usr/sbin/httpd │ ││ Domain: init_t │ Type: httpd_exec_t │ Domain: httpd_t ││ │ │ │└──────────────────┘ └──────────────────────────────────┘ Without domain transition: - httpd would run as init_t - Would have init_t's permissions (dangerous!) With domain transition: - httpd runs as httpd_t - Has only httpd_t's permissions (confined!)In the Reference Policy, domain transitions are typically set up using the domtrans_pattern macro:
domtrans_pattern(init_t, httpd_exec_t, httpd_t)
This expands to all necessary allow rules and the type_transition rule.
1234567891011121314151617181920212223
# Find transitions from init_t$ sesearch --type_trans -s init_t | headtype_transition init_t NetworkManager_exec_t:process NetworkManager_t;type_transition init_t abrt_dump_oops_exec_t:process abrt_dump_oops_t;type_transition init_t httpd_exec_t:process httpd_t;type_transition init_t sshd_exec_t:process sshd_t;... # Find what can transition to httpd_t$ sesearch --type_trans -t httpd_ttype_transition init_t httpd_exec_t:process httpd_t;type_transition initrc_t httpd_exec_t:process httpd_t;... # Check all three required permissions$ sesearch --allow -s init_t -t httpd_t -c process -p transitionallow init_t httpd_t:process transition; $ sesearch --allow -s init_t -t httpd_exec_t -c file -p executeallow init_t httpd_exec_t:file { execute getattr open read }; $ sesearch --allow -s httpd_t -t httpd_exec_t -c file -p entrypointallow httpd_t httpd_exec_t:file { entrypoint execute ... };The 'entrypoint' permission prevents arbitrary code from assuming a privileged domain. Without it, an attacker could write a malicious executable, somehow label it httpd_exec_t, and run it to gain httpd_t's permissions. Entry points ensure only legitimate program binaries can enter their respective domains.
Beyond files, SELinux labels network ports, interfaces, and other objects. This enables network-level access control.
Each network port has an SELinux type. Processes must have permission to bind to or connect to specific port types:
1234567891011121314151617181920212223242526272829
# View port type assignments$ semanage port -l | grep httphttp_cache_port_t tcp 8080, 8118, 8123, 10001-10010http_cache_port_t udp 3130http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000pegasus_http_port_t tcp 5988 # httpd_t can bind to http_port_t$ sesearch --allow -s httpd_t -c tcp_socket -p name_bindallow httpd_t http_port_t:tcp_socket name_bind;allow httpd_t http_cache_port_t:tcp_socket name_bind;... # Add a custom port to a type# Example: Allow Apache to bind to port 8080$ sudo semanage port -a -t http_port_t -p tcp 8081# Now httpd can also bind 8081 # View current port$ sudo semanage port -l | grep 8081http_port_t tcp 8081 # Modify existing port assignment$ sudo semanage port -m -t http_cache_port_t -p tcp 8081 # Delete custom port assignment$ sudo semanage port -d -p tcp 8081 # Note: Cannot delete system-defined ports, only custom onesInterfaces can be labeled for network access control:
1234567891011121314151617
# View interface labels$ semanage interface -lSELinux Interface Context lo system_u:object_r:lo_netif_t:s0-s15:c0.c1023eth0 system_u:object_r:netif_t:s0-s15:c0.c1023eth1 system_u:object_r:netif_t:s0-s15:c0.c1023 # Network node (IP address) labeling$ semanage node -lIP Address Netmask Protocol Context # Add a node label$ sudo semanage node -a -t myserver_node_t -p ipv4 -M 255.255.255.255 192.168.1.100 # This enables IP-based access control - processes can be restricted# to communicate only with certain IP types| Object Type | Labeling Mechanism | Management Tool |
|---|---|---|
| Files | inode extended attributes | semanage fcontext, chcon, restorecon |
| Processes | Inherited/transitioned | Automatic via exec |
| Ports | semanage port database | semanage port |
| Interfaces | semanage interface | semanage interface |
| Nodes (IPs) | semanage node | semanage node |
| User logins | semanage login | semanage login |
| Users | semanage user | semanage user |
Mislabeled files are the most common cause of SELinux denials. Here's a systematic approach to diagnosing and fixing labeling issues.
123456789101112131415161718192021222324252627282930313233
# Find recent AVC denials$ sudo ausearch -m avc -ts recent----time->Thu Jan 15 10:23:45 2025type=AVC msg=audit(1642236225.789:456): avc: denied { read } for pid=12345 comm="httpd" name="index.html" dev="sda1" ino=987654 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:tmp_t:s0 tclass=file permissive=0 # Key information:# scontext (source): httpd_t - Apache process# tcontext (target): tmp_t - WRONG! Should be httpd_content_t# tclass: file# denied: { read } # Use audit2why for explanation$ sudo ausearch -m avc -ts recent | audit2whytype=AVC msg=audit(...): avc: denied { read }... Was caused by: Missing type enforcement (TE) allow rule. You can use audit2allow to generate a loadable module to allow this access. # Better: Check if it's a labeling issue$ ls -Z /var/www/html/index.html-rw-r--r--. root root unconfined_u:object_r:tmp_t:s0 /var/www/html/index.html $ matchpathcon /var/www/html/index.html/var/www/html/index.html system_u:object_r:httpd_content_t:s0 # MISMATCH! File has tmp_t but should have httpd_content_tCommon causes of mislabeling:
| Cause | How to Identify | Solution |
|---|---|---|
File moved with mv | File has source location's type | restorecon |
| Created in wrong directory, then moved | Has parent directory's type | restorecon |
Copied with cp -a from wrong location | Preserved wrong context | restorecon |
| Custom application with no policy | No file context rule exists | semanage fcontext -a |
| Extracted from tar without SELinux support | Generic type | restorecon -R |
| System relabeling was interrupted | Random mislabeling | Full relabel |
12345678910111213141516171819202122232425262728
# Fix single file$ sudo restorecon -v /var/www/html/index.htmlRelabeled /var/www/html/index.html from unconfined_u:object_r:tmp_t:s0 to unconfined_u:object_r:httpd_content_t:s0 # Fix entire directory tree$ sudo restorecon -Rv /var/www/html/Relabeled /var/www/html/page1.html from tmp_t to httpd_content_tRelabeled /var/www/html/page2.html from user_tmp_t to httpd_content_tRelabeled /var/www/html/css/style.css from tmp_t to httpd_content_t... # If no file context rule exists for non-standard path:# First add the rule$ sudo semanage fcontext -a -t httpd_content_t '/opt/mywebsite(/.*)?' # Then apply it$ sudo restorecon -Rv /opt/mywebsite/ # Verify$ ls -Z /opt/mywebsite/-rw-r--r--. root root system_u:object_r:httpd_content_t:s0 index.html # For system-wide labeling issues (nuclear option)$ sudo touch /.autorelabel$ sudo reboot # System will relabel entire filesystem on boot# This can take 10-30 minutes on large systemsNot all denials are labeling issues. If matchpathcon shows the file has the correct type, the denial may be a legitimate policy restriction (the process shouldn't access that type) or a missing policy rule for new functionality. Use audit2why to help distinguish.
We've explored the labeling system that makes SELinux work. Let's consolidate the key points:
ls -Z, ps -Z, id -Z show contexts for files, processes, and current user.What's Next:
With contexts understood, we'll explore Type Enforcement—the core access control mechanism where policy rules meet labeled objects. You'll learn how SELinux uses types to make access decisions and how to analyze the rules that govern your system.
You now understand SELinux contexts—how they're structured, how files and processes get labeled, how labels persist and change, and how to diagnose and fix labeling issues. This knowledge is essential for effective SELinux administration.