- Lab 2: Security Enhanced Linux (SELinux)
- 2.1: Introduction
- 2.2: Exploiting Red Hat Enterprise Linux 7 with Shellshock Vulnerability
- 2.3: Exploiting Fedora 27 with a Runcescape Vulnerability
- 2.4: Enabling SELinux via Ansible
- 2.4.1: Setting Up Environment
- 2.4.2: Reviewing Setup Steps (Preconfigured)
- 2.4.3: Testing the Preconfigured Setup
- 2.4.4: Turning SELinux On
- 2.4.5: SELinux Troubleshooting
- 2.4.6: Viewing and Executing the SELinux
setup-selinux.yml
Ansible Playbook - 2.4.7: Viewing SELinux Configuration Changes
- 2.4.8: Reverting Script
- 2.5: Setting Up a System with SELinux Confined Users
-
Long (~30 mins)
Use Security Enhanced Linux® (SELinux) to mitigate attacks that exploit privilege escalation vulnerabilities
Security Enhanced Linux (SELinux) can help proactively mitigate systems from the consequences of exploits during the window of vulnerability—that period of time after an exploit is discovered and before a security fix is released. This protection is provided by defining SELinux policies on your systems.
-
In the first and second sections of the lab, you assume the role of a hacker and exploit vulnerabilities on given Red Hat® Enterprise Linux® 7 and Fedora systems, and then you explore how SELinux can help to protect these systems.
-
In the third section, you use Ansible® to turn SELinux on for an environment of web servers in an automated fashion.
-
In the last section, you configure SELinux according to a U.S. Department of Defense STIG security control policy rule.
SELinux isolates all of the processes running on the system to mitigate attacks that take advantage of privilege escalation vulnerabilities. A privilege escalation vulnerability exists whenever a process gains more access rights than it is allowed to have. To prevent this, SELinux enforces a Mandatory Access Control (MAC) policy over processes running on the system. It labels every process, file, or directory according to rules specified in a security policy known as the SELinux policy. The policy also specifies how processes interact with each other and how they can access files and directories. SELinux denies every action that it is not explicitly allowed by the policy.
In the following sections, you see examples where using this process isolation can help protect you from an exploit of a privilege escalation vulnerability.
The Shellshock vulnerability is a bash
exploit that adds a command after the env
variable in a command, which allows an attacker to execute arbitrary code. The following image shows an example of the bash
exploit on an Apache server:
If you have a CGI script accessing user data or the internal network, this script can compromise both the internal network and user data on your system.
So what can SELinux do to mitigate the Shellshock bash
exploit?
It is important to understand that SELinux does not block the exploit, but rather prevents escalation from confined domains. These confined domains are defined by the SELinux policy installed on a system. Because SELinux places controls on its types (SELinux labels), the process is limited to doing what it was designed to do and cannot access what is not allowed by the SELinux policy.
This image shows the situation as described with SELinux enabled and in enforcing mode:
While the attacker can execute the CGI script, the attacker is not able to access either user data or certain parts of the internal network as defined in the SELinux policy.
The following sections describe how types provide process separation and how SELinux is able to mitigate the exploit’s effects.
In this part of the lab, you assume the role of a hacker and exploit Red Hat Enterprise Linux 7 (RHEL 7) using the Shellshock vulnerability.
As an administrator of Red Hat Enterprise Linux servers, you want to enable SELinux for the web servers in your environment to mitigate damages caused by zero-day vulnerabilities.
In this section, you exploit a RHEL system first with SELinux in permissive mode and again with SELinux in enforcing mode. You then analyze the SELinux denials.
In this section, you log in to the selinux1.example.com system and navigate to the SELinux scripts directory.
The exploit is executed from the selinux1.example.com system.
-
If you are not already there, log in to the bastion host as lab-user from your desktop system (replacing
GUID
with your lab-provided GUID and using r3dh4t1! as the password):[localhost ~]$ ssh [email protected]
-
If you are not already root, become root and then log in to the selinux1.example.com system:
[lab-user@workstation-GUID ~]# ssh [email protected]
-
Change to the
/root/selinux_scripts/
directory that has the scripts needed to perform the attack:[root@selinux1 ~]# cd /root/selinux_scripts
NoteYou can find copies of these selinux_scripts
at this Red Hat GitHub security demos link. -
Repeat the previous steps in this section on a second terminal that is also connected to selinux1.example.com.
In this section, using the two terminal shells that you opened on selinux1.example.com, you listen on TCP port 9999 (in the first shell) and execute the exploit script (in the second).
This image depicts how the exploit is executed:
On the left, you see the victim server, the selinux4 system. An HTTP request is sent to this server from the attacker machine selinux1 on the right. The HTTP request opens a shell to the attacker machine, which is listening on port 9999.
The victim server, selinux4, has SELinux set up in permissive mode by default. It is running the Apache web server and an older version of bash.
-
In the first terminal shell that you opened earlier on selinux1.example.com and whose working directory is
/root/selinux_scripts
, start Ncat listening on TCP port 9999:[root@selinux1 selinux_scripts]# nc -lvp 9999 Ncat: Version 7.50 ( https://nmap.org/ncat ) Ncat: Listening on :::9999 Ncat: Listening on 0.0.0.0:9999
Ncat is a feature-packed networking utility that reads and writes data across networks.
-
From the second selinux1 terminal, run the exploit:
[root@selinux1 selinux_scripts]# ./shellshock_exploit.sh
-
Back on the first selinux1 terminal (where you executed
nc
), look for abash
prompt to appear:[root@selinux1 selinux_scripts]# nc -lvp 9999 Ncat: Version 7.50 ( https://nmap.org/ncat ) Ncat: Listening on :::9999 Ncat: Listening on 0.0.0.0:9999 Ncat: Connection from 192.168.0.24. Ncat: Connection from 192.168.0.24:38668. bash: no job control in this shell bash-4.2$
-
At the bash prompt that appeared in the selinux1 shell, type id, then uname -a, followed by exit to see the results of the exploit:
bash-4.2$ id id uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_sys_script_t:s0 bash-4.2$ uname -a uname -a Linux selinux4.example.com 3.10.0-418.el7.x86_64 #1 SMP Thu May 26 20:35:02 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux bash-4.2$ exit
As you can see from the resulting output, these commands were executed on selinux4, the victim server (selinux4), despite the fact that the session was started on the attacker machine (selinux1).
The
id
command prints real and effective user and group IDs, where the user and group areapache
, demonstrating that the CGI script was started as the Apache user.The
uname
command prints system information. You can see the selinux4.example.com host name being printed, which indicates that this is the victim system.These commands indicate that the attack succeeded.
The victim server (selinux4) has been running SELinux in permissive mode. In this section, you switch SELinux to enforcing mode and then repeat the attack.
-
Connect to selinux4 and switch to enforcing mode:
[root@selinux1 selinux_scripts]# ssh root@selinux4 setenforce 1
-
Begin the Shellshock attack again by listening on TCP port 9999 in one of the terminal shells for the selinux1 system:
[root@selinux1 selinux_scripts]# nc -lvp 9999 Ncat: Version 7.50 ( https://nmap.org/ncat ) Ncat: Listening on :::9999 Ncat: Listening on 0.0.0.0:9999
-
From the other terminal shell on the selinux1 system, run the exploit again:
[root@selinux1 selinux_scripts]# ./shellshock_exploit.sh
This time there is no
bash
prompt on the terminal where you executed thenc
command. This is because SELinux blocked this access.
In this section, you analyze what happened and why SELinux blocked the Shellshock exploit.
-
Connect to the selinux4 system from the selinux1 machine:
[root@selinux1 selinux_scripts]# ssh root@selinux4 [root@selinux4 ~]# ausearch -m AVC -ts today | grep name_connect type=AVC msg=audit(1524909646.681:86): avc: denied { name_connect } for pid=2091 comm="bashbug.sh" dest=9999 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:jboss_management_port_t:s0 tclass=tcp_socket
This is the AVC record from the audit daemon. It says that the CGI script, called
bashbug.sh
(labeled ashttpd_sys_script_t
) tried to connect to TCP port 9999 (labeled asjboss_management_port_t
). There is noallow
rule for this access, so the kernel denied access. This demonstrates that SELinux mitigated this attack.
The CVE-2019-5736 runc
escape is the latest vulnerability in the area of containers. It is a privilege escalation vulnerability that allows arbitrary code execution as root when a malicious process inside a container escapes from the container namespace and executes on the host system. Because any container can contain a malicious process, a container can gain root access to the entire system when a system administrator starts that container.
Fortunately, SELinux technology, which separates containers from each other and from the host system, blocks this exploit.
Note
|
For more information about this exploit, see Runcescape Security Vulnerability. |
In this section, you assume the role of a hacker and try to exploit Fedora 27 using the Runcescape vulnerability. As mentioned, this vulnerability in runc
allows breaking out from a container to gain root-level access on the host machine.
An earlier release of Fedora 27 is used for the purpose, because the Docker vulnerability is fixed in Red Hat Enterprise Linux 7. Fedora 27 is in its end-of-life state, which means no updates are provided and its Docker daemon is still vulnerable.
As an administrator of Red Hat Enterprise Linux servers, you want to enable SELinux for containers in your environment to mitigate damages caused by zero-day vulnerabilities.
This lab exercise consists of three key parts:
-
Exploiting a Fedora system with SELinux in enforcing mode
-
Exploiting a Fedora system with SELinux in permissive mode
-
Analyzing SELinux denials
The exploit is executed from the selinux6.example.com system.
-
If not already there, log in to the bastion host as lab-user from your desktop system (replacing
GUID
with your lab-provided GUID and using r3dh4t1! as the password):[localhost ~]$ ssh [email protected]
-
If you are not already root, become root and then log in to the selinux6.example.com system:
[lab-user@workstation-GUID ~]# ssh [email protected]
-
Change to the
/root/selinux_scripts/
directory that has the scripts needed to perform the attack:[root@selinux6 ~]# cd /root/selinux_scripts
NoteYou can find copies of these selinux_scripts
at this Red Hat GitHub security demos link.
In this section, you reproduce the attack. First, you verify the state of the host system, then you prepare the environment, and finally you execute the program within the container.
-
Verify that the host system is running SELinux in enforcing mode:
[root@selinux6 selinux_scripts]# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 31
Now you prepare the environment for the attack. A
runcescape.sh
shell script is already prepared for you. The script installs and starts a Docker container engine that has the vulnerablerunc
bundled inside. Then a standard container with the latest Ubuntu distribution is downloaded. Finally, the script uploads a malicious program to the container and prepares the exploit. -
Execute the script to prepare the environment:
[root@selinux6 selinux_scripts]# ./runcescape.sh [+] Installing docker [+] Starting docker [+] Downloading container [+] Uploading exploit [+] Executing docker
Now, the container with the malicious program is ready and waiting for a system administrator to execute the program inside the container. In this case, the malicious program is renamed to
bash
and replaced with the real/bin/bash
. -
Start the Docker container, which executes the malicious program:
[root@selinux6 selinux_scripts]# docker exec -it pwnme /bin/bash [+] bad_libseccomp.so booted. [+] opened ro /proc/self/exe <3>. [+] constructed fdpath </proc/self/fd/3> [+] bad_init is ready -- see </tmp/bad_init_log> for logs. [*] dying to allow /proc/self/exe to be unused...
Due to the nature of the attack, it may be necessary to execute the last command multiple times to make sure the attack is successful.
-
Execute the
docker exec -it pwnme /bin/bash
command multiple times.
In this section, you analyze what happened and why SELinux blocked the runc
escape exploit.
-
Run the
ausearch
command to see the SELinux denial:[root@selinux6 selinux_scripts]# ausearch -m AVC -ts today | grep container_runtime_exec_t type=AVC msg=audit(1554464510.001:479): avc: denied { write } for pid=5190 comm="bad_init" name="docker-runc-current" dev="dm-0" ino=9162730 scontext=system_u:system_r:container_t:s0:c915,c946 tcontext=system_u:object_r:container_runtime_exec_t:s0 tclass=file permissive=0
This is the AVC record from the audit daemon. It says that a malicious process inside the bad_init container (labeled
container_t
) is trying to modify the docker-runc-current container (labeled ascontainer_runtime_exec_t
) on the host system. SELinux blocked this clearly malicious behavor.
The selinux6 system has been running SELinux in enforcing mode. In this section, you switch SELinux to permissive mode and then you repeat the attack with SELinux in permissive mode.
-
Switch SELinux to permissive mode:
[root@selinux6 selinux_scripts]# setenforce 0 [root@selinux6 selinux_scripts]# getenforce Permissive
-
First, prepare the new container with the exploit:
[root@selinux6 selinux_scripts]# ./runcescape.sh [+] Installing docker [+] Starting docker [+] Downloading container [+] Uploading exploit [+] Executing docker
The container with the malicious program is ready for the system administrator to execute it. In this case, the malicious program is renamed to bash and replaced with the real
/bin/bash
. -
Repeat the attack and, because SELinux is in permissive mode, expect it to be successful:
[root@selinux6 selinux_scripts]# docker exec -it pwnme /bin/bash [+] bad_libseccomp.so booted. [+] opened ro /proc/self/exe <3>. [+] constructed fdpath </proc/self/fd/3> [+] bad_init is ready -- see </tmp/bad_init_log> for logs. [*] dying to allow /proc/self/exe to be unused...
Because of the nature of the attack, it is sometimes necessary to execute the last command multiple times to make sure the attack is successful.
-
Execute the last command (
docker exec -it pwnme /bin/bash
) multiple times until you see this output:[root@selinux6 selinux_scripts]# docker exec -it pwnme /bin/bash rpc error: code = 2 desc = containerd: container not started
This proves that exploit was successful.
-
Run the
ausearch -m AVC -ts today
command again and note that it shows the same SELinux denial as it did in enforcing mode—but because the machine is in permissive mode, the payload of the exploit is also executed. -
Determine that the exploit actually worked:
[root@selinux6 selinux_scripts]# cd / [root@selinux6 /]# ls bin dev HACKED lib media opt root sbin sys usr boot etc home lib64 mnt proc run srv tmp var
The payload creates a file named
HACKED
in the root file system.Note the
HACKED
file. This is simply an example—a real exploit, rather than merely creating a file in the/
directory, would have allowed arbitrary and far more dangerous code execution as root.
SELinux brings additional security to an environment and often needs to be modified to reflect the current environment configuration. In such cases, SELinux can be switched during debugging to permissive mode so that it does not block the basic functionality of the system. In permissive mode, you can run the system for some time to debug all possible SELinux AVC denials. Once you have adjusted the rules to handle all of the desired functionality, you can switch SELinux back to enforcing mode.
There are many ways to view or modify the installed SELinux policy. In this section, you use the SELinux Ansible role to distribute all of the required changes in the SELinux policy to make your Apache configuration work with SELinux in enforcing mode.
More specifically, you enable SELinux in your environment, which consists of an Apache server using both custom and standard paths for web files, so that the Apache server is fully confined by SELinux. You do this by using the SELinux system roles feature as an Ansible role to configure SELinux in an automated fashion.
In this section, you have an environment with Apache web servers, where both default and custom paths for Apache web files are used. Specifically:
-
/var/www/html
(default) -
/var/www_new/html
(custom)
These web files are accessible using TCP/80 and TCP/7070 ports on each web server:
-
selinux2.example.com:80 (default)
-
selinux2.example.com:7070 (custom)
-
selinux3.example.com:80 (default)
-
selinux3.example.com:7070 (custom)
-
selinux5.example.com:80 (default)
-
selinux5.example.com:7070 (custom)
By default, SELinux is disabled for all web servers. In a fully automated fashion, you turn SELinux on for all web servers without breaking any functionality using the SELinux system roles feature as an Ansible role.
The SELinux part of the lab environment consists of four machines:
-
selinux1, selinux1.example.com (RHEL-8 admin host)
-
selinux2, selinux2.example.com (RHEL-8 host)
-
selinux3, selinux3.example.com (RHEL-6 host)
-
selinux5, selinux5.example.com (RHEL-7 host)
The first selinux1.example.com host is used as an administrative interface to set up the other hosts, where you complete all of the configuration steps.
Important
|
All of the steps in this Setup Steps section have already been performed in the lab environment for you. They are described here for informative purposes, and must be executed only if you use the revert script for this lab. |
In this section, you explore what is already configured for you in this part of the lab.
-
If not already there, log in to the workstation bastion host as lab-user from your desktop system (replacing
GUID
with your lab-provided GUID and using r3dh4t1! as the password):[localhost ~]$ ssh [email protected]
-
Log in to the selinux1.example.com system as root:
[lab-user@workstation-GUID ~]# ssh [email protected]
-
Look at the DNS records on the selinux1 server:
[root@selinux1 ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.0.20 selinux2 192.168.0.21 selinux3 192.168.0.6 selinux5
-
Install Ansible if it is not already installed on the selinux1 host:
[root@selinux1 ~]# pip3 install ansible
-
Change to the
selinux_scripts
working directory on the selinux1 host:[root@selinux1 ~]# cd /root/selinux_scripts
-
Look at the created inventory file for your Ansible usage:
[root@selinux1 selinux_scripts]# cat inventory selinux2 ansible_python_interpreter=/usr/libexec/platform-python selinux3 selinux5
The Apache web servers are already set up using the setup-webserver.yml
playbook, which was executed on the selinux2, selinux3, and selinux5 hosts. SELinux is also turned off.
All of the Ansible commands in this section were executed from selinux1.example.com.
In this section, you test whether all of the servers are available via the ansible
command.
-
If not already there, log in to the workstation bastion host as lab-user from your desktop system (replacing
GUID
with your lab-provided GUID and using r3dh4t1! as the password):[localhost ~]$ ssh [email protected]
-
If you are not already root, become root and then log in to the selinux1.example.com system:
[lab-user@workstation-GUID ~]# ssh [email protected]
-
Change to the
selinux_scripts
working directory on the selinux1 host:[root@selinux1 ~]# cd /root/selinux_scripts
-
Test which servers are accessible:
[root@selinux1 selinux_scripts]# ansible all -i inventory -m ping -u root
This Ansible invocation specifies all listed servers in the inventory file and tests to see if they are accessible. Accessible servers return the
pong
response:selinux3 | SUCCESS => { "changed": false, "ping": "pong" } selinux5 | SUCCESS => { "changed": false, "ping": "pong" } selinux2 | SUCCESS => { "changed": false, "ping": "pong" }
-
Configure Apache web servers on the given servers via the
setup_webserver.yml
playbook:[root@selinux1 selinux_scripts]# ansible-playbook -i inventory -u root setup-webserver.yml
This playbook performs the following actions for all of the hosts mentioned in the
inventory
file:-
SELinux is disabled.
-
Apache web servers are:
-
Installed
-
Configured to listen on ports TCP/80 and TCP/7070 via the linux-sytem-roles/firewall Ansible role
-
Configured to use
/var/www/html
(default) and/var/www_new/html
(custom) as root directories -
Rebooted
-
-
-
Install the
setools-console
package containing SELinux policy query tools, which is used for SELinux troubleshooting:[root@selinux1 selinux_scripts]# ssh root@selinux2 yum install setools-console -y [root@selinux1 selinux_scripts]# ssh root@selinux3 yum install setools-console -y [root@selinux1 selinux_scripts]# ssh root@selinux5 yum install setools-console -y
-
Test the preconfigured setup steps:
[root@selinux1 selinux_scripts]# hostname selinux1.example.com
[root@selinux1 selinux_scripts]# cd /root/selinux_scripts
[root@selinux1 selinux_scripts]# curl selinux{2,3,5} <h1>Default Document Root</h1> <h1>Default Document Root</h1> <h1>Default Document Root</h1>
[root@selinux1 selinux_scripts]# curl selinux{2,3,5}:7070 <h1>Custom Document Root</h1> <h1>Custom Document Root</h1> <h1>Custom Document Root</h1>
[root@selinux1 selinux_scripts]# ssh root@selinux2 getenforce Disabled
[root@selinux1 selinux_scripts]# ssh root@selinux3 getenforce Disabled
[root@selinux1 selinux_scripts]# ssh root@selinux5 getenforce Disabled
-
Set SELinux to permissive mode and relabel the entire file system:
[root@selinux1 selinux_scripts]# ansible-playbook -i inventory -u root enable-selinux.yml
SELinux is switched to permissive mode using the
enable-selinux
playbook. This means that SELinux policy is enabled but not enforced. -
Use
getenforce
andsestatus
to view the current SELinux mode for your servers:[root@selinux1 selinux_scripts]# ssh root@selinux2 getenforce [root@selinux1 selinux_scripts]# ssh root@selinux2 sestatus
SELinux does not deny access, but denials are logged for actions that would have been denied had SELinux been running in enforcing mode.
-
Run the
curl
command to show logged denials for certain actions:[root@selinux1 selinux_scripts]# curl selinux{2,3,5}:7070 <h1>Custom Document Root</h1> <h1>Custom Document Root</h1> <h1>Custom Document Root</h1>
-
Note that AVC denials are generated and and view the denials using the
ausearch
:[root@selinux1 selinux_scripts]# ssh root@selinux2 [root@selinux2 ~]# ausearch -m AVC -su httpd_t -ts recent avc: denied { name_bind } for pid=1830 comm="httpd" src=7070 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket avc: denied { read } for pid=1831 comm="httpd" name="index.html" dev="vda3" ino=8511801 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file avc: denied { map } for pid=778 comm="httpd" path="/var/www_new/html/index.html" dev="dm-0" ino=8751871 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file permissive=1 avc: denied { open } for pid=778 comm="httpd" path="/var/www_new/html/index.html" dev="dm-0" ino=8751871 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file permissive=1 avc: denied { getattr } for pid=778 comm="httpd" path="/var/www_new/html/index.html" dev="dm-0" ino=8751871 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file permissive=1
In the previous section, you enabled SELinux and AVC denials occurred. Denial messages are logged when SELinux denies access. In this section, you investigate why these denials occurred.
SELinux troubleshooting can be performed on both the selinux2 and selinux3 hosts. You use the selinux2 host in the following examples.
-
Log in to the selinux2 host, if you are not already logged in:
[root@selinux1 selinux_scripts]# ssh root@selinux2
-
Verify that SELinux
httpd_t
process domain used for Apache web servers is not able to bind to TCP/7070 port by default:[root@selinux2 ~]# sesearch -A -s httpd_t -t unreserved_port_t -c tcp_socket -p name_bind | grep httpd_t
There is no default rule for this access in the SELinux policy on the RHEL-7 selinux5 and RHEL-8 selinux2 hosts.
-
Verify that Apache web servers can bind to other ports and these SELinux port types can be assigned to your selected custom port (TCP/7070):
[root@selinux2 ~]# sesearch -A -s httpd_t -c tcp_socket -p name_bind | grep httpd_t
The SELinux httpd_t
process domain used for Apache web servers is not able to read a general /var
content with the SELinux var_t
file type.
-
Verify that there is no rule for this access in the SELinux policy:
[root@selinux2 ~]# sesearch -A -s httpd_t -t var_t -c file -p read
-
Verify that Apache web servers can read a specific content with a specific SELinux file type:
[root@selinux2 ~]# sesearch -A -s httpd_t -c file -p read
-
Use the
matchpathcon
utility to decide the proper context for your alternate location for web files:[root@selinux2 ~]# matchpathcon /var/www/html /var/www/html system_u:object_r:httpd_sys_content_t:s0 [root@selinux2 ~]# exit
In this section, you examine and then execute an Ansible Playbook that switches SELinux to enforcing mode and applies all of the required changes for your web servers' configurations.
The playbook uses the linux-system-roles/selinux Ansible
role.
-
Make sure that you are on the selinux1 system, then navigate to the
/root/selinux_scripts
directory:[root@selinux1 selinux_scripts]# hostname selinux1.example.com [root@selinux1 selinux_scripts]# pwd /root/selinux_scripts
-
Open the
setup-selinux.yml
Ansible Playbook to take a closer look at it:[root@selinux1 selinux_scripts]# cat setup-selinux.yml - hosts: all become: true become_user: root vars: SELinux_type: targeted SELinux_mode: enforcing SELinux_change_running: 1 SELinux_file_contexts: - { target: '/var/www_new(/.*)?', setype: 'httpd_sys_content_t', ftype: 'a' } SELinux_restore_dirs: - /var/www/html - /var/www_new/ SELinux_ports: - { ports: '7070', proto: 'tcp', setype: 'http_port_t', state: 'present' } roles: - linux-system-roles.selinux
In the
vars
section, you switch SELinux to enforcing mode:SELinux_type: targeted SELinux_mode: enforcing SELinux_change_running: 1
Web servers use the custom
/var/www_new/html
path for web pages. SELinux labels must be fixed for this directory and subdirectories/files to reflect the default SELinux security labels for the/var/www/html
location. This is done by the following lines in the playbook:SELinux_file_contexts: - { target: '/var/www_new(/.*)?', setype: 'httpd_sys_content_t', ftype: 'a' }
After SELinux security labels are defined in the SELinux context database, these labels must be applied into extended attributes of selected files as done by these lines in the playbook:
SELinux_restore_dirs: - /var/www_new
All web servers are bound to the custom TCP/7070 port in the configuration. This setup must be reflected in a SELinux configuration as done in these lines of the playbook:
SELinux_ports: - { ports: '7070', proto: 'tcp', setype: 'http_port_t', state: 'present' }
-
Execute the
setup_selinux.yml
Ansible Playbook and apply these defined configurations for all of the servers:[root@selinux1 selinux_scripts]# ansible-playbook -i inventory -u root setup-selinux.yml
-
Test and view all of the recent SELinux configuration changes:
[root@selinux1 selinux_scripts]# ssh selinux2 semanage export [root@selinux1 selinux_scripts]# ssh selinux5 semanage export [root@selinux1 selinux_scripts]# ssh selinux3 semanage -o -
-
Determine the current SELinux status for all of the servers:
[root@selinux1 selinux_scripts]# ansible all -i inventory -u root -a getenforce
-
Check the functionality with SELinux enabled:
[root@selinux1 selinux_scripts]# curl selinux{2,3,5} <h1>Default Document Root</h1> <h1>Default Document Root</h1> <h1>Default Document Root</h1>
[root@selinux1 selinux_scripts]# curl selinux{2,3,5}:7070 <h1>Custom Document Root</h1> <h1>Custom Document Root</h1> <h1>Custom Document Root</h1>
This revert
script is needed to proceed to the next lab section (or if you plan to repeat the lab again from the beginning). Additionally, all of the steps in the Setup Steps section mentioned in the beginning of this lab must be executed, with the exception of the package installation steps.
In this section, you invoke the revert
script.
-
Run the
revert
script:[root@selinux1 selinux_scripts]# hostname selinux1.example.com [root@selinux1 selinux_scripts]# pwd /root/selinux_scripts [root@selinux1 selinux_scripts]# cat inventory selinux2 ansible_python_interpreter=/usr/libexec/platform-python selinux3 selinux5 [root@selinux1 selinux_scripts]# ansible-playbook -i inventory -u root revert-all.yml
As an enterprise system administrator, you may want your systems to follow the U.S. Department of Defense STIG security rule V-71971 so that your system is fully confined without unconfined users. In addition, you may want to have only one administrator user who can become root and manage the system, and to limit the access of other users.
In Red Hat Enterprise Linux, Linux users are mapped to the SELinux unconfined_u
user by default. All of the processes run by unconfined_u
are in the unconfined_t
domain. This means that users can access the system within the limits of the standard Linux DAC policy. However, a number of confined SELinux users are available in Red Hat Enterprise Linux. This means that users can be restricted to a limited set of capabilities. Each Linux user is mapped to an SELinux user using SELinux policy, allowing Linux users to inherit the restrictions placed on SELinux users.
This lab section is comprised of three key parts:
-
Confining regular Linux users
-
Confining Linux root users
-
Using the revert script
-
Execute the
revert
script if you did not do this in the previous section:[root@selinux1 selinux_scripts]# ansible-playbook -i inventory -u root revert-all.yml
All actions are performed on the selinux5 host, which is a RHEL 7.5 system.
-
If you are not already there, log in to the bastion host as lab-user from your desktop system (replacing
GUID
with your lab-provided GUID and using r3dh4t1! as the password):[localhost ~]$ ssh [email protected]
-
Log in to the selinux1.example.com system as root, then SSH to selinux5.example.com as root:
[lab-user@workstation-GUID ~]# ssh [email protected] [root@selinux1 ~]# ssh [email protected]
-
Use the
semanage
login tool to assign Linux users to SELinux users:[root@selinux5 ~]# semanage login -l
Users are mapped to
unconfined_u
by default.
-
Modify the record with
__default__
, which represents all of the users without an explicit mapping, to change the mapping of all Linux users:[root@selinux5 ~]# semanage login -m -s user_u -r s0 __default__ [root@selinux5 ~]# semanage login -l
system_u is a special user used only for system processes and is not listed.
After this, when users who are not root log in, their processes run in the user_t
domain.
-
Every user session, other than for root, runs with
user_t
:[root@selinux5 ~]# adduser user42
[root@selinux5 ~]# passwd user42
TipYou can select any password for user42, but make sure you remember what it is.
[root@selinux5 ~]# ssh user42@localhost user42@localhost's password: [user42@selinux5 ~]$ id -Z user_u:user_r:user_t:s0
[user42@selinux5 ~]$ ps axZ LABEL PID TTY STAT TIME COMMAND - 1 ? Ss 0:00 /usr/lib/systemd/systemd --switched-root --system --deserialize 21 user_u:user_r:user_t:s0 2780 ? S 0:00 sshd: user42@pts/1 user_u:user_r:user_t:s0 2781 pts/1 Ss 0:00 -bash user_u:user_r:user_t:s0 2808 pts/1 R+ 0:00 ps axZ # exit
-
Determine whether the user can become root.
-
Add this line to the
/etc/sudoers.d/administrators
file:user42 ALL=(ALL) NOPASSWD: ALL
[root@selinux5 ~]# visudo -f /etc/sudoers.d/administrators
-
In the text editor, copy and past this line:
user42 ALL=(ALL) NOPASSWD: ALL
TipTo insert the line, copy it and then press i to insert. To save and exit, press esc and then press :wq!.
-
Confirm your changes:
[root@selinux5 ~]# grep user42 /etc/sudoers.d/administrators user42 ALL=(ALL) NOPASSWD: ALL
[root@selinux5 ~]# ssh user42@localhost user42@localhost's password:
[user42@selinux5 ~]$ sudo -i sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted sudo: no valid sudoers sources found, quitting sudo: setresuid() [0, 0, 0] -> [1001, -1, -1]: Operation not permitted sudo: unable to initialize policy plugin
-
Attempt the same in permissive mode:
[user42@selinux5 ~]$ exit [root@selinux5 ~]# id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@selinux5 ~]# setenforce 0 [root@selinux5 ~]# ssh user42@localhost user42@localhost's password: [user42@selinux5 ~]$ sudo -i [root@selinux5 ~]# id uid=0(root) gid=0(root) groups=0(root) context=user_u:user_r:user_t:s0
[root@selinux5 ~]# id -Z User_u:user_r:user_t:s0
[root@selinux5 ~]# exit
[user42@selinux5 ~]$ exit [root@selinux5 ~]# setenforce 1
Because SELinux denials are not enforced in permissive mode,
user42
can become root. But you can see that the context stayeduser_t
and did not change tounconfined_t
.
There are two basic methods for confining the administrator user:
An administrator can be directly mapped to the sysadm_u
SELinux user so that when that user logs in, the session is run with sysadm_t
domain. Alternatively, you assign administrator users to staff_u
and configure sudo
so that specific users can gain the SELinux administrator role.
-
In this case, enable the
ssh_sysadm_login
SELinux boolean option to allow users assignedsysadm_u
to log in using SSH:[root@selinux5 ~]# semanage user -m -R "sysadm_r secadm_r" sysadm_u [root@selinux5 ~]# adduser -G wheel -Z sysadm_u admin1
[root@selinux5 ~]# passwd admin1
TipYou can select any password for admin1, but make sure you remember what it is.
[root@selinux5 ~]# semanage login -l | grep admin admin1 sysadm_u s0-s0:c0.c1023 *
[root@selinux5 ~]# setsebool -P ssh_sysadm_login on [root@selinux5 ~]# ssh admin1@localhost
[admin1@selinux5 ~]$ id -Z sysadm_u:sysadm_r:sysadm_t:s0-s0:c0.c1023
[admin1@selinux5 ~]$ sudo -i [sudo] password for admin1:
[root@selinux5 ~]# id -Z sysadm_u:sysadm_r:sysadm_t:s0-s0:c0.c1023
-
Perform the administrator’s operation, which can be executed only by admin SELinux users:
[root@selinux5 ~]# systemctl restart sshd [root@selinux5 ~]# exit [admin1@selinux5 ~]# exit
-
Using the second approach, assign administrator users to
staff_u
and configuresudo
so that specific users can gain the SELinux administrator role:[root@selinux5 ~]# adduser -G wheel -Z staff_u admin2
[root@selinux5 ~]# passwd admin2
TipYou can select any password for admin2, but make sure you remember what it is.
[root@selinux5 ~]# semanage login -l | grep admin admin1 sysadm_u s0-s0:c0.c1023 * admin2 staff_u s0-s0:c0.c1023 *
[root@selinux5 ~]# ssh admin2@localhost [admin2@selinux5 ~]$ id -Z staff_u:staff_r:staff_t:s0-s0:c0.c1023
[admin2@selinux5 ~]$ sudo -i [sudo] password for admin2: -bash: /root/.bash_profile: Permission denied -bash-4.2# id -Z staff_u:staff_r:staff_t:s0-s0:c0.c1023
-
Perform the administrator’s operation, which can be executed only by admin SELinux users:
-bash-4.2# systemctl restart sshd Failed to restart sshd.service: Access denied See system logs and 'systemctl status sshd.service' for details. -bash-4.2# exit [admin2@selinux5 ~]$ exit
-
Add the following rule to
sudoers
to allow the admin2 user to gain the SELinux administrator role:[root@selinux5 ~]# visudo -f /etc/sudoers.d/administrators
-
Append the following line to the end of the file:
admin2 ALL=(ALL) TYPE=sysadm_t ROLE=sysadm_r ALL admin2 ALL=(ALL) TYPE=secadm_t ROLE=secadm_r /usr/sbin/semanage,/usr/sbin/semodule
TipIn the
vi
text editor, press o, then copy and paste these lines into the buffer. Then press esc and then type :wq! to save and exit. -
The admin2 user can gain the administrator role using
sudo
:[root@selinux5 ~]# ssh admin2@localhost [admin2@selinux5 ~]$ sudo -i [sudo] password for admin2:
[root@selinux5 ~]# id -Z staff_u:sysadm_r:sysadm_t:s0-s0:c0.c1023
[root@selinux5 ~]# systemctl restart sshd [root@selinux5 ~]#
[root@selinux5 ~]# exit [admin2@selinux5 ~]# exit
In this section, you use a revert
script to restore the default SELinux user’s configuration.
-
(Optional) Run this
revert
script on the selinux5 host:[root@selinux5 ~]# hostname selinux5.example.com
[root@selinux5 ~]# cd /root [root@selinux5 ~]# sh confined_users_revert.sh