Linux Security Modules
DAC vs MAC
Section titled “DAC vs MAC”The standard Linux permission model is Discretionary Access Control (DAC): the file owner controls access by setting rwx permissions plus group membership. It’s called “discretionary” because the owner has discretion over who can access their files.
DAC has a fundamental weakness: a compromised process running as root has zero restrictions. If nginx is exploited and runs as root, it can read /etc/shadow, modify system files, or install backdoors.
Mandatory Access Control (MAC) adds an additional policy layer that the kernel enforces regardless of DAC settings - even root is bound by MAC policy. Applications can only do what the policy explicitly allows.
Linux Security Modules (LSM)
Section titled “Linux Security Modules (LSM)”LSM is a kernel framework (merged in 2003) that allows different MAC implementations to hook into the kernel’s access control decisions. The framework is lightweight and doesn’t favor any particular security model.
An LSM intercepts security-sensitive operations and calls registered security hooks before allowing them to proceed. If any hook returns denial, the operation is blocked.
Available LSMs
Section titled “Available LSMs”| LSM | Default on | Focus |
|---|---|---|
| SELinux | RHEL, Fedora, CentOS | Fine-grained label/policy; NSA-developed |
| AppArmor | Ubuntu, SUSE | Path-based profiles; simpler to configure |
| Smack | Embedded systems (Tizen) | Simplified mandatory access |
| Tomoyo | Japan-originated distros | Pathname-based; good learning mode |
| Yama | Many distros (minor LSM) | Restrict ptrace scope; can stack with others |
SELinux
Section titled “SELinux”SELinux was developed by the NSA and contributed to the Linux kernel in 2000. It has been the default MAC system on RHEL-family distros ever since.
Core Concepts
Section titled “Core Concepts”SELinux works with three building blocks:
| Concept | Description | Example |
|---|---|---|
| Context (label) | Security label on files, processes, and ports | system_u:system_r:httpd_t:s0 |
| Rule | A statement defining allowed access between two contexts | allow httpd_t var_log_t : file write |
| Policy | A compiled set of rules loaded into the kernel | targeted, minimum, MLS |
The default policy is deny: if no rule explicitly allows an operation, SELinux blocks it.
Context format: user:role:type:level
- user - SELinux user (not the same as Linux username)
- role - Role (RBAC component)
- type - The most important field; type enforcement rules use this. Convention: ends in
_t - level - MLS/MCS security level (only used in MLS/MCS policies)
Enforcement Modes
Section titled “Enforcement Modes”| Mode | Behavior |
|---|---|
| Enforcing | Policy violations are blocked AND logged. This is the normal production state. |
| Permissive | Policy violations are only logged (not blocked). Use for debugging or gradual policy rollout. |
| Disabled | SELinux is completely off. Requires reboot to re-enable; triggers full filesystem relabel. |
sestatus # show current mode, policy, and MLS statusgetenforce # print just the mode (Enforcing/Permissive/Disabled)sudo setenforce 1 # switch to Enforcing (live; no reboot needed)sudo setenforce 0 # switch to Permissive (live; no reboot needed)To permanently change the mode, edit /etc/selinux/config:
sudo vim /etc/selinux/config# SELINUX=enforcing # or permissive, or disabled# SELINUXTYPE=targeted # policy typeSELinux Policies
Section titled “SELinux Policies”| Policy | Description |
|---|---|
| targeted (default) | Only network-facing services are confined. User processes and init are unconfined. Best balance of security vs. usability. |
| minimum | Targeted policy with most processes unconfined; a subset of targeted. |
| MLS | Multi-Level Security: all processes confined; Bell-LaPadula model; used in classified environments. Very restrictive. |
Viewing and Managing Contexts
Section titled “Viewing and Managing Contexts”# View contexts with -Z flagls -Z /var/www/html/# -rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
ps axZ# LABEL PID TTY STAT COMMAND# system_u:system_r:httpd_t:s0 7490 ? Ss /usr/sbin/httpd
id -Z # SELinux context of current sessionChanging Contexts with chcon
Section titled “Changing Contexts with chcon”chcon changes the context of a file (temporary - reverted by restorecon or autorelabel):
chcon -t httpd_sys_content_t /mydir/index.htmlchcon --reference /var/www/html/index.html /mydir/index.html # copy context from referencePersistent Context with semanage + restorecon
Section titled “Persistent Context with semanage + restorecon”For permanent context changes that survive a relabel:
# 1. Define the rule (maps path to context type)sudo semanage fcontext -a -t httpd_sys_content_t '/virtualHosts(/.*)?'
# 2. Apply the rule to existing filessudo restorecon -RFv /virtualHosts# restorecon reset /virtualHosts context default_t->httpd_sys_content_t
# View all custom fcontext rulessudo semanage fcontext -l | grep virtualHostsThe two-step pattern is essential:
semanage fcontext= writes the rule to the policy databaserestorecon= applies the rule to the filesystem right now
SELinux Booleans
Section titled “SELinux Booleans”Booleans allow runtime adjustment of policy behavior without writing new rules:
getsebool -a # list all booleans and current valuesgetsebool httpd_can_network_connect # check specific booleansudo setsebool httpd_can_network_connect on # enable (not persistent across reboot)sudo setsebool -P httpd_can_network_connect on # enable AND persist (-P flag)semanage boolean -l | grep httpd # view persistent settingsCommon booleans:
| Boolean | Effect |
|---|---|
httpd_can_network_connect | Allow Apache to initiate network connections |
httpd_can_network_connect_db | Allow Apache to connect to databases |
httpd_enable_homedirs | Allow Apache to serve user home directories |
allow_ftpd_anon_write | Allow vsftpd anonymous uploads |
samba_enable_home_dirs | Allow Samba to share home directories |
Diagnosing SELinux Denials
Section titled “Diagnosing SELinux Denials”Install setroubleshoot-server for human-readable denial messages:
sudo dnf install setroubleshoot-server # RHEL/Fedora
# After installation, restart auditdsudo systemctl restart auditdAVC (Access Vector Cache) denials are logged to /var/log/audit/audit.log. setroubleshoot also pushes summaries to /var/log/messages:
# Check for recent denialssudo ausearch -m AVC -ts recentsudo grep "SELinux is preventing" /var/log/messages
# Get detailed explanation and suggested fixsudo sealert -l <uuid-from-ausearch>
# Generate a custom policy module from denialsgrep httpd /var/log/audit/audit.log | audit2allow -M mypolsudo semodule -i mypol.ppTypical debugging workflow:
- Try the operation that fails
- Check
/var/log/audit/audit.logfor AVC entries (ausearch -m AVC -ts recent) - Read
sealertoutput for the root cause and suggested fix - Fix with: boolean change,
semanage fcontext+restorecon, oraudit2allow(last resort)
AppArmor
Section titled “AppArmor”AppArmor is a path-based MAC system, simpler to configure than SELinux. It’s the default on Ubuntu and SUSE.
Key Differences from SELinux
Section titled “Key Differences from SELinux”| Feature | SELinux | AppArmor |
|---|---|---|
| Access control basis | Labels (contexts) | File paths |
| Policy complexity | High | Lower |
| Learning mode | No | Yes (complain mode) |
| File relabeling | Required | Not required |
| Default distros | RHEL, Fedora | Ubuntu, SUSE |
Checking Status
Section titled “Checking Status”sudo apparmor_status # full status: loaded profiles, modessudo systemctl status apparmorsudo aa-status # abbreviated status| Mode | Behavior | Set with |
|---|---|---|
| Enforce | Violations are blocked and logged. Default for active profiles. | sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx |
| Complain | Violations are only logged (not blocked). “Learning mode” - use to build new profiles. | sudo aa-complain /etc/apparmor.d/usr.sbin.nginx |
Profiles
Section titled “Profiles”Profiles are stored in /etc/apparmor.d/. Each profile restricts what a specific executable can do (file access, capabilities, network, etc.):
ls /etc/apparmor.d/# bin.ping usr.sbin.cups-browsed usr.sbin.avahi-daemon ...
# View a profilecat /etc/apparmor.d/usr.sbin.cups-browsed
# Reload a modified profilesudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
# Disable a profilesudo aa-disable /etc/apparmor.d/usr.sbin.nginxAppArmor Utilities
Section titled “AppArmor Utilities”| Command | Purpose |
|---|---|
apparmor_status / aa-status | Show all profiles and their modes |
aa-enforce <profile> | Switch profile to enforce mode |
aa-complain <profile> | Switch profile to complain (learning) mode |
aa-disable <profile> | Unload profile permanently |
aa-logprof | Review log, suggest profile additions, apply them interactively |
aa-genprof <binary> | Interactive profile generator for a new program |
apparmor_parser -r <profile> | Reload a modified profile |
Building a New Profile
Section titled “Building a New Profile”The recommended workflow for profiling a new application:
# 1. Start profile generation (runs app in complain mode)sudo aa-genprof /usr/sbin/myapp
# 2. In another terminal: exercise the application (all normal use cases)myapp --do-typical-things
# 3. Back in aa-genprof: press 'S' to scan logs and approve/deny suggested rules# 4. Press 'F' to finish - profile is saved to /etc/apparmor.d/
# 5. Switch to enforce modesudo aa-enforce /etc/apparmor.d/usr.sbin.myappChoosing Between SELinux and AppArmor
Section titled “Choosing Between SELinux and AppArmor”| Consideration | Choose SELinux | Choose AppArmor |
|---|---|---|
| Your distro | RHEL/Fedora/CentOS | Ubuntu/SUSE/Debian |
| Security strictness | Maximum (label-based, everything labeled) | High but more practical |
| Admin learning curve | Steeper | More approachable |
| Policy fine-tuning | Very granular | Profile-based but simpler |
| Filesystem independence | Yes (labels follow files) | No (path-based; symlinks matter) |
In practice, use whatever your distro ships with. Both provide effective MAC when properly configured. Switching from one to the other is complex.