thelinuxvault guide

Tutorial: Implementing SELinux for Enhanced Linux Security

In the landscape of Linux security, discretionary access control (DAC) systems—such as file permissions (user/group/other)—have long been the first line of defense. However, DAC has limitations: it relies on user and process ownership, leaving systems vulnerable to privilege escalation if a trusted user or process is compromised. This is where **SELinux (Security-Enhanced Linux)** steps in. SELinux is a mandatory access control (MAC) system developed by the National Security Agency (NSA) to provide granular control over system access. Unlike DAC, which focuses on "who" (user) can access a resource, SELinux enforces policies based on "what" (process) is allowed to access "which" (resource), regardless of user privileges. This makes it a powerful tool to mitigate attacks, even if an attacker gains root access. In this tutorial, we’ll demystify SELinux, guide you through its implementation, and show you how to leverage it to harden your Linux system. Whether you’re a system administrator, developer, or security enthusiast, this guide will equip you with the skills to configure and manage SELinux effectively.

Table of Contents

  1. Understanding SELinux Basics

    • 1.1 Mandatory Access Control (MAC) vs. Discretionary Access Control (DAC)
    • 1.2 Key SELinux Components: Policies, Domains, and Contexts
    • 1.3 Why SELinux Matters for Security
  2. Prerequisites and Installation

    • 2.1 Supported Linux Distributions
    • 2.2 Checking SELinux Installation
  3. Checking SELinux Status

    • 3.1 Using sestatus
    • 3.2 Using getenforce
  4. SELinux Modes: Enforcing, Permissive, and Disabled

    • 4.1 Temporary Mode Changes with setenforce
    • 4.2 Permanent Mode Changes
  5. SELinux Policy Types

    • 5.1 Targeted Policy (Default)
    • 5.2 Strict Policy
    • 5.3 MLS/MCS Policy
  6. Configuring SELinux: Core Concepts

    • 6.1 SELinux Contexts Explained
    • 6.2 Managing File Contexts
    • 6.3 Managing Process Contexts
    • 6.4 SELinux Booleans: Toggling Policy Rules
  7. Handling SELinux Denials

    • 7.1 Understanding AVC Denials
    • 7.2 Analyzing Logs with audit.log, ausearch, and sealert
    • 7.3 Resolving Common Denials
  8. Advanced SELinux Configuration: Custom Policies

    • 8.1 Generating Policy Modules with audit2allow
    • 8.2 Compiling and Loading Custom Modules
  9. SELinux Best Practices

  10. Conclusion

  11. References

1. Understanding SELinux Basics

1.1 Mandatory Access Control (MAC) vs. Discretionary Access Control (DAC)

Linux systems traditionally use DAC, where access to files/processes is controlled by user ownership and permissions (e.g., chmod, chown). If a user with root privileges is compromised, an attacker can access any resource.

SELinux introduces MAC, where access is enforced by a central policy defined by the system administrator, not just user permissions. Even root processes are restricted by SELinux policies, limiting the impact of breaches.

1.2 Key SELinux Components: Policies, Domains, and Contexts

SELinux operates on three core concepts:

  • Policy: A set of rules defining allowed interactions between processes and resources. Policies are preconfigured (e.g., “targeted”) or custom.
  • Domain: A security context assigned to a process (e.g., httpd_t for the Apache web server). Domains restrict what a process can do.
  • Context: A label applied to all system objects (files, processes, sockets) in the format user:role:type:level (e.g., unconfined_u:object_r:httpd_sys_content_t:s0 for a web file).

1.3 Why SELinux Matters for Security

  • Mitigates Privilege Escalation: Even if an attacker compromises a root process, SELinux policies limit its actions.
  • Granular Control: Restrict processes to only the resources they need (principle of least privilege).
  • Auditability: Logs all access attempts, aiding in incident response.

2. Prerequisites and Installation

2.1 Supported Linux Distributions

SELinux is pre-installed on most Red Hat-based distributions:

  • RHEL/CentOS 7/8/9
  • Fedora
  • Rocky Linux/AlmaLinux

Debian/Ubuntu systems use AppArmor by default, but SELinux can be installed (see Debian’s SELinux guide for details). This tutorial focuses on RHEL-based systems.

2.2 Checking SELinux Installation

Verify SELinux is installed with:

rpm -qa | grep selinux

You should see packages like selinux-policy, libselinux, and selinux-policy-targeted.

If missing, install via:

