Understanding SELinux and AppArmor Security Modules
Short introduction
Mandatory Access Control (MAC) systems like SELinux and AppArmor add a powerful layer of protection beyond traditional Unix user/group permissions. This article gives practical, beginner-friendly explanations of what they do, how they differ, and common commands and workflows for managing and troubleshooting each system.
What is MAC, and how do SELinux and AppArmor differ?
Both SELinux (Security-Enhanced Linux) and AppArmor implement Mandatory Access Control: security policies that limit what processes can do regardless of the user identity. The two projects take different approaches:
- SELinux uses label-based controls: every file, process, and resource gets a security context (label), and policies define allowed interactions between contexts.
- AppArmor uses path-based profiles: policies are attached to program binaries (by path) describing allowed file access, capabilities, and networking.
This makes SELinux more fine-grained and flexible for complex environments, while AppArmor is often easier to write and adopt for single-application restrictions.
Example: view basic status for each
# SELinux status (Red Hat/CentOS/Fedora):
$ getenforce
Enforcing
$ sestatus
SELinux status: enabled
SELinuxfs mount: /selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
...
# AppArmor status (Ubuntu/Debian):
$ sudo aa-status
apparmor module is loaded.
16 profiles are loaded.
12 profiles are in enforce mode.
3 profiles are in complain mode.
...
Note: commands vary slightly by distribution; use the package manager to confirm installation if these fail.
SELinux basics: labels, modes, and common commands
SELinux enforces policies by comparing labels (user:role:type:level) on processes and objects. Common troubleshooting starts with checking modes and reading audit logs.
Key SELinux modes:
- Enforcing: policy violations are blocked.
- Permissive: violations are logged but allowed.
- Disabled: SELinux is off.
Check and change mode:
# Check current mode
$ getenforce
Permissive
# Temporarily set enforcing (until reboot)
$ sudo setenforce 1
# Make permanent (edit /etc/selinux/config)
$ sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
List loaded modules and policies:
# Show loaded SELinux modules
$ sudo semodule -l
100 local
400 allow_ssh
...
# Show contexts of a file
$ ls -Z /var/www/html/index.html
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
If a service is denied access, check the audit log and generate a local allow rule:
# Search audit logs for AVC denials
$ sudo ausearch -m AVC,USER_AVC -ts recent-time
# Convert denial to allow rule (careful; review before installing)
$ sudo ausearch -m AVC -ts today | audit2allow -M myapp
$ sudo semodule -i myapp.pp
Important: audit2allow helps test fixes but you should review generated rules to avoid weakening policy.
AppArmor basics: profiles, modes, and typical workflows
AppArmor profiles are usually found under /etc/apparmor.d/ and can be in enforce or complain mode. Complain mode logs violations but doesn’t block them — good for developing profiles.
Check and manage AppArmor:
# Show status and loaded profiles
$ sudo aa-status
# Put a profile into complain (learning) mode
$ sudo aa-complain /usr/sbin/nginx
# Put it back into enforce mode
$ sudo aa-enforce /usr/sbin/nginx
Create or adjust profiles:
- Use aa-genprof to generate a profile by exercising application behavior.
- Use aa-logprof to interactively convert logged denies into profile rules.
Example workflow:
# Generate profile template while running a service
$ sudo aa-genprof /usr/sbin/myapp
# After running the app and creating log entries, update profile
$ sudo aa-logprof
Parsing AppArmor message log...
Suggesting changes for profile: /usr/sbin/myapp
Accept change? (y/n) y
For quick testing, you can load an unconfined profile or remove a profile (not recommended for production):
# Disable a profile (unloads it)
$ sudo aa-disable /etc/apparmor.d/usr.sbin.myapp
Managing policies and troubleshooting techniques
Both systems rely heavily on logs when debugging denials. Learn to read audit/audit.log (SELinux) and kern.log/syslog (AppArmor). A methodical approach helps:
- Reproduce the denial (run the failing action).
- Find the corresponding log lines.
- Interpret the message (which process, which file, which rule).
- Decide: change file labels (SELinux), alter profile (AppArmor), or create allowed rules.
SELinux example: a web server cannot write uploads:
# Reproduce failure, then search deny
$ sudo ausearch -m AVC -ts recent | tail -n 20
----
type=AVC msg=audit(1600000000.000:123): avc: denied { write } for pid=1234 comm="httpd" name="uploads" dev="sda1" ino=5678 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file
# Fix by restoring correct context or setting a permissive rule
$ sudo chcon -t httpd_sys_rw_content_t /var/www/html/uploads -R
# Or make persistent:
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?"
$ sudo restorecon -Rv /var/www/html/uploads
AppArmor example: a program denied network access:
# Check logs for AppArmor messages (journalctl or syslog)
$ sudo journalctl -k | grep apparmor | tail -n 20
# Edit profile to allow network access and reload
$ sudo vim /etc/apparmor.d/usr.bin.myapp
# add: network inet stream,
$ sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myapp
When troubleshooting, use the following checklist:
- Is the module enabled? (getenforce / aa-status)
- Is the service running under the expected context/profile?
- Are file contexts correct (SELinux)?
- Does the profile allow needed capabilities/networks (AppArmor)?
- Did you reload or install the changed policy?
Commands table
| Command | System | Purpose / Notes |
|---|---|---|
| getenforce | SELinux | Show SELinux mode (Enforcing/Permissive/Disabled) |
| sestatus | SELinux | Detailed SELinux status and policy info |
| semodule -l | SELinux | List installed SELinux modules |
| semanage fcontext | SELinux | Manage file context rules (add, delete) |
| restorecon | SELinux | Apply file contexts from semanage to files |
| ausearch / audit2allow | SELinux | Search AVC logs and generate allow rules |
| aa-status | AppArmor | Show AppArmor status and loaded profiles |
| aa-complain / aa-enforce | AppArmor | Switch profile between complain and enforce modes |
| aa-genprof / aa-logprof | AppArmor | Generate and refine profiles interactively |
| apparmor_parser | AppArmor | Load or reload profiles from /etc/apparmor.d |
| journalctl / /var/log/audit/audit.log | Both | Inspect kernel/AppArmor/SELinux logs |
Common Pitfalls
- Thinking Permissive = safe to ignore: Running SELinux in permissive mode hides denials and can mask issues you should fix before switching to enforcing.
- Changing permissions instead of contexts: For SELinux, adjusting Unix permissions (chmod/chown) often won’t fix denials; you need to set the correct SELinux context (chcon/semanage + restorecon).
- Over-permissive fixes: Using audit2allow blindly or adding broad allow rules in AppArmor can accidentally weaken security; always review generated rules and prefer the minimum privilege.
Next Steps
- Practice in a safe environment: enable permissive mode, reproduce denials, and convert them to controlled policy changes.
- Read real logs: regularly inspect /var/log/audit/audit.log (SELinux) or journalctl/kern.log (AppArmor) to learn how denials are reported.
- Start small: create or modify a single AppArmor profile or one SELinux fcontext rule for a service, then tighten controls incrementally.
If you want, I can provide a step-by-step lab exercise for either SELinux or AppArmor tailored to your Linux distribution.
👉 Explore more IT books and guides at dargslan.com.