Are you trying to configure SELinux users on CentOS 7?
This guide is for you.
A Linux kernel security module, SELinux brings heightened security for Linux systems.
Here at Ibmi Media, as part of our Server Management Services, we regularly help our Customers to perform Linux related queries.
In this context, we shall look into SELinux users and how to fine-tune their access.
We don't create an SELinux user with a special command, nor does it have its own login access to the server.
Instead, they are in the policy that loads into memory at boot time.
Different SELinux users have different rights in the system and that is what makes them useful.
When we enforce SELinux, each regular Linux user account maps to an SELinux user account. This enables a regular account to inherit the permission of its SELinux counterpart.
To view this mapping, we run:
# semanage login -l
In CentOS 7, the result will be like this:
Login Name SELinux User MLS/MCS Range Service
__default__ unconfined_u s0-s0:c0.c1023 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *
Any regular Linux user account is first mapped to the default login.
Then, we have the root user that does not map to the “default” login. Rather, it has its own entry.
system_u is a different class of user, meant for running processes or daemons.
To see available SELinux users in the system, we can run:
# semanage user -l
The output will be like this:
Labeling MLS/ MLS/
SELinux User Prefix MCS Level MCS Range SELinux Roles
guest_u user s0 s0 guest_r
root user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r
staff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r
sysadm_u user s0 s0-s0:c0.c1023 sysadm_r
system_u user s0 s0-s0:c0.c1023 system_r unconfined_r
unconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_r
user_u user s0 s0 user_r
xguest_u user s0 s0 xguest_r
To verify our guest, we look at the fifth column, SELinux Roles.
SELinux roles are like gateways between a user and a process. If a role has the authorization to access a process domain, the users associating that role will be able to enter that process domain.
From the above table, we can see the unconfined_u user maps to the system_r and unconfined_r roles. Similarly, user sysadm_u is authorized for the sysadmr role, but guestu maps to the guest_r role.
The default login maps to the unconfinedu user, just like the root user maps to the unconfined_u user. Since the **default_** login represents any regular Linux user account, those accounts will be authorized for system_r and unconfined_r roles as well.
Hence, any Linux user that maps to the unconfined_u user will have the privilege to run any app that runs within the unconfined_t domain.
To demonstrate this, we run:
# id -Z
This shows the SELinux security context for root:
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Here, the root account maps to the unconfined_u SELinux user, and unconfined_u authorize for the unconfined_r role, which in turn authorize to run processes in the unconfined_t domain.
Moving ahead, our Support Experts suggest starting four new sessions with the four users This will help us switch between different accounts when in need.
i. regularuser
ii. switcheduser
iii. guestuser
iv. restricteduser
Next, we switch to the terminal session as the regularuser. We execute the id -Z command from there:
[regularuser@localhost ~]$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
In this case, regularuser account maps to the unconfined_u SELinux user account, and it can assume the unconfined_r role.
The role can run processes in an unconfined domain.
Furthermore, let us discuss the benefits of SELinux users on CentOS 7.
To see how SELinux can enforce security for user accounts, let us think about the regularuser account. Specifically, we do not want the user to be able to switch to other accounts, including the root account.
Initially, let’s check the user’s ability to switch to another account.
For example, here, the regularuser switches to the switcheduser account.
We assume he knows the password:
[regularuser@localhost ~]$ su – switcheduser
Password:
[switcheduser@localhost ~]$
Then, we go back to the terminal window as the root user and change regularuser's SELinux user mapping. We will map regularuser to user_u.
# semanage login -a -s user_u regularuser
We are adding (-a) the regularuser account to the SELinux (-s) user account user_u.
The change will not take effect until regularuser logs out and logs back in.
Going back to regularuser’s terminal window, we first switch back from switcheduser:
[switcheduser@localhost ~]$ logout
Next, the regularuser also logs out:
[regularuser@localhost ~]$ logout
We then open a new terminal window to connect as regularuser.
Next, we try to change to switcheduser again:
[regularuser@localhost ~]$ su – switcheduser
Password:
Eventually, we will see:
su: Authentication failure
Now if we run the id -Z command, we will see the output is quite different:
[regularuser@localhost ~]$ id -Z
user_u:user_r:user_t:s0
We can stop the switch from happening by restricting their ability to switch accounts.
Let us see another example to restrict user access through SELinux.
By default, SELinux allows users mapped to the guest_t account to execute scripts from their home directories.
We can run the getsebool command to check the boolean value:
# getsebool allow_guest_exec_content
The output shows the flag is on.
guest_exec_content –> on
To verify its effect, we change the SELinux user mapping for the guestuser account.
In order to do that, run:
# semanage login -a -s guest_u guestuser
Then we verify the action:
# semanage login -l
As we can see, guestuser now maps to the guest_u SELinux user account.
Login Name SELinux User MLS/MCS Range Service
__default__ unconfined_u s0-s0:c0.c1023 *
guestuser guest_u s0 *
regularuser user_u s0 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *
If we have an open terminal window as guestuser, we log out and log back as guestuser.
Next, we create an extremely simple bash script in the user’s home directory. The below code blocks first check the home directory, then creates the file and reads it on the console. Finally, the execute permission changes.
To verify we are in the guestuser home directory, we run:
[guestuser@localhost ~]$ pwd
/home/guestuser
Then we create the script:
[guestuser@localhost ~]$ vi myscript.sh
Script contents:
#!/bin/bash
echo “This is a test script”
Make the script executable:
# chmod u+x myscript.sh
When we try to execute the script as guestuser, it works:
[guestuser@localhost ~]$ ~/myscript.sh
This is a test script
Next, we go back to the root terminal window and change the boolean setting allow_guest_exec_content to off and verify it:
setsebool allow_guest_exec_content off
getsebool allow_guest_exec_content
guest\_exec\_content –> off
We go back to the console as guestuser and try to run the script again:
[guestuser@localhost ~]$ ~/myscript.sh
-bash: /home/guestuser/myscript.sh: Permission denied
So this is how SELinux can apply an additional layer of security on top of DAC.
In a production system, if we like them to access the server for viewing error messages and log files, and not to execute any shell scripts, we can first enable SELinux and then ensure the corresponding boolean value is set.
To see where this denial is, we can look at the /var/log/messages file.
Execute this from the root session:
# grep “SELinux is preventing” /var/log/messages
The last two messages in the file in our CentOS 7 server show the access denial:
Aug 23 12:59:42 localhost setroubleshoot: SELinux is preventing /usr/bin/bash from execute access on the file . For complete SELinux messages. run sealert -l 8343a9d2-ca9d-49db-9281-3bb03a76b71a
Aug 23 12:59:42 localhost python: SELinux is preventing /usr/bin/bash from execute access on the file .
The message also shows a long ID value and suggests we run the sealert command with this ID for more information.
The following command shows this (use your own alert ID):
sealert -l 8343a9d2-ca9d-49db-9281-3bb03a76b71a
And indeed, the output gives us greater detail about the error:
SELinux is preventing /usr/bin/bash from execute access on the file .
***** Plugin catchall_boolean (89.3 confidence) suggests ******************
If you want to allow guest to exec content
Then you must tell SELinux about this by enabling the ‘guest\_exec\_content’ boolean.
You can read ‘None’ man page for more details.
Do
setsebool -P guest\_exec\_content 1
***** Plugin catchall (11.6 confidence) suggests **************************
…
In the output note the lines at the beginning:
SELinux is preventing /usr/bin/bash from execute access on the file .
This gives us an idea of where the error is coming from.
Similarly, the next few lines also tell you how to fix the error:
If you want to allow guest to exec content
Then you must tell SELinux about this by enabling the ‘guest\_exec\_content boolean.
…
setsebool -P guest\_exec\_content 1
Roles become important when dealing with users and processes.
Initially, we make sure that the httpd daemon is not running in the system.
As the root user, to ensure this, we run:
# service httpd stop
Next, we switch to the terminal window we had logged in as a restricteduser and see the SELinux security context for it.
Start a new terminal session, if there isn’t one, against the system and log in as the restricteduser account:
[restricteduser@localhost ~]$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
The account has the default behavior of running as unconfined_u user and having access to unconfined_r role.
However, it does not have the right to start any processes within the system.
The following code block shows that restricteduser is trying to start the httpd daemon and getting an access denied error:
[restricteduser@localhost ~]$ service httpd start
Redirecting to /bin/systemctl start httpd.service
Failed to issue method call: Access denied
Then, we move back to the root user terminal window and ensure the restricteduser account adds to the /etc/sudoers file.
This action will enable the restricteduser account to use root privileges.
# visudo
And then in the file, we add the following line, save and exit:
restricteduser ALL=(ALL) ALL
If we now log out of the restricteduser terminal window and log back in again, we can start and stop the httpd service with sudo privileges:
[restricteduser@localhost ~]$ sudo service httpd start
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for restricteduser:
Redirecting to /bin/systemctl start httpd.service
The user can also stop the service:
[restricteduser@localhost ~]$ sudo service httpd stop
Redirecting to /bin/systemctl stop httpd.service
Suppose we want to stop this particular user from starting the httpd service even when the user’s account is listed in the sudoers file, we switch back to the root user's terminal window and map the restricteduser to the SELinux user_r account.
# semanage login -a -s user_u restricteduser
Going back, we log out and log back in again in a new terminal session as restricteduser.
One done, we can verify its access using the seinfo command from our root user’s window:
seinfo -uuser_u -x
The output shows the roles user_u can assume.
These are object_r and user_r:
user_u
default level: s0
range: s0
roles:
object_r
user_r
We can run the seinfo command to check what domains the user_r role is authorized to enter:
seinfo -ruser_r -x
There are a number of domains user_r is authorized to enter:
user_r
Dominated Roles:
user_r
Types:
git_session_t
sandbox_x_client_t
git_user_content_t
virt_content_t
policykit_grant_t
httpd_user_htaccess_t
telepathy_mission_control_home_t
qmail_inject_t
gnome_home_t
…
…
We can try the same command with a filter:
seinfo -ruser_r -x | grep httpd
There are a number of httpd related domains the role has access to, but httpd_t is not one of them:
httpd_user_htaccess_t
httpd_user_script_exec_t
httpd_user_ra_content_t
httpd_user_rw_content_t
httpd_user_script_t
httpd_user_content_t
Taking this example, if the restricteduser account tries to start the httpd daemon, the access should deny because the httpd process runs within the httpd_t domain and that is not one of the domains the user_r role is authorized to access.
In addition, we know user_u can assume the user_r role. This should fail even if the restricteduser account has been granted sudo privilege.
Going back to the restricteduser account's terminal window, we try to start the httpd daemon:
[restricteduser@localhost ~]$ sudo service httpd start
The access is denied:
sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted
Generally, the error messages log in specific files and can provide details of access denials.
In a CentOS 7 system we can look at it in two files:
/var/log/audit/audit.log
/var/log/messages
The auditd daemon and the rsyslogd daemon respectively populates these files.
The /var/log/audit/audit.log file will be used if the auditd daemon is running and /var/log/messages file if auditd is stopped and rsyslogd is running.
However, if both are running, we use both. While /var/log/audit/audit.log records detailed information, an easy-to-read version is kept in /var/log/messages.
We can use the grep command to sift through /var/log/messages file. Fortunately, SELinux comes with a few tools to make life a bit easier than that.
The first command is ausearch. We can make use of this command if the auditd daemon is running.
The following code looks at all the error messages related to the httpd daemon. We ensure we are in the root account:
ausearch -m avc -c httpd
It will list a number of entries, but we will concentrate on the last one:
—-
time->Thu Aug 21 16:42:17 2014
…
type=AVC msg=audit(1408603337.115:914): avc: denied { getattr } for pid=10204 comm=”httpd” path=”/www/html/index.html” dev=”dm-0″ ino=8445484 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file
If we look closely, the process domain is httpd_t and the file's type context is default_t.
Since the httpd daemon runs within a confined domain and the SELinux policy stipulates this domain does not have any access to files with default_t type, the access was denied.
We can use the sealert command with the id value of the error message in the /var/log/messages file.
In the following command we grep through the /var/log/message file for SELinux related errors:
# cat /var/log/messages | grep “SELinux is preventing”
This error logs when the restricted user tries to run the httpd daemon:
…
Aug 25 11:59:46 localhost setroubleshoot: SELinux is preventing /usr/bin/su from using the setuid capability. For complete SELinux messages. run sealert -l e9e6c6d8-f217-414c-a14e-4bccb70cfbce
As suggested, we ran sealert with the ID value and were able to see the details:
sealert -l e9e6c6d8-f217-414c-a14e-4bccb70cfbce
SELinux is preventing /usr/bin/su from using the setuid capability.
…
Raw Audit Messages:
type=AVC msg=audit(1408931985.387:850): avc: denied { setuid } for pid=5855 comm=”sudo” capability=7 scontext=user_u:user_r:user_t:s0 tcontext=user_u:user_r:user_t:s0 tclass=capability
type=SYSCALL msg=audit(1408931985.387:850): arch=x86_64 syscall=setresuid success=no exit=EPERM a0=ffffffff a1=1 a2=ffffffff a3=7fae591b92e0 items=0 ppid=5739 pid=5855 auid=1008 uid=0 gid=1008 euid=0 suid=0 fsuid=0 egid=0 sgid=1008 fsgid=0 tty=pts2 ses=22 comm=sudo exe=/usr/bin/sudo subj=user_u:user_r:user_t:s0 key=(null)
Hash: su,user_t,user_t,capability,setuid
Here, the first few lines of the output of sealert tell us about the remediation steps. However, if we look near the end of the output stream, we can see the “Raw Audit Messages” section.
The entry here is coming from the audit.log file, so we can use that section to help interpret the output here.
It is the fine-grained part of an SELinux security context.
So far we discussed, SELinux user, SELinux role, and SELinux type or domain. The fourth field of the security context shows the sensitivity and optionally, the category of the resource.
To understand it, let us consider the security context of the FTP daemon's configuration file:
# ls -Z /etc/vsftpd/vsftpd.conf
This security context shows a sensitivity of s0.
-rw——-. root root system_u:object_r:etc_t:s0 /etc/vsftpd/vsftpd.conf
The sensitivity is part of the hierarchical multilevel security mechanism.
When an SELinux system uses MLS for its policy type, it marks certain files and processes with certain levels of sensitivity. The lowest level is “current sensitivity” and the highest level is “clearance sensitivity”.
Going hand-in-hand with sensitivity is the category of the resource. Categories are labels assigned to a resource. The purpose of categorization is to further fine-tune access control.
For example, we can mark certain files with confidential sensitivity for users from two different internal departments.
Here is an example of sensitivity/category pair:
user_u:object_r:etc_t:s0:c0.c2
Here, the only sensitivity level is s0. We can also write the category level as c0-c2.
Now, let us find the details from the /etc/selinux/targeted/setrans.conf file:
# cat /etc/selinux/targeted/setrans.conf
#
# Multi-Category Security translation table for SELinux
#
#
# Objects can be categorized with 0-1023 categories defined by the admin.
# Objects can be in more than one category at a time.
# Categories are stored in the system as c0-c1023. Users can use this
# table to translate the categories into a more meaningful output.
# Examples:
# s0:c0=CompanyConfidential
# s0:c1=PatientRecord
# s0:c2=Unclassified
# s0:c3=TopSecret
# s0:c1,c3=CompanyConfidentialRedHat
s0=SystemLow
s0-s0:c0.c1023=SystemLow-SystemHigh
s0:c0.c1023=SystemHigh
A process allows read access to a resource only when its sensitivity and category level are higher than that of the resource.
The process can write to the resource when its sensitivity/category level is less than that of the resource.
This article covers more information about SELinux users on CentOS 7.
Deciphering SELinux Error Messages
We looked at one SELinux error message. We were then using the grep command to sift through /var/log/messages file. Fortunately SELinux comes with a few tools to make life a bit easier than that. These tools are not installed by default and require installing a few packages, which you should have installed in the first part of this tutorial.
The first command is ausearch. We can make use of this command if the auditd daemon is running. In the following code snippet we are trying to look at all the error messages related to the httpd daemon. Make sure you are in your root account:
ausearch -m avc -c httpd
In our system a number of entries were listed, but we will concentrate on the last one:
----
time->Thu Aug 21 16:42:17 2014
...
type=AVC msg=audit(1408603337.115:914): avc: denied { getattr } for pid=10204 comm="httpd" path="/www/html/index.html" dev="dm-0" ino=8445484 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file
Even experienced system administrators can get confused by messages like this unless they know what they are looking for. To understand it, let’s take apart each of the fields:
type=AVC and avc: AVC stands for Access Vector Cache. SELinux caches access control decisions for resource and processes. This cache is known as the Access Vector Cache (AVC). That's why SELinux access denial messages are also known as “AVC denials”. These two fields of information are saying the entry is coming from an AVC log and it’s an AVC event.
denied { getattr }: The permission that was attempted and the result it got. In this case the get attribute operation was denied.
pid=10204. This is the process id of the process that attempted the access.
comm: The process id by itself doesn’t mean much. The comm attribute shows the process command. In this case it’s httpd. Immediately we know the error is coming from the web server.
path: The location of the resource that was accessed. In this case it’s a file under /www/html/index.html.
dev and ino: The device where the target resource resides and its inode address.
scontext: The security context of the process. We can see the source is running under the httpd_t domain.
tcontext: The security context of the target resource. In this case the file type is default_t.
tclass: The class of the target resource. In this case it’s a file.