sudo dnf install selinux-policy selinux-policy-targeted libselinux-utils setools-console

3. Checking SELinux Status

Before configuring SELinux, check its current state.

3.1 Using sestatus

The sestatus command provides a detailed overview:

sestatus

Sample output:

SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33

Key fields:

  • SELinux status: enabled or disabled.
  • Loaded policy name: e.g., targeted.
  • Current mode: enforcing, permissive, or disabled.

3.2 Using getenforce

For a quick mode check:

getenforce
# Output: Enforcing

4. SELinux Modes: Enforcing, Permissive, and Disabled

SELinux operates in three modes:

4.1 Temporary Mode Changes with setenforce

Use setenforce to switch modes temporarily (until reboot):

  • Enforcing: Blocks policy violations and logs them.
    sudo setenforce 1  # or "Enforcing"
  • Permissive: Logs violations but does not block them (for troubleshooting).
    sudo setenforce 0  # or "Permissive"

4.2 Permanent Mode Changes

To persist mode changes across reboots, edit /etc/selinux/config:

sudo nano /etc/selinux/config

Set SELINUX= to:

  • enforcing: Enforce policies (recommended for production).
  • permissive: Log violations but do not block (use for testing/troubleshooting).
  • disabled: Disable SELinux entirely (not recommended).

Note: Changing to disabled requires a reboot. Use enforcing/permissive for runtime adjustments.

5. SELinux Policy Types

SELinux policies define which processes/resources are controlled. The most common types are:

5.1 Targeted Policy (Default)

The targeted policy is the default on RHEL-based systems. It focuses on “targeting” critical processes (e.g., httpd, sshd, mysql) with strict rules, while leaving most user processes unconfined. This balances security and usability.

5.2 Strict Policy

The strict policy enforces rules for all processes, including user applications. It is rarely used today due to complexity.

5.3 MLS/MCS Policy

  • MLS (Multi-Level Security): Enforces confidentiality levels (e.g., “Top Secret”, “Secret”) for high-security environments (e.g., government).
  • MCS (Multi-Category Security): Extends MLS with categories (e.g., “Finance”, “HR”) for granular access control.

To switch policies, edit /etc/selinux/config and set SELINUXTYPE=mls, then reboot.

6. Configuring SELinux: Core Concepts

6.1 SELinux Contexts Explained

Every object (file, process, socket) has an SELinux context, formatted as:

user:role:type:level
  • user: The SELinux user (e.g., unconfined_u for regular users).
  • role: The role (e.g., object_r for files, system_r for processes).
  • type: The most critical field; defines allowed interactions (e.g., httpd_t for Apache processes, httpd_sys_content_t for web files).
  • level: MLS/MCS sensitivity (e.g., s0 for unlabeled, s0:c123 for MCS categories).

6.2 Managing File Contexts

Files inherit contexts from their parent directory. If you move/copy a file, its context may become invalid (e.g., moving a file to /var/www/html but retaining its original context).

View File Contexts

Use ls -Z to view contexts:

ls -Z /var/www/html
# Output: -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html

Set File Contexts Temporarily with chcon

Use chcon to manually set a context (temporary—resets on restorecon):

# Set context for a custom web file
sudo chcon -t httpd_sys_content_t /var/www/html/custom-page.html

Restore Default Contexts with restorecon

To revert to the default context (defined by the policy), use restorecon:

# Restore context for /var/www/html and subdirectories
sudo restorecon -Rv /var/www/html
  • -R: Recursive.
  • -v: Verbose (show changes).

Persist Context Changes with semanage fcontext

To permanently set a context (survives restorecon and reboots), use semanage fcontext (part of the policycoreutils-python-utils package):

# Install semanage if missing
sudo dnf install policycoreutils-python-utils

# Add a permanent context rule for /srv/web (custom web root)
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"

# Apply the rule
sudo restorecon -Rv /srv/web

6.3 Managing Process Contexts

Processes run in domains (e.g., httpd_t for Apache). Use ps -Z to view process contexts:

ps -Z | grep httpd
# Output: system_u:system_r:httpd_t:s0  1234 ?  00:00:00 httpd

Domains are defined by the policy; you rarely need to modify them directly.

6.4 SELinux Booleans: Toggling Policy Rules

Booleans are switches that enable/disable specific policy rules (e.g., “allow Apache to connect to the network”).

List All Booleans

getsebool -a  # List all booleans
getsebool -a | grep httpd  # Filter for Apache-related booleans

