Linux Foundation Certified System Administrator (LFCS)
Operations Deployment
Create and Enforce MAC Using SELinux
In this lesson, we'll explore how to enforce Mandatory Access Control (MAC) using the SELinux security module. SELinux is enabled by default on Red Hat and CentOS systems, while Ubuntu uses AppArmor as its default security module. Before installing or configuring SELinux on a system set up by others, always verify which security module is active. In this guide, we assume that your Ubuntu system does not have SELinux enabled. We'll walk you through disabling AppArmor, installing SELinux tools, and configuring SELinux from scratch.
Disabling AppArmor
Since AppArmor is enabled by default on Ubuntu systems, you must stop its service to avoid conflicts with SELinux:
sudo systemctl stop apparmor.service
After stopping the service, ensure that AppArmor does not automatically start on boot.
Installing SELinux and AuditD
To get started with SELinux, install the SELinux utilities along with the AuditD package. The SELinux package includes essential tools and default security policies, and AuditD logs system events—a critical resource when building custom SELinux policies.
Below is a sample installation output. Your output may vary:
Selecting previously unselected package libgfortran5:amd64.
Preparing to unpack .../04-libgfortran5_14-20240412-0ubuntu1_amd64.deb ...
Unpacking libgfortran5:amd64 (14-20240412-0ubuntu1) ...
...
Progress: [ 28%] [##############################.....................]
Similarly, when AuditD is configured, you might observe:
Setting up python3-semanage (3.5-1build5) ...
...
Progress: [ 78%] [#########################.........................]
Verifying and Activating SELinux
To verify whether SELinux is enabled on your system, run:
sestatus
If SELinux is disabled, the output will resemble:
jeremy@kodekLOUD:~$ sestatus
SELinux status: disabled
jeremy@kodekLOUD:~$
You can also verify the current SELinux context assignments in the root directory:
jeremy@kodekLOUD:~$ ls -Z /
? bin ? lib64 ? mnt ? run
? boot ? home ? lost+found ? proc
...
To enable SELinux, execute:
sudo selinux-activate
This command modifies the GRUB bootloader to include the parameter necessary to load the SELinux module at boot. The output will inform you about generating the GRUB configuration and activate SELinux—after which a system reboot is recommended.
Configuring the Bootloader
After activating SELinux, verify the bootloader settings. In the /etc/default/grub
file, you should see the following line instructing the kernel to load SELinux:
GRUB_CMDLINE_LINUX="security=selinux"
Once you confirm the configuration, note the presence of an autorelabel file in the root filesystem. This file tells SELinux to relabel every file upon reboot. For example, listing the root directory including hidden files shows:
jeremy@kodekLOUD:~$ ls -a /
. bin.usr-is-merged etc lib.usr-is-merged opt sbin swap.img var
.. boot home lost+found proc sbin.usr-is-merged root snap
.autolabel cdrom lib media mnt run srv tmp usr
Now, reboot your system to allow SELinux to relabel the filesystem. The relabeling process might take some time depending on the number of files:
sudo reboot
During the first boot after enabling SELinux, the bootloader pauses for 30 seconds to allow intervention if necessary. Once complete, subsequent boots will operate normally. After rebooting, verify SELinux status by running:
sestatus
Expected output:
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: default
Current mode: permissive
Mode from config file: permissive
...
Understanding SELinux Modes
SELinux operates in two primary modes:
- Permissive: In this mode, SELinux logs policy violations without enforcing them. It is ideal for "training" your policies.
- Enforcing: Here, SELinux actively enforces policies and may deny actions that do not comply with the defined rules.
To check the current mode, run:
getenforce
When operating in permissive mode, actions that should be blocked are only logged. For instance, accessing via SSH may trigger AVC (Access Vector Cache) messages in the audit log. To inspect these messages, use:
sudo audit2why --all | less
The output might include entries like:
type=AVC msg=audit(1716410944.847:111): avc: denied { execute } for pid=922 comm="run-parts" name="1-landscape-sysinfo.wrapper" dev="dm-0" ino=284986 scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:usr_t:s0 tclass=file permissive=1
Was caused by:
Missing type enforcement (TE) allow rule.
You can use audit2allow to generate a loadable module to allow this access.
Note
Although similar audit entries might be repeated, a single occurrence generally provides enough insight into the issue.
Examining Process and File Contexts
You can verify SELinux contexts for running processes and files. For example, to inspect the contexts of SSH daemons, run:
ps -eZ | grep sshd_t
Sample output:
system_u:system_r:sshd_t:s0 905 ? 00:00:00 sshd
system_u:system_r:sshd_t:s0 906 ? 00:00:00 sshd
system_u:system_r:sshd_t:s0 1020 ? 00:00:02 sshd
To view the context for the SSH daemon executable:
ls -Z /usr/sbin/sshd
Expected output:
system_u:object_r:sshd_exec_t:s0 /usr/sbin/sshd
When a file labeled with sshd_exec_t
is executed, SELinux transitions the process into the sshd_t
domain, where its type enforcement rules become active.
Generating and Loading a Custom SELinux Policy
After operating in permissive mode and gathering necessary logs, you can generate a custom SELinux policy module from the audit logs. Run:
sudo audit2allow --all -M mymodule
The command output will include a message similar to:
********************* IMPORTANT *********************
To make this policy package active, execute:
semodule -i mymodule.pp
To load the custom policy module, execute:
sudo semodule -i mymodule.pp
Note
If you see a warning such as "libsemanage.add_user: user sddm not in password file", it is a known issue. You can safely ignore it if you are not using sddm.
Switching to Enforcing Mode
To temporarily switch SELinux from permissive to enforcing mode, execute:
sudo setenforce 1
getenforce
Expected output:
Enforcing
For initial testing, it is advisable to use temporary enforcement. Once you are sure that your custom policies cover all the necessary actions, make the change permanent by modifying /etc/selinux/config
.
Open the configuration file with:
sudo vim /etc/selinux/config
Then change:
SELINUX=permissive
to:
SELINUX=enforcing
Save the file and reboot the system to apply the changes.
Reviewing the Custom Policy Module
After loading the custom module, two files are created:
- A binary package (
mymodule.pp
) - A human-readable policy file (
mymodule.te
)
The .te
file contains the type enforcement rules. An excerpt might look like:
module mymodule 1.0;
require {
type sshd_t;
type tmp_t;
type getty_t;
type systemd_journal_init_t;
type policykit_t;
type mount_exec_t;
type kmod_t;
type var_t;
type systemd_journal_t;
type systemd_runtime_notify_t;
type xdg_cache_t;
type fixed_disk_device_t;
type fsadm_exec_t;
type mount_runtime_t;
type var_lib_t;
type mount_t;
type systemd_hostnamed_t;
type var_log_t;
type etc_runtime_t;
type lib_t;
type system_dbusd_t;
type usr_t;
};
class dir { add_name search watch write };
class dbus send_msg;
Review this file carefully before deploying the policies in production.
Understanding and Reviewing SELinux Type Enforcement
The custom module includes specific rules for the sshd_t
domain. For instance, it allows the SSH daemon to access files labeled with var_log_t
:
allow sshd_t var_log_t:file { append create getattr ioctl open };
This rule ensures that processes running in the sshd_t
domain, such as the SSH daemon, can write to log files like /var/log/auth.log
, which are labeled as var_log_t
.
You can verify these contexts with the following commands:
ps -eZ | grep sshd_t
ls -Z /var/log/auth.log
grep var_log_t:file mymodule.te
Expected outputs:
system_u:system_r:sshd_t:s0 905 ? 00:00:00 sshd
...
system_u:object_r:var_log_t:file /var/log/auth.log
...
allow sshd_t var_log_t:file { append create getattr ioctl open };
This level of fine-grained control helps restrict processes to specific files, reducing potential risks if a process (such as an NGINX web server) is compromised.
Changing SELinux File Contexts with chcon and restorecon
A file's SELinux context is composed of three parts: user, role, and type. To manually change a file’s context, use the chcon
command. For example, to view and modify the context of /var/log/auth.log
:
ls -Z /var/log/auth.log
sudo chcon -u unconfined_u /var/log/auth.log
To change the role and type:
sudo chcon -r object_r /var/log/auth.log
sudo chcon -t user_home_t /var/log/auth.log
Warning
Manual changes made with chcon
may be overridden during a complete filesystem relabel.
To restore the default context, use the restorecon
command. For example, to fix the context of /var/log/auth.log
based on /var/log/dmesg
as a reference:
sudo chcon --reference=/var/log/dmesg /var/log/auth.log
Fixing Contexts for Web Content
If you create a new directory for your website under /var/www
and add files, they may initially have an incorrect SELinux type (such as var_t
). To correct this recursively:
sudo mkdir /var/www
sudo touch /var/www/{1..10}
ls -Z /var/www/
# Files may initially be labeled as:
# unconfined_u:object_r:var_t:s0
sudo restorecon -R /var/www/
ls -Z /var/www/
After running restorecon -R /var/www/
, the files should be relabeled correctly (for example, as httpd_sys_content_t
), which is appropriate for web content. Note that restorecon
by default only restores the type, not the user or role. If needed, use the force option to restore all parts of the label.
To permanently assign a default label to a specific file (e.g., /var/www/10
), add a rule with semanage
:
sudo semanage fcontext --add --type var_log_t /var/www/10
# You might encounter:
# libsemanage.add_user: user sddm not in password file
sudo restorecon /var/www/10
For directories and their contents, remember to wrap the path in quotes if it contains special characters. Refer to the SELinux documentation for additional examples.
SELinux Booleans and Port Bindings
SELinux booleans act as switches to enable or disable related security policies. To list supported booleans and their current settings, run:
getsebool -a | grep virt_use_nfs
To set the virt_use_nfs
boolean, execute:
sudo setsebool virt_use_nfs 1
SELinux also manages which ports a daemon is allowed to bind to. To list all allowed port bindings:
sudo semanage port --list
For instance, SSH is typically restricted to port 22. To allow SSH to bind to an additional port (such as 2222), run:
sudo semanage port --add --type ssh_port_t --proto tcp 2222
To later remove this port binding:
sudo semanage port --delete --type ssh_port_t --proto tcp 2222
Note
Any warnings regarding the user sddm may be safely ignored if you are not using sddm.
Conclusion
SELinux offers a robust mechanism for enforcing security policies by confining processes and controlling file access through detailed security contexts. While the setup and testing process can seem complex initially, running in permissive mode allows you to identify and adjust potential issues before switching to enforcing mode for full protection.
With continuous study and practice, you can develop tailored SELinux policies that significantly reduce the risk of common attacks. Enjoy your journey to a more secure system!
Watch Video
Watch video content
Practice Lab
Practice lab