Linux distributions come with Discretionary Access Control (DAC) preinstalled in them. A sudo user is usually created in a Linux system to work at root-level privileges. DAC system provides the sudo user with all the administrator rights which may be a security threat if the sudo user is not trustworthy.
SELinux is a Mandatory Access Control (MAC) system that replaced traditional DAC systems in modern Linux OS. SELinux allows the system admins to have more control over who can access the system. SELInux defaults to deny anything that is not explicitly allowed. Red Hat Enterprise Linux and all of its derivatives e.g. CentOS, Fedora, etc have SELinux already configured and running on the new installation.
SELinux has two modes of operation
SELinux Permissive Mode: While running in permissive mode, it works like a traditional DAC system but logs any violation to SELinux.
SELinux Enforcing Mode: The enforcing mode denies by default access to everything even for a sudo user until and unless it is explicitly allowed by writing a rule.
SELinux makes use of a set of rules, also known as Security Policies, to define what services, processes, and applications are accessible to which user on the system. Each time an application or a process makes a request to access a file in the operating system, SELinux checks its AVC to identify the permissions that the program is allowed to use the file with. In case that permission is being denied, an "avc:denied" is logged in /var/log messages.
As a SysAdmin, you must be aware of the following commands to work with SELinux:
- Verifying SELinux installation.
- Enable and Disable SELinux.
- The different SELinux modes.
- What is SELinux context.
- SELinux boolean.
- Getting SELinux to work with modified applications.
- Creating SELinux policy for a custom application.
- Linux hardening automation.
How to verify if SELinux is installed or not
First, use the following command:
sudo rpm -aq | grep selinux
The following output must be shown on a CentOS 7 system.
libselinux-2.5-14.1.el7.x86_64
selinux-policy-3.13.1-252.el7_7.6.noarch
selinux-policy-targeted-3.13.1-252.el7_7.6.noarch
libselinux-utils-2.5-14.1.el7.x86_64
libselinux-python-2.5-14.1.el7.x86_64
The following packages must be installed in the system to work with SELinux:
sudo yum install policycoreutils policycoreutils-python setools setools-console setroubleshoot
Disable SELinux Security Configuration
Many systems come preinstalled with SELinux but need to enable it manually.
To check SELinux status use the following command:
sudo sestatus
If SELinux is disabled, then it can be enabled by editing line number 5 in the following file:
sudo nano /etc/selinux/config
12
3 4 5 6 7 8 9 10 11 12 |
# This file controls the state of SELinux on the system.
SELINUXTYPE=targeted
|
- The system needs to be rebooted after editing this file.
SELinux Modes:
SELinux has two modes – Permissive and Enforcing.
- SELinux policy modules can be listed by issuing the command
sudo semodule -l
- To check which is mode SELinux is running use:
sudo getenforce
SELinux Permissive mode:
The permissive mode does not make use of any SELinux policies and allows everything, the same as a DAC system. However, it logs the denied actions in the /var/log/audit/audit.log
file.
The Permissive mode is usually used when a user wants to configure the system as it allows us to make changes to the System as a sudo user.
To change the mode to permissive the command should be sudo setenforce 0
What is SELinux Context:
SELinux has a context for every file, user, and process. SELinux context is divided into 3 parts: user, role, and type. A SELinux policy defines which user gets which roles. Each role will define what type of files each user can access.
SELinux Boolean:
Any Policy can be modified without reloading or recompiling it using SELinux Boolean variables. A list of Boolean variables can be seen by entering "getsebool -a". For example, to see the Booleans for apache webserver a command can be run as: sudo getsebool -a | grep "httpd_can"
The output resembles the following:
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_connect_mythtv --> off
httpd_can_connect_zabbix --> off
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
The value of a Boolean can be changed by using sudo setsebool -P httpd_can_network_connect ON
. This -P flag persists through reboots.
Getting SELinux to work with modified applications
Sometimes you need to run applications such as Apache webserver on a port other than standard port 80, or park the website files outside the default /var/www/ directory. In such cases, SELinux won't let the webserver start as it expects the connection to through standard ports only.
The following commands lets you check which ports are allowed for HTTPD (Apache webserver) in SELinux default policy:
sudo semanage port -l | grep http
http_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t tcp 5988
pegasus_https_port_t tcp 5989
If Apache is running on a non-standard port 2907 and website files are stored in /var/test_www, SELinux policy can be modified using:
sudo semanage port -a -t http_port -p tcp 2907
sudo semanage fcontext -a -e /var/www /var/test_www
sudo restorecon -Rv /var/
Creating SELinux Policy for a custom application
Let’s assume a simple application named "mydaemon" is created, that can read /var/log/messages file.
A systemd unit file can be created as nano mydaemon.service
[Unit]
Description=Simple testing daemon
[Service]
Type=simple
ExecStart=/usr/local/bin/mydaemon
[Install]
WantedBy=multi-user.target
Install and start the daemon:
cp mydaemon /usr/local/bin
cp mydaemon.service /usr/lib/system/system
systemctl start mydaemon
Varify that daemon is not confined by SELinux
ps -efZ | grep mydaemon
system_u:system_r:unconfined_service_t:s0 root 4117 1 0 16:56 ? 00:00:00 /usr/local/bin/mydaemon
sepolicy generate -init /usr/local/bin/daemon
Created the following files:
/home/example.user/mysepol/mydaemon.te # Type Enforcement file
/home/example.user/mysepol/mydaemon.if # Interface file
/home/example.user/mysepol/mydaemon.fc # File Contexts file
/home/example.user/mysepol/mydaemon_selinux.spec # Spec file
/home/example.user/mysepol/mydaemon.sh # Setup Script
Rebuild the system policy with the new policy module using the setup script created by the previous command:
./mydaemon.sh
Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile mydaemon.pp
Compiling targeted mydaemon module
Creating targeted mydaemon.pp policy package
rm tmp/mydaemon.mod.fc tmp/mydaemon.mod
+ /usr/sbin/semodule -i mydaemon.pp
...
restorecon -v /usr/local/bin/mydaemon /usr/lib/systemd/system
Restart the daemon, and check that it now runs confined by SELinux:
systemctl restart mydaemon
ps -efZ | grep mydaemon
system_u:system_r:mydaemon_t:s0 root 8150 1 0 17:18 ? 00:00:00 /usr/local/bin/mydaemon
Because the daemon is now confined by SELinux, SELinux also prevents it from accessing /var/log/messages. Display the corresponding denial message:
ausearch -m AVC =ts recent
...
type=AVC msg=audit(1590247112.719:5935): avc: denied { open } for pid=8150 comm="mydaemon" path="/var/log/messages" dev="dm-0" ino=2430831 scontext=system_u:system_r:mydaemon_t:s0 tcontext=unconfined_u:object_r:var_log_t:s0 tclass=file permissive=1
...
Use the audit2allow
tool to suggest changes
Ausearch -m AVC -ts recent | audit2allow -R
require {
type mydaemon_t;
}
#============= mydaemon_t ==============
logging_write_generic_logs(mydaemon_t)
Because rules suggested by audit2allow
can be incorrect in some cases, use only a part of its output to find the corresponding policy interface:
grep -r "logging_write_generic_logs" /usr/share/selinux/devel/include | grep .if
/usr/share/selinux/devel/include/system/logging.if:interface(logging_write_generic_logs',
Check the definition of the interface:
cat /usr/ share/selinux/devel/include/system/logging.if
...
interface(logging_write_generic_logs',
gen_require(`
type var_log_t;
')
files_search_var($1)
allow $1 var_log_t:dir list_dir_perms;
write_files_pattern($1, var_log_t, var_log_t)
')...
In this case, you can use the suggested interface. Add the corresponding rule to your type enforcement file:
echo "logging_write_generic_logs(mydaemon_t)" >> mydaemon.te
Reinstall the policy:
./mydaemon.sh
Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile mydaemon.pp
Compiling targeted mydaemon module
Creating targeted mydaemon.pp policy package
rm tmp/mydaemon.mod.fc tmp/mydaemon.mod
+ /usr/sbin/semodule -i mydaemon.pp
...
Verification
Check that your application runs confined by SELinux, for example:
ps -efZ | grep mydaemon
system_u:system_r:mydaemon_t:s0 root 8150 1 0 17:18 ? 00:00:00
/usr/local/bin/mydaemon
Verify that your custom application does not cause any SELinux denials:
ausearch -m AVC -ts recent
<no matches>
Linux Hardening Automation
SELinux is a really handy tool for enforcing security policies in high-security server environments. Using tools such as SELinux and enforcing a secured configuration policy is mandatory in every network that handles sensitive information. The common process of hardening requires to:
a. Perform impact analysis, indicating what will be the impact of each configuration change on production.
b. Implement each policy on the correct machine and environment.
c. Monitor the compliance posture, remediate any undesired change and maintain compliance through the dynamic nature of the network.
This complex process often leads organizations to neglect hardening, therefore, being exposed to audit failures and fines and cyber attacks. By automating the hardening process organizations don’t need to either hold the knowledge and the manpower in-house since tools such as CalCom Hardening Automation Suite turns hardening into a matter of a click of a button. To learn more about hardening automation, continue your reading here.