Toggle Booleans Temporarily

Use setsebool to toggle a boolean (resets on reboot):

# Allow Apache to connect to databases (temporary)
sudo setsebool httpd_can_network_connect_db on

Toggle Booleans Permanently

Add -P to persist changes:

# Allow Apache to connect to databases (permanent)
sudo setsebool -P httpd_can_network_connect_db on

7. Handling SELinux Denials

When SELinux blocks an action, it logs an AVC (Access Vector Cache) denial to /var/log/audit/audit.log.

7.1 Understanding AVC Denials

An AVC denial log entry looks like this:

type=AVC msg=audit(1620000000.123:456): avc:  denied  { read } for  pid=1234 comm="httpd" name="secret.txt" dev="sda1" ino=789 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

Key fields:

  • comm="httpd": Process name.
  • scontext: Source context (process: httpd_t).
  • tcontext: Target context (file: user_home_t—a user home directory file).
  • { read }: Action denied.

7.2 Analyzing Logs with audit.log, ausearch, and sealert

1. Check Raw Logs

View /var/log/audit/audit.log directly:

sudo tail -f /var/log/audit/audit.log | grep AVC

2. Filter with ausearch

Use ausearch to filter denials by time, process, or action:

# Search for AVC denials in the last hour
sudo ausearch -m AVC -ts recent

# Search for denials involving httpd
sudo ausearch -m AVC -c httpd

3. Analyze with sealert

The sealert tool (part of setroubleshoot-server) interprets denials and suggests fixes:

# Install sealert if missing
sudo dnf install setroubleshoot-server

# Analyze audit logs and generate a report
sudo sealert -a /var/log/audit/audit.log

Sample sealert output:

SELinux is preventing httpd from reading a file labeled user_home_t.

*****  Plugin catchall (100. confidence) suggests **************************

If you want to allow httpd to read user home directories
Then you must tell SELinux about this by enabling the 'httpd_read_user_content' boolean.

Do
setsebool -P httpd_read_user_content 1

7.3 Resolving Common Denials

Most denials are fixed by:

  • Restoring file contexts with restorecon.
  • Toggling a boolean with setsebool -P.
  • Adding a custom policy (see Section 8).

8. Advanced SELinux Configuration: Custom Policies

For unique use cases (e.g., a custom application), you may need to create a custom policy module.

8.1 Generating Policy Modules with audit2allow

The audit2allow tool converts AVC denials into policy rules.

Step 1: Capture Denials
Run your application and let SELinux log denials to audit.log.

Step 2: Generate a Policy Module

# Extract denials for your application (e.g., myapp) and generate a .te file
sudo ausearch -m AVC -c myapp | audit2allow -M myapp_policy
  • -M myapp_policy: Creates myapp_policy.te (policy source) and myapp_policy.mod (compiled module).

Step 3: Review the Policy
Edit myapp_policy.te to ensure it only allows necessary actions:

module myapp_policy 1.0;

require {
        type myapp_t;
        type httpd_t;
        class file read;
}

allow myapp_t httpd_t:file read;

8.2 Compile and Load the Module

# Compile the module
checkmodule -M -m -o myapp_policy.mod myapp_policy.te
semodule_package -o myapp_policy.pp -m myapp_policy.mod

# Load the module
sudo semodule -i myapp_policy.pp

# Verify the module is loaded
semodule -l | grep myapp_policy

Note: Custom modules require maintenance—rebuild them if your application or SELinux policy updates.

9. SELinux Best Practices

  • Keep SELinux in Enforcing Mode: Only use permissive for troubleshooting.
  • Update Policies Regularly: Use sudo dnf update selinux-policy* to patch vulnerabilities.
  • Avoid Disabling SELinux: Disabling removes a critical security layer.
  • Use restorecon for File Moves: Always run restorecon after moving files to system directories.
  • Monitor Audit Logs: Use tools like sealert or SIEM solutions to track denials.
  • Backup Policies: Before making changes, back up /etc/selinux/.

Conclusion

SELinux is a powerful MAC system that significantly enhances Linux security by enforcing granular access controls. While it may seem complex initially, mastering its basics—contexts, booleans, and denial handling—empowers you to harden your system against attacks. By following this tutorial, you’ve learned to configure SELinux, troubleshoot denials, and even create custom policies.

Start with permissive mode to test, then switch to enforcing for production. Your systems will thank you.

References