SELinux users on CentOS 7 – Actions and Deciphering error messages

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.

More about SELinux Users?

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:


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

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.

SELinux users on CentOS 7

Furthermore, let us discuss the benefits of SELinux users on CentOS 7.

SELinux in Action 1: Restrict Switched User Access

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
[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

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

We can stop the switch from happening by restricting their ability to switch accounts.

SELinux in Action 2: Restrict Permissions to Run Scripts

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

Then we create the script:

[guestuser@localhost ~]$ vi myscript.sh

Script contents:

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.

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


SELinux in Action 3: Restrict Access to Services

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


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:

default level: s0
range: s0

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:


Dominated Roles:




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:


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

 SELinux users on CentOS 7 and Audit Logs

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:


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.

Deciphering Error 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.

Multilevel Security

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:


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

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.

[Need urgent help with understanding SELinux? We can help you. ]


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.