Loading learning content...
An SELinux system without a policy is like a courtroom without laws—the infrastructure exists, but no decisions can be made. SELinux policies are the comprehensive rule sets that define every permitted interaction between subjects (processes) and objects (files, sockets, devices) in the system.
A typical SELinux policy contains hundreds of thousands of rules, carefully structured to:
Understanding SELinux policies is essential for anyone who administers, develops for, or secures Linux systems. This page explores policy architecture from first principles through practical application.
By the end of this page, you will understand the types of SELinux policies, the structure and syntax of policy rules, how the Reference Policy framework organizes rules into modules, and the process of policy development and customization. You'll be equipped to read, analyze, and modify SELinux policies.
SELinux supports multiple policy types, offering different trade-offs between security guarantees and operational flexibility. Understanding these types is the first step in working with SELinux policies.
The targeted policy is the default on Red Hat-derived distributions. It embodies a pragmatic approach:
unconfined_t domain with minimal restrictionsThis approach acknowledges that comprehensive policy for every application is impractical in many environments. The targeted policy protects against the most likely attack vectors—network service compromises—while minimizing user-facing policy issues.
The strict policy takes a comprehensive approach:
Strict policies are used in high-security environments where the additional administrative overhead is justified (government, military, financial systems).
Multi-Level Security (MLS) policy adds Bell-LaPadula-style security levels:
MLS is used in environments processing classified data at multiple levels (e.g., a system handling both Secret and Top Secret information).
| Policy Type | Coverage | Complexity | Use Case |
|---|---|---|---|
| Targeted | Network services confined, users unconfined | Low-Medium | Enterprise servers, default deployments |
| Strict | All processes confined | High | High-security environments |
| MLS | Full confinement + security levels | Very High | Multi-level classified systems |
| Minimum | Base types only, very minimal | Very Low | Embedded, minimal systems |
Independent of policy type, SELinux operates in one of three modes:
Enforcing Mode
SELinux policy is active and access violations are denied. This is production mode.
Permissive Mode
SELinux logs what would be denied but allows access. Use for debugging and policy development—never in production.
Disabled Mode
SELinux is completely inactive. No policy enforcement, no context labeling. Not recommended.
123456789101112131415161718192021222324252627282930
# Check current SELinux status$ getenforceEnforcing # Detailed status information$ sestatusSELinux status: enabledSELinuxfs mount: /sys/fs/selinuxSELinux root directory: /etc/selinuxLoaded policy name: targetedCurrent mode: enforcingMode from config file: enforcingPolicy MLS status: enabledPolicy deny_unknown status: allowedMemory protection checking: actual (secure)Max kernel policy version: 33 # Temporarily switch to permissive mode (requires root)# Change persists until reboot$ sudo setenforce 0$ getenforcePermissive # Switch back to enforcing mode$ sudo setenforce 1 # Persistent configuration is in /etc/selinux/config$ cat /etc/selinux/configSELINUX=enforcingSELINUXTYPE=targetedSwitching to 'disabled' mode removes security labels from files as they're created or modified. Re-enabling SELinux later requires relabeling the entire filesystem. Use permissive mode for troubleshooting—it provides the same debugging information without the destructive side effects.
SELinux policies are composed of several distinct components that work together to define the security model. Let's examine each:
The foundation of SELinux is types. Every subject (process) and object (file, socket, etc.) has a type. Types are declared in policy:
type httpd_t; # Type for Apache processes
type httpd_exec_t; # Type for Apache executable
type httpd_content_t; # Type for web content files
type httpd_log_t; # Type for Apache log files
type httpd_config_t; # Type for Apache config files
Type naming convention: <name>_t for process types (domains), <name>_exec_t for executables, <name>_log_t for logs, etc.
Attributes are named groups of types. Rules can reference attributes, applying to all types with that attribute:
attribute domain; # All process types
attribute file_type; # All file types
attribute exec_type; # All executable types
attribute port_type; # All port types
# Assign types to attributes
typeattribute httpd_t domain;
typeattribute httpd_exec_t exec_type, file_type;
This enables writing rules that apply to categories of types without listing each one.
The heart of policy is access vector (AV) rules that specify what's allowed:
# Basic allow rule syntax:
# allow source_type target_type : object_class permissions;
allow httpd_t httpd_content_t : file { read open getattr };
allow httpd_t httpd_log_t : file { create write append };
Rules can also deny, audit, or conditionally allow access.
123456789101112131415161718192021222324252627282930313233
# ALLOW RULES - Grant permissions# Syntax: allow source target : class { permissions };allow httpd_t httpd_content_t : file { read open getattr };allow httpd_t httpd_log_t : dir { search write add_name };allow httpd_t httpd_log_t : file { create write append }; # DONTAUDIT RULES - Deny silently (suppress audit messages)# Useful for harmless access attempts that would flood logsdontaudit httpd_t etc_t : dir search; # AUDITALLOW RULES - Allow but log (for monitoring)# Useful for detecting unusual but permitted behaviorauditallow httpd_t admin_home_t : file read; # NEVERALLOW RULES - Policy compile-time assertions# Ensures certain rules can never exist in policyneverallow httpd_t shadow_t : file { read write };neverallow * kernel_t : process ptrace; # TYPE TRANSITION RULES - Domain transitions# Syntax: type_transition source target : class new_type;# When source creates object of class on target, use new_typetype_transition httpd_t httpd_log_t : file httpd_log_t;type_transition init_t httpd_exec_t : process httpd_t; # ROLE ALLOW RULES - Role transitionsrole_transition user_r httpd_exec_t system_r; # CONDITIONAL RULES - Based on boolean valuesbool httpd_can_network_connect false;if (httpd_can_network_connect) { allow httpd_t port_t : tcp_socket name_connect;}Object classes represent categories of kernel objects. Each class has a set of applicable permissions:
| Object Class | Examples | Key Permissions |
|---|---|---|
file | Regular files | read, write, execute, open, create, unlink, getattr, setattr |
dir | Directories | search, add_name, remove_name, read, write, rmdir |
lnk_file | Symbolic links | read, write, create, unlink |
sock_file | Unix sockets | read, write, create, connectto |
tcp_socket | TCP sockets | create, connect, listen, accept, send, recv |
udp_socket | UDP sockets | create, bind, send, recv |
process | Processes | signal, ptrace, transition, fork, exec |
capability | Linux caps | sys_admin, net_admin, dac_override |
The complete list comprises over 80 object classes with hundreds of unique permissions.
To see all object classes and permissions available on your system, consult the policy source or use: seinfo --class and seinfo --permission. These tools reveal the complete access control vocabulary.
Writing SELinux policy from scratch would be impractical—a complete system requires policies for hundreds of programs. The Reference Policy solves this by providing a well-structured, modular policy base that distributions customize.
The Reference Policy organizes rules into modules, each handling a specific service or subsystem:
refpolicy/
├── policy/
│ ├── modules/
│ │ ├── admin/ # Administrative tools
│ │ │ ├── sudo.te # sudo rules
│ │ │ ├── sudo.if # sudo interface
│ │ │ └── sudo.fc # sudo file contexts
│ │ ├── services/ # System services
│ │ │ ├── apache.te
│ │ │ ├── postgresql.te
│ │ │ └── sshd.te
│ │ ├── kernel/ # Kernel and core
│ │ ├── roles/ # Role definitions
│ │ └── system/ # System components
│ └── support/ # Macros and helpers
├── config/
└── doc/
Each module consists of three files:
.te (Type Enforcement) — Domain rules and type declarations.if (Interface) — Reusable macros for other modules.fc (File Contexts) — Path-to-label mappings123456789101112131415161718192021222324252627282930313233343536373839404142
# mydaemon.te - Type Enforcement rules policy_module(mydaemon, 1.0.0) ######################################### Type declarations######################################## type mydaemon_t; # Domain for the daemon processtype mydaemon_exec_t; # Type for the executabletype mydaemon_log_t; # Type for log filestype mydaemon_var_run_t; # Type for runtime files # Register mydaemon_t as a domain (process type)domain_type(mydaemon_t) # Register mydaemon_exec_t as an entry pointdomain_entry_file(mydaemon_t, mydaemon_exec_t) # Register as a system daemoninit_daemon_domain(mydaemon_t, mydaemon_exec_t) ######################################### Daemon rules######################################## # Allow reading /etc filesfiles_read_etc_files(mydaemon_t) # Allow network operationscorenet_tcp_bind_generic_port(mydaemon_t)corenet_tcp_connect_http_port(mydaemon_t) # Manage own log fileslogging_log_file(mydaemon_log_t)allow mydaemon_t mydaemon_log_t:file { create write append };allow mydaemon_t mydaemon_log_t:dir { search add_name }; # Manage runtime files (PID files, sockets)files_pid_file(mydaemon_var_run_t)allow mydaemon_t mydaemon_var_run_t:file manage_file_perms;allow mydaemon_t mydaemon_var_run_t:dir manage_dir_perms;Interfaces are the Reference Policy's key innovation. They encapsulate common access patterns as reusable macros:
# Instead of writing these raw rules:
allow mydaemon_t self:capability { net_bind_service };
allow mydaemon_t mydaemon_port_t:tcp_socket { bind listen accept };
corenet_tcp_bind_port(mydaemon_t, mydaemon_port_t)
...
# You write:
corenet_tcp_bind_generic_port(mydaemon_t)
Interfaces:
The Reference Policy source is invaluable for learning SELinux. Study existing modules (apache.te, sshd.te) to see patterns. Use the interface documentation: seinfo -i lists available interfaces. On Fedora/RHEL, install selinux-policy-devel for policy sources.
Recompiling policy for every configuration change would be impractical. Policy booleans provide runtime-adjustable policy switches without recompilation.
Booleans are named true/false values that enable or disable sets of rules:
# Policy defines conditional rules:
bool httpd_can_network_connect false;
if (httpd_can_network_connect) {
corenet_tcp_connect_all_ports(httpd_t)
}
# Administrator enables at runtime:
$ sudo setsebool httpd_can_network_connect on
# Now httpd_t can connect to any port
Booleans enable administrators to:
123456789101112131415161718192021222324252627282930313233343536
# List all booleans with current and pending values$ getsebool -aabrt_anon_write --> offabrt_handle_event --> off...httpd_can_network_connect --> offhttpd_can_network_connect_db --> offhttpd_can_sendmail --> off... # Filter for specific service$ getsebool -a | grep httpdhttpd_builtin_scripting --> onhttpd_can_check_spam --> offhttpd_can_connect_ftp --> offhttpd_can_network_connect --> offhttpd_can_network_connect_db --> offhttpd_can_sendmail --> offhttpd_enable_cgi --> onhttpd_enable_homedirs --> offhttpd_read_user_content --> offhttpd_use_nfs --> off # Get detailed information about a boolean$ semanage boolean -l | grep httpd_can_network_connecthttpd_can_network_connect (off , off) Allow httpd to can network connecthttpd_can_network_connect_db (off , off) Allow httpd to can network connect db # Set boolean temporarily (until reboot)$ sudo setsebool httpd_can_network_connect on # Set boolean permanently (persists across reboots)$ sudo setsebool -P httpd_can_network_connect on # Set multiple booleans at once$ sudo setsebool -P httpd_can_network_connect=on httpd_use_nfs=onSome booleans have significant security implications:
| Boolean | Default | Effect When Enabled |
|---|---|---|
| httpd_can_network_connect | off | Apache can connect to any TCP port (needed for proxies) |
| httpd_can_network_connect_db | off | Apache can connect to database ports specifically |
| httpd_enable_cgi | on | Apache can execute CGI scripts |
| httpd_read_user_content | off | Apache can read user home directories |
| ftpd_anon_write | off | FTP anonymous users can write files |
| samba_enable_home_dirs | off | Samba can share user home directories |
| allow_guest_exec_content | off | Guest users can execute content in home |
| selinuxuser_execmod | on | Users can execute text-relocated libraries |
| deny_ptrace | off | Denies ptrace (debugging) for all domains |
Each boolean represents a security trade-off. Enabling 'httpd_can_network_connect' allows Apache to talk to backend services, but also means a compromised Apache can make arbitrary network connections. Enable only what's necessary, and understand the implications.
SELinux policies undergo compilation from human-readable source to an optimized binary format the kernel can efficiently query. Understanding this process helps with policy development and troubleshooting.
[Policy Source Files] (.te, .if, .fc)
↓
checkmodule (parses, validates)
↓
[Policy Module] (.mod)
↓
semodule_package (packages)
↓
[Policy Package] (.pp)
↓
semodule (installs to policy store)
↓
[Policy Store] (/var/lib/selinux/targeted/)
↓
semodule/libsemanage (links all modules)
↓
[Compiled Binary Policy] (policy.XX)
↓
Kernel loads binary policy
1234567891011121314151617181920212223242526272829303132
# Step 1: Write your policy source files# mydaemon.te, mydaemon.if, mydaemon.fc # Step 2: Compile Type Enforcement to module$ checkmodule -M -m -o mydaemon.mod mydaemon.te# -M: MLS/MCS enabled# -m: build a module (not base policy)# -o: output file # Step 3: Package the module with file contexts$ semodule_package -o mydaemon.pp -m mydaemon.mod -f mydaemon.fc # Step 4: Install the module$ sudo semodule -i mydaemon.pp# This installs to policy store and rebuilds binary policy # List installed modules$ semodule -labrt 1.4.1accountsd 1.1.0acct 1.6.0...mydaemon 1.0.0... # Remove a module$ sudo semodule -r mydaemon # Alternative: Use Makefile from selinux-policy-devel# Create a Makefile in your module directory:$ make -f /usr/share/selinux/devel/Makefile# This handles all compilation steps automaticallySELinux maintains a policy store containing all installed modules:
/var/lib/selinux/targeted/
├── active/
│ ├── modules/100/ # Priority 100 (default)
│ │ ├── base/
│ │ ├── httpd/
│ │ ├── sshd/
│ │ └── mydaemon/
│ ├── modules/400/ # Higher priority overrides
│ ├── file_contexts
│ ├── booleans.local
│ └── seusers
└── policy/
└── policy.33 # Compiled binary (kernel version 33)
Modules have priority levels (100-999). Higher priority modules can override rules from lower ones. This enables local customizations without modifying distribution modules.
The compiled binary policy is loaded into the kernel through the SELinux filesystem:
/sys/fs/selinux/
├── load # Write policy binary here to load
├── enforce # Read/write enforcement mode
├── policy # Read current loaded policy
├── status # Sequence number for policy changes
├── class/ # Object class definitions
├── booleans/ # Boolean values
└── ... # Other tunables
Use 'seinfo' and 'sesearch' to analyze the currently loaded policy. 'seinfo -t' lists all types. 'sesearch --allow -s httpd_t' shows all allow rules for httpd_t. These tools work on the live binary policy.
When stock policy doesn't meet your needs, you can create custom modules. This section walks through the process for common scenarios.
The most common customization: your application triggers SELinux denials and you need to allow the access. The workflow:
ausearch -m avc -ts recentaudit2allow as a starting point12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
# Step 1: Find recent denials for your process$ sudo ausearch -m avc -ts recent | grep mydaemontype=AVC msg=audit(1642589234.789:456): avc: denied { read } for pid=12345 comm="mydaemon" name="config.dat" dev="sda1" ino=987654 scontext=system_u:system_r:mydaemon_t:s0 tcontext=system_u:object_r:etc_t:s0 tclass=file permissive=0 # Step 2: Analyze the denial# Subject: mydaemon_t (our daemon)# Object: etc_t (generic /etc type)# Action: read file# This looks legitimate - daemon reading config from /etc # Step 3: Generate policy rules from audit log$ sudo ausearch -m avc -ts recent | audit2allow -m mydaemon_custom module mydaemon_custom 1.0; require { type mydaemon_t; type etc_t; class file read;} #============= mydaemon_t ==============allow mydaemon_t etc_t:file read; # Step 4: Review - is this too broad?# "etc_t" includes ALL of /etc. Do we need that?# Better: Create a specific type for our config, or use an interface # Step 5: Refined approach - use proper interface$ cat mydaemon_etc.tepolicy_module(mydaemon_etc, 1.0.0) require { type mydaemon_t;} # Use the standard interface for reading /etcfiles_read_etc_files(mydaemon_t) # Step 6: Build and install$ make -f /usr/share/selinux/devel/Makefile mydaemon_etc.pp$ sudo semodule -i mydaemon_etc.pp # Step 7: Verify - retest application, check no more denials$ sudo ausearch -m avc -ts recent | grep mydaemon<no output = no denials>Never use 'audit2allow -M mypolicy | semodule -i' blindly! Generated rules may be overly permissive. An application probing the filesystem generates denials for files it shouldn't access—naively allowing all probed access defeats MAC entirely. Always review and minimize generated rules.
For a new application requiring full confinement:
setenforce 0, collect denialssetenforce 1 for production<app>_t for domain, <app>_exec_t for executable, etc.SELinux provides powerful tools for inspecting and querying policy. Mastering these tools is essential for policy development and troubleshooting.
seinfo provides statistics and listings from the loaded policy:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
# Overall policy statistics$ seinfo Statistics for policy file: /sys/fs/selinux/policyPolicy Version: 33 (MLS enabled) Classes: 130 Permissions: 463 Sensitivities: 1 Categories: 1024 Types: 4932 Attributes: 264 Users: 8 Roles: 14 Booleans: 338 Cond. Expr.: 420 Allow: 118394 Neverallow: 0 Auditallow: 17 Dontaudit: 17643 Type_transition: 24857 Role_transition: 418 Range_transition: 623 ... # List all types$ seinfo -t | head -20Types: 4932 ... NetworkManager_t abrt_dump_oops_t abrt_helper_t abrt_t httpd_t httpd_exec_t httpd_content_t ... # Types with specific attribute$ seinfo -a domain -x domain NetworkManager_t abrt_t httpd_t sshd_t ... # List all object classes$ seinfo --class | headClasses: 130 appletalk_socket association binder blk_file capability ...sesearch finds specific rules in the policy:
1234567891011121314151617181920212223242526272829303132
# All allow rules for a source type$ sesearch --allow -s httpd_t | head -10allow httpd_t NetworkManager_var_run_t:dir { getattr search };allow httpd_t bin_t:dir { getattr ioctl read search ... };allow httpd_t bin_t:file { execute getattr ioctl map open read };allow httpd_t cert_t:dir { getattr ioctl read search };allow httpd_t cert_t:file { getattr ioctl open read };... # Rules for specific source, target, and class$ sesearch --allow -s httpd_t -t httpd_content_t -c fileallow httpd_t httpd_content_t:file { getattr ioctl open read };allow httpd_t httpd_content_t:file { map }; # Rules with specific permission$ sesearch --allow -s httpd_t -c tcp_socket -p name_connectallow httpd_t http_port_t:tcp_socket name_connect;# (only if httpd_can_network_connect is enabled) # Type transition rules$ sesearch --type_trans -s init_t | grep httpdtype_transition init_t httpd_exec_t:process httpd_t; # All rules involving an attribute$ sesearch --allow -t file_type -c file -p write# Shows all write access to any file_type # Dontaudit rules (silenced denials)$ sesearch --dontaudit -s httpd_t | headdontaudit httpd_t admin_home_t:dir search;dontaudit httpd_t default_t:dir search;...| Tool | Purpose | Example |
|---|---|---|
semanage | Manage policy components | semanage boolean -l |
audit2why | Explain why denial occurred | audit2why < /var/log/audit/audit.log |
sealert | User-friendly denial analysis | sealert -l <alert-id> |
matchpathcon | Show expected context for path | matchpathcon /var/www/html |
secon | Show context of current process | secon -t |
apol | Graphical policy analysis | GUI application |
When things don't work: (1) Check ausearch -m avc -ts recent for denials, (2) Use audit2why to understand why denied, (3) Use sesearch to see what rules exist, (4) Use sealert for human-readable analysis and suggestions.
We've explored the architecture and mechanics of SELinux policies. Let's consolidate the key insights:
What's Next:
With policy structure understood, we'll explore Contexts and Labels—the mechanism by which types are assigned to subjects and objects. Understanding labeling is crucial for ensuring policy rules match the resources they're meant to protect.
You now understand SELinux policy architecture, from high-level types to individual rules, from the Reference Policy framework to custom module development. This foundation enables you to analyze, customize, and troubleshoot SELinux policies effectively.