This course is using an outdated version of the technology and is now considered to be Legacy content. It will be removed from our catalog on June 28, 2024. Please be sure to complete your course and finish any remaining labs before that date. We recommend moving to version 9.2, which is the latest version currently available.
Abstract
| Goal | Improve security and confinement between processes by using SELinux and advanced SELinux techniques and analysis. |
| Objectives |
|
| Sections |
|
| Lab | Mitigating Risk with SELinux |
After completing this section, students should be able to configure SELinux in Enforcing mode on a server that has been running with SELinux disabled.
Security Enhanced Linux (SELinux) is an additional layer of system security. A primary goal of SELinux is to protect user data from system services that have been compromised.
SELinux confines programs, mainly system services, to the minimum amount of privilege they require to do their jobs successfully. For that purpose, a set of security rules determine which processes can access which files, directories, and ports. If there is no explicit rule for access, SELinux denies the operation.
Every file, process, directory, and port has a particular security label called the SELinux context. A context is used by SELinux rules to control access.
For files and directories, SELinux stores the contexts as file system extended attributes.
For processes and ports, the kernel maintains the contexts in memory.
You can view the context of processes and files by adding the -Z option to the ps and ls commands.
For ports, use the semanage port -l command.
[user@demo ~]$ps -Z -C httpdLABEL PID TTY TIME CMDsystem_u:system_r:httpd_t:s031270 ? 00:00:01 httpdsystem_u:system_r:httpd_t:s031271 ? 00:00:00 httpdsystem_u:system_r:httpd_t:s031272 ? 00:00:00 httpdsystem_u:system_r:httpd_t:s031273 ? 00:00:00 httpdsystem_u:system_r:httpd_t:s031274 ? 00:00:00 httpdsystem_u:system_r:httpd_t:s031275 ? 00:00:00 httpd[user@demo ~]$ls -Z -d /var/www/htmldrwxr-xr-x. root rootsystem_u:object_r:httpd_sys_content_t:s0/var/www/html[user@demo ~]$ls -Z -d /home/userdrwx------. user userunconfined_u:object_r:user_home_dir_t:s0/home/user[root@demo ~]#semanage port -l | grep " 80,"http_port_ttcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
SELinux contexts have the following syntax:
user:role:type:level
In Red Hat Enterprise Linux, SELinux bases most of its rules on the third element: the context type.
The context type for the web server is httpd_t. Context types for processes are also known as domains.
The context type for files and directories in /var/www/html is httpd_sys_content_t.
The context type for web server ports is http_port_t.
The previous figure shows that an SELinux rule allows Apache, whose domain is httpd_t, to access files and directories with the httpd_sys_content_t context type.
Another rule allows Apache to access its log files under /var/log/httpd. The rule allows the httpd_t domain to access files and directories with the httpd_log_t type.
There is no rule to allow the httpd_t domain to access files and directories with the user_home_dir_t type.
Access is therefore denied.
If a malicious user manages to exploit a security flaw in Apache, they cannot access the contents of user home directories, even if the Linux access rights permit the access.
This way, SELinux mitigates the risk while the security flaw is fixed.
Changing SELinux Contexts for Files and Directories
Usually, a newly created file inherits the context of its parent directory.
[root@demo ~]#ls -Zd /etc/httpd/conf.d/drwxr-xr-x. root root system_u:object_r:httpd_config_t:s0 /etc/httpd/conf.d/[root@demo ~]#touch /etc/httpd/conf.d/test[root@demo ~]#ls -Z /etc/httpd/conf.d/test-rw-r--r--. root root unconfined_u:object_r:httpd_config_t:s0 /etc/httpd/conf.d/test
Therefore, you rarely need to change directory or file contexts manually. Sometimes, however, files get the wrong label, or you create a custom directory for your application that needs to have the correct context. In those situations, you need to relabel the files and directories.
The following log message is a typical SELinux denial due to a mislabeled file.
type=AVC msg=audit(1533046323.633:489): avc: denied { getattr } for pid=31274comm="httpd" path="/var/www/html/index.html" dev="vdb1" ino=4602819scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
The message gives the name and PID of the process, and the path, device, and inode of the file or directory that the process tries to access.
The tclass attribute specifies the type of the targeted object, either a file or a directory.
The scontext attribute, or source context, is the context of the process, and tcontext, or target context, is the actual context of the file or directory.
This message indicates that SELinux denies the httpd process, whose PID is 31274 and domain is httpd_t, access to the /var/www/html/index.html file, whose inode number is 4602819 on the vdb1 device, and whose context type is admin_home_t.
To fix such mislabeling issues, use the restorecon command.
[root@demo ~]#restorecon -v /var/www/html/index.htmlrestorecon reset /var/www/html/index.html context unconfined_u:object_r:admin_home_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
The -v options lists the relabeled files.
You can also add the -R option to recursively restore the contexts on directories.
Notice that with restorecon you do not specify the context to apply. The command uses rules in the SELinux policy to determine what the context of the file should be.
Defining SELinux Default File Context Rules
File context rules already exist in SELinux for the standard system files and directories. You can list these rules with the semanage fcontext -l command.
[root@demo ~]#semanage fcontext -l...output omitted... /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 ...output omitted...
Rules use extended regular expressions to specify the path and file names.
The most common extended regular expression used is (/.*)?, which means "optionally, match a / directory followed by any number of characters".
It matches the directory listed before the expression and everything in that directory recursively.
When you create a custom directory for your application, you also need to add a rule. This way, restorecon can apply the context you expect for your directory. To add a new rule, use the semanage fcontext -a command.
[root@demo ~]#mkdir /virtual[root@demo ~]#touch /virtual/index.html[root@demo ~]#ls -Zd /virtual/drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /virtual/[root@demo ~]#ls -Z /virtual/index.html-rw-r--r--. root root unconfined_u:object_r:default_t:s0 /virtual/index.html[root@demo ~]#semanage fcontext -a -t httpd_sys_content_t '/virtual(/.*)?'[root@demo ~]#restorecon -Rv /virtualrestorecon reset /virtual context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0 restorecon reset /virtual/index.html context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0[root@demo ~]#ls -Zd /virtualdrwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /virtual[root@demo ~]#ls -Z /virtual/index.html-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /virtual/index.html
Labeling SELinux Ports
SELinux does more than just label files and processes.
Network traffic is also tightly controlled.
One of the methods that SELinux uses for controlling network traffic is to label network ports; for example, TCP port 22 has the label ssh_port_t associated with it.
Whenever a process wants to listen on a port, SELinux controls if the label associated with that process, the domain, is allowed to bind to that port label. This can stop a rogue service from taking over ports otherwise used by other legitimate network services.
If you decide to run a service on a nonstandard port, there is a high chance that you also need to update the SELinux port labels.
In some cases, the system has already labeled the port with a type that you can use.
For example, because web applications sometimes use TCP port 8008, that port is already labeled with http_port_t, the default port type for the web server.
[root@demo ~]#semanage port -l | grep 8008http_port_ttcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
The following log message is a typical SELinux denial due to a mislabeled port.
type=AVC msg=audit(1533133795.952:571): avc: denied {name_bind} for pid=31311comm="mysqld" src=13306scontext=system_u:system_r:mysqld_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket
The message provides the name and PID of the process, and the port that the process tries to access.
The tclass attribute specifies the type of the targeted object; a TCP socket in this example.
The src attributes indicates the port number.
name_bind is the system operation that SELinux denies.
This message indicates that SELinux denies the mysqld process, whose PID is 31311, from binding to the TCP socket port 13306.
To fix such mislabeling issues, use the semanage port -a command.
[root@demo ~]#semanage port -a -t mysqld_port_t -p tcp 13306[root@demo ~]#semanage port -l | grep 13306mysqld_port_t tcp13306, 1186, 3306, 63132-63164
Using the SELinux Booleans
SELinux Booleans are switches that change the behavior of the SELinux policy. They enable or disable sets of SELinux access rules. Booleans can be used by security administrators to tune the policy to make selective adjustments.
Use the getsebool command to display SELinux Booleans, and the setsebool command to modify them. setsebool -P modifies the SELinux policy to make the modification persistent.
[root@demo ~]#getsebool -a | headabrt_anon_write --> off abrt_handle_event --> off abrt_upload_watch_anon_write --> on ...output omitted...[root@demo ~]#getsebool httpd_enable_homedirshttpd_enable_homedirs --> off[root@demo ~]#setsebool -P httpd_enable_homedirs on[root@demo ~]#getsebool httpd_enable_homedirshttpd_enable_homedirs --> on
Accessing the Documentation
Each SELinux command has a manual page. Each semanage subcommand also has its own manual page. For example, the manual page for semanage fcontext is semanage-fcontext(8), and for semanage port, it is semanage-port(8).
Some domains, such as httpd_t and sshd_t, have dedicated manual pages that describe all their types and Booleans.
In Red Hat Enterprise Linux 7, the selinux-policy-doc package, in the rhel-7-server-optional-rpms channel, provides these manual pages.
You can also build the man pages yourself; install the policycoreutils-devel package and run the sepolicy manpage command with the following options:
-a to build all the manual pages.
-d to only build the manual page of a specific domain.
For example:
domain
[root@demo ~]#sepolicy manpage -d httpd_t/tmp/apache_selinux.8 /tmp/httpd_selinux.8
-p to create the manual pages in a specific directory, for example in the standard path/usr/share/man/man8 directory.
By default the sepolicy manpage command generates the manual pages in /tmp.
SELinux can work in one of three modes: enforcing, permissive, disabled.
Enforcing
In this mode, SELinux actively enforces its access controls. SELinux both logs attempted violations and protects the system from them.
Permissive
In this mode, SELinux does not block any access, but still logs access violations it would have denied in enforcing mode, and maintains file, port, and process contexts.
You do not need to reboot to switch from Enforcing to Permissive or back again.
Disabled
In this mode, SELinux is completely disabled.
It does not block access or log violations.
In addition, SELinux file and port contexts are not maintained, and processes do not have SELinux contexts assigned.
You need to reboot your system to disable SELinux, or to switch from the disabled mode to the enforcing or permissive mode.
To display the SELinux mode in effect, use the getenforce command.
[user@demo ~]$getenforceEnforcing
Use the setenforce command to switch between enforcing and permissive.
[root@demo ~]#setenforceusage: setenforce [ Enforcing | Permissive | 1 | 0 ][root@demo ~]#setenforce 0[root@demo ~]#getenforcePermissive[root@demo ~]#setenforce 1[root@demo ~]#getenforceEnforcing
To persistently set the mode at boot time, edit the /etc/selinux/config configuration file.
[root@demo ~]#vim /etc/selinux/config# This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=enforcing# SELINUXTYPE= can take one of three two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted
To configure the system to start SELinux in disabled, enforcing, or permissive mode at boot time, set the SELINUX variable to the desired value.
This should work without problems when switching between permissive and enforcing modes.
It should also work without problems which switching a machine to disabled mode.
When you switch a system that has been running in disabled mode to enforcing or permissive mode, you may see problems or unexpected access violations.
If SELinux is disabled, then it cannot set the SELinux contexts on any files that are created.
This means that there may be files on the system with incorrect contexts.
Those contexts could be on files installed as part of the operating system, or on files belonging to applications or application content that you added to the system.
When you take an existing system that was installed with SELinux in disabled mode, and put it in one of its enabled modes (permissive or enforcing), that is the first time that the system can correctly manage file contexts and check for SELinux permission issues.
Switching directly to enforcing mode will probably lead to many access violations as processes attempt to access unlabeled or mislabeled files.
It is possible to take an existing system in disabled mode and successfully migrate it to enforcing mode through a more considered process.
If you switch an existing system directly from disabled mode to enforcing mode, expect to see access violations and other SELinux issues.
The next part of this section covers how to better manage this configuration change more gracefully.
In general, a better practice is to configure systems in either permissive or enforcing mode when they are installed.
It is easier to convert a system to enforcing mode from permissive mode than it is to convert it from disabled mode.
When you have systems running in the SELinux disabled mode, and you decide to switch to enforcing, you need to proceed with care to avoid having your applications blocked by SELinux.
Follow these steps as a general guide:
Switch your system to permissive mode.
In this mode, SELinux does not enforce the policy, but at least the denials are reported in the /var/log/audit/audit.log file.
You need to plan some downtime to switch from disabled to permissive because you need to reboot and relabel the file systems.
Review the audit.log file for denials.
Remember that in permissive mode, SELinux does not enforce these denials.
Fix the denials. This is probably the most complicated step because you need to analyze each denial and grasp the behavior of the suspicious application to respond appropriately.
Repeat steps 2 and 3 until you have addressed all the denials.
After an observation period to make sure that you have fixed all the issues, switch to enforcing mode.
With complex applications, you may want to develop your own SELinux policy. You might add SELinux contexts specific to the application, and new SELinux rules. Developing such a custom policy is beyond the scope of this course.
For applications developed in-house, the best solution is often to involve and train your developers to work with SELinux. Because they know their application, which files and directories it is accessing, and which network ports it is using, they can provide and maintain the application's SELinux policy. SELinux can become part of their development and testing workflow.
For third-party applications, your first step is to read the application documentation or contact the application vendor. They may already have an SELinux policy available that you can use.
Reviewing SELinux Access Violation Audit Events
When SELinux denies access to a resource, it also logs an audit event.
If the auditd service is started, the system logs the SELinux audit events in /var/log/audit/audit.log.
Otherwise, the system sends SELinux messages to the /var/log/messages file.
In Red Hat Enterprise Linux, the audit package is installed by default unless you explicitly remove it from the package selection at installation time.
To extract the SELinux denials from the log file, use the standard Linux tools.
[root@demo ~]#grep denied /var/log/audit/audit.logtype=AVC msg=audit(1533046323.631:488): avc:denied{ getattr } for pid=31274comm="httpd" path="/var/www/html/index.html" dev="vdb1" ino=4602819scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file type=AVC msg=audit(1533046323.633:489): avc:denied{ getattr } for pid=31274comm="httpd" path="/var/www/html/index.html" dev="vdb1" ino=4602819scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
You can also use the ausearch command.
The -m AVC and -ts boot options filter the output to only display messages from SELinux, and since the last system boot.
With the -ts option, you can also use the recent and today parameters to only display messages from the last 10 minutes, or since midnight.
[root@demo ~]#ausearch -m AVC -ts boot---- time->Tue Jul 31 10:12:03 2018type=PROCTITLE msg=audit(1533046323.631:488): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44 type=SYSCALL msg=audit(1533046323.631:488): arch=c000003e syscall=4 success=no exit=-13 a0=559069ca5578 a1=7ffcb501ec90 a2=7ffcb501ec90 a3=7f201a844712 items=0 ppid=31270pid=31274auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null) type=AVC msg=audit(1533046323.631:488): avc: denied { getattr } for pid=31274comm="httpd" path="/var/www/html/index.html" dev="vdb1" ino=4602819scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file ---- time->Tue Jul 31 10:12:03 2018type=PROCTITLE msg=audit(1533046323.633:489): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44 type=SYSCALL msg=audit(1533046323.633:489): arch=c000003e syscall=6 success=no exit=-13 a0=559069ca5658 a1=7ffcb501ec90 a2=7ffcb501ec90 a3=0 items=0 ppid=31270pid=31274auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null) type=AVC msg=audit(1533046323.633:489): avc: denied { getattr } for pid=31274comm="httpd" path="/var/www/html/index.html" dev="vdb1" ino=4602819scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
As you can see, these are normal audit events with an AVC audit record.
It looks like these two events, 2 milliseconds apart, were attempts by PID 31274, a /usr/sbin/httpd daemon, to access /var/www/html/index.html (inode 4602819 on /dev/vdb1).
SELinux blocked a getattr attempt on that file.
That is an attempt to read information about the file.
On the x86_64 architecture, the system calls that were attempted (4 and 6) are stat and lstat calls, which would both try to read the file's metadata.
When enabling enforcing mode, usually the problem will be an unlabeled or mislabeled file.
That appears to be the case here.
The httpd process has an SELinux domain of httpd_t, and it is trying to read a file of type admin_home_t.
That does not look like the right type for the file.
If the default policy settings are still in place, the restorecon /var/www/html/index.html command should fix the problem by setting the file's SELinux type to one that works; httpd_sys_content_t.
If the file was in an unusual location, you would need to determine the correct context by looking at files that work, the documentation from selinux-policy-doc, or by doing some policy analysis to figure out what to do.
Later in this chapter we will look at how to do basic policy analysis.
Using Permissive Domains
In permissive mode, SELinux logs the access that it should have denied, but allows the access anyway.
Switching the system into permissive mode, perhaps to debug a specific process, may have severe security consequences because all the other services are now also unprotected by SELinux.
Sometimes, you may be migrating a system from disabled to enforcing mode, and you have resolved the AVC issues for all but one or two SELinux domains.
You can set the system to enforcing mode for most things, but leave just those one or two domains in permissive mode until you can determine their issues.
You can keep your system in enforcing mode and only set a specific domain or domains to permissive mode.
Use the semanage permissive -a domain command to set a specific domain in permissive mode.
This makes it act like an unconfined domain, although accesses that the policy would normally block will still be logged as violations.
The following command sets the Apache httpd_t domain in permissive mode.
[root@demo ~]#semanage permissive -a httpd_t
Use the semanage permissive -l command to list the domains in permissive mode.
[root@demo ~]#semanage permissive -lCustomized Permissive Types httpd_t Builtin Permissive Types
Use the semanage permissive -d domain command to switch a domain back to enforcing.
The following command changes back the httpd_t domain to enforcing.
[root@demo ~]#semanage permissive -d httpd_t
Using Ansible for SELinux Remediation
When you have a set of identical systems, running the same applications, you can use Ansible to configure SELinux and remediate denials in a reproducible and identical way on those systems.
Ansible provides a mechanism called roles that allow you to include a set of tasks, files, and variables in an Ansible Playbook that help you apply a specific configuration. They can generally be customized for your own settings by applying custom variables in the play that calls them. Roles make it easier to reuse code in other playbooks or to exchange code with other Ansible users. For example, your organization might have a standard role that configures Postfix on a server. The role might take different variables to control whether Postfix is set up as a null client or a relay. You could then write a play that calls that role, setting the right variables to correctly customize the Postfix configuration for the play's hosts.
Normally, the role would be called by listing it in a roles: section of the play:
- hosts: all
vars:
postfix_conf:
relay_domains: "$mydestination"
relay_host: "example.com"
roles:
- postfix
If a play has both a list of roles and a list of tasks, the roles are run first.
There are ways to adjust this.
A play with roles does not need to have any tasks.
Red Hat Enterprise Linux 7.4 introduced Red Hat Enterprise Linux System Roles as a Technology Preview. This is a collection of Ansible roles to help administrators configure their Red Hat Enterprise Linux systems. Install the rhel-system-roles package on your Ansible control node to deploy these roles.
You can use the SELinux role it provides to:
Set the SELinux mode: enforcing, permissive, or disabled.
Restore file and directory contexts. This is the equivalent of the restorecon command.
Label SELinux ports. This is the equivalent of the semanage port command.
Add new file context rules. This is the equivalent of the semanage fcontext command.
Switch Booleans. This is the equivalent of the setsebool command.
Documentation on how to use the SELinux role from rhel-system-roles is installed into /usr/share/doc/rhel-system-roles-*/selinux/README.md.
The following Ansible Playbook is an example that uses the SELinux role.
It ensures that SELinux is in enforcing mode, that the type httpd_sys_content_t is set on the contents of the /virtual directory, that the label on port 8080/TCP is set to http_port_t, and that the SELinux Boolean httpd_enable_homedirs is enabled.
---
- name: Configure SELinux for the custom Apache configuration
hosts: webfarm
become: true
become_user: root
vars:
# The linux-system-roles.selinux role uses those variables
# for selecting the operations to perform.
SELinux_mode: enforcing
SELinux_change_running: 1
SELinux_file_contexts:
- { target: '/virtual(/.*)?', setype: 'httpd_sys_content_t', ftype: 'a' }
SELinux_restore_dirs:
- /virtual
SELinux_ports:
- { ports: '8080', proto: 'tcp', setype: 'http_port_t', state: 'present' }
SELinux_booleans:
- { name: 'httpd_enable_homedirs', state: 'on', persistent: 'yes' }
roles:
- linux-system-roles.selinux
...
With the correct settings, and possibly some supporting plays and tasks, you can use this SELinux role to ensure that machines installed with specific applications are correctly running in enforcing mode even if they started in disabled mode.
The semanage-*(8) and restorecon(8) man pages.
Knowledgebase: "Red Hat Enterprise Linux (RHEL) System Roles"
For more information, refer to the Working with SELinux chapter in the SELinux User's and Administrator's Guide at https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/selinux_users_and_administrators_guide/#chap-Security-Enhanced_Linux-Working_with_SELinux