SELinux explained for beginners

SELinux is a mandatory access control system built into the Linux kernel that enforces strict rules about which processes can access which files and resources, and Fedora ships with it enabled and set to Enforcing by default.

SELinux explained for beginners

You restarted Apache and the service failed. journalctl -u httpd shows Permission denied. You check the file permissions with ls -l and everything looks correct. The owner is apache, the group is apache, and the mode is 755. The problem is not Unix permissions. SELinux is blocking the access because the file has the wrong security context. This scenario repeats whenever you move files outside their default directories, run a service on a non-standard port, or install a third-party application that expects different labels.

What is actually happening

Traditional Unix permissions rely on user and group ownership. They answer the question of who owns the resource. SELinux adds mandatory access control. It answers the question of what the process is allowed to do. Think of Unix permissions as a keycard that grants you entry to a building. SELinux is the security guard inside who checks whether you are authorized to touch the server rack, even if you have the keycard.

SELinux assigns a security context to every process, file, and network socket. The context is a label in the format user:role:type:level. The policy defines which types can interact. Fedora uses the targeted policy by default. This policy confines high-risk daemons like web servers, databases, and mail servers. Most user processes run unconfined. This approach provides strong protection for critical services while keeping the desktop experience familiar.

Check the current mode to see if SELinux is active.

getenforce
# Returns Enforcing, Permissive, or Disabled. Enforcing is the default and the only safe state for production.

Get detailed status including the policy name and MLS configuration.

sudo sestatus
# Shows the current mode, policy name, and MLS status. Look for "SELinux status: enabled" and "Current mode: enforcing".

Use Enforcing mode when the system is running normally and you want full protection against unauthorized access. Use Permissive mode when you are debugging a new service or a configuration change and need to see what SELinux would block without actually stopping the process. Use Disabled mode only when you have a specific hardware or kernel module requirement that fundamentally conflicts with SELinux, which is rare on modern Fedora. Never leave a system in Permissive mode longer than the debugging session requires.

Switch modes temporarily for testing.

sudo setenforce 0
# Sets mode to Permissive immediately. Changes revert on reboot. Useful for quick tests.
sudo setenforce 1
# Sets mode back to Enforcing immediately.

Edit the configuration file to make the mode persistent across reboots.

# /etc/selinux/config
SELINUX=enforcing
# Controls the boot-time mode. Change this only if you need a permanent mode switch.

Reboot after changing /etc/selinux/config. The kernel parameter must match the file.

Reading AVC denial messages

When SELinux blocks an action, it logs an AVC denial to the audit subsystem. The raw denial contains technical details about the source process and the target object. The setroubleshoot package translates these details into human-readable advice. Install the package if it is missing. Fedora includes setroubleshoot by default on Workstation, but Server images may require manual installation.

sudo dnf install setroubleshoot-server policycoreutils-python-utils
# Provides sealert, semanage, and other tools for managing SELinux policy. Essential for custom configurations.

Check the journal for denial summaries. The setroubleshoot service writes a concise explanation for every denial.

journalctl -t setroubleshoot
# Filters the journal for SELinux denial summaries. The output includes a one-line explanation and a link to the full alert.

If you see a denial, read the summary line. It often tells you exactly what went wrong and suggests a fix. For example, a summary might say httpd_t is not allowed to read user_home_t files. This means Apache tried to access a file in a user's home directory, and the policy forbids that interaction.

The raw audit log contains the full context. You will see this format when searching for errors online.

type=AVC msg=audit(1698765432.123:456): avc:  denied  { read } for  pid=1234 comm="httpd" name="index.html" 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

Break down the denial line. The scontext field shows the source context. The tcontext field shows the target context. The tclass field shows the object type. In the example, scontext=system_u:system_r:httpd_t:s0 identifies the Apache process. tcontext=unconfined_u:object_r:user_home_t:s0 identifies the file. The policy does not allow httpd_t to read user_home_t. The fix is to move the file to a directory with the correct context, such as /var/www/html, or to change the context of the file to match the web content type.

Parse the audit log for actionable suggestions.

sudo sealert -a /var/log/audit/audit.log
# Parses the audit log and prints actionable suggestions. Requires setroubleshoot-server package.

Read the setroubleshoot output before changing booleans or contexts. The tool often points to the exact command you need to run.

Common fixes

Use restorecon to fix file contexts. This command compares the current context of files against the policy rules and resets them to the default. It is safe to run on directories managed by packages.

sudo restorecon -Rv /var/www/html/mysite
# Recursively restores the default SELinux context for files based on the policy. The -v flag prints changes.

Use semanage fcontext when you need a custom context that persists across reboots and package updates. This command adds a rule to the local policy. After adding the rule, run restorecon to apply it.

sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
# Adds a rule that labels /srv/www and all contents as httpd_sys_content_t.
sudo restorecon -Rv /srv/www
# Applies the new rule to existing files.

Use setsebool to toggle specific permissions for confined services. Booleans are switches provided by the policy. They allow you to enable or disable features without writing custom rules.

getsebool -a | grep httpd
# Lists all booleans related to httpd. Check the current state before changing.
sudo setsebool -P httpd_can_network_connect on
# Enables the boolean persistently. The -P flag writes the change to disk so it survives reboots.

Use semanage port when a service binds to a non-standard port. SELinux restricts which ports confined services can use. If you change the port in the service configuration, you must also update the SELinux port definition.

sudo semanage port -a -t ssh_port_t -p tcp 2222
# Adds port 2222 to the allowed list for ssh_port_t. Use -m instead of -a to modify an existing entry.

Run restorecon after moving files. Manual chcon commands drift and get overwritten by package updates.

Verify the fix

Restart the service and check the journal for new denials. If the service starts and no new AVC messages appear, the configuration is correct. Run getenforce to confirm the system is back in Enforcing mode if you switched to Permissive for testing.

sudo systemctl restart httpd
# Restarts the service to apply changes.
journalctl -xeu httpd
# Shows the service log with explanatory text. Look for "Started The Apache HTTP Server" and no AVC denials.

Check the service status to confirm it is running.

systemctl status httpd
# Shows the current state and recent log lines. Active: active (running) indicates success.

Common pitfalls

Avoid using chcon for permanent changes. chcon modifies the file context directly. If you run chcon on a file managed by a package, the next dnf upgrade may reset the context or trigger an integrity error. Use restorecon to apply defaults. Use semanage fcontext to define custom rules. Reserve chcon for temporary testing or files that are not managed by any package.

Do not disable SELinux to fix a denial. Disabling SELinux removes a significant layer of system security. If an application misbehaves, switch to Permissive mode, diagnose the denial, and apply the minimal fix. The denial is usually a sign that a configuration step is missing, not that SELinux is broken.

Install policycoreutils-python-utils and setroubleshoot-server immediately after a fresh install. You will need these tools before you encounter your first denial.

When to use which tool

Use restorecon when files have been moved or copied and need their default security context restored. Use semanage fcontext when you need a persistent custom context rule that restorecon will apply automatically. Use setsebool when you need to toggle a specific permission for a confined service without writing custom policy. Use semanage port when a service runs on a non-standard port and SELinux blocks the binding. Use chcon only for temporary testing or files that are not managed by any package.

Where to go next