Radoslaw Smigielski

21 March 2020

Living with enabled SELinux

by Radosław Śmigielski

Why?

How many times things didn’t work for you and tou end up running one of these:

# setenforce 0
# setenforce Permissive
# echo 0 > /selinux/enforce

Below post hopefully will show you better way of living with SELinux enabed.

Intro

Let’s start from checking current status of SELinux with sestatus

[radek@photon][/tmp]$ 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

Line SELinux status shows you current SELinux status.

On most of the system there is auditd daemon running which manages SELinux policies but also logs SELinux events to log file, on Redhat based system this is the log file: /var/log/audit/audit.log

Example

Classic example of SELinux causing some problems looks like this:

[root@localhost cbis]# systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Sat 2020-03-21 05:15:25 UTC; 7s ago
  Process: 46754 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=1/FAILURE)
  Process: 46751 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)

Mar 21 05:15:25 localhost.localdomain systemd[1]: Starting The nginx HTTP and reverse proxy server...
Mar 21 05:15:25 localhost.localdomain nginx[46754]: nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
Mar 21 05:15:25 localhost.localdomain nginx[46754]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Mar 21 05:15:25 localhost.localdomain nginx[46754]: 2020/03/21 05:15:25 [emerg] 46754#0: open() "/var/log/nginx/access.log" failed (13: Permission denied)
Mar 21 05:15:25 localhost.localdomain nginx[46754]: nginx: configuration file /etc/nginx/nginx.conf test failed
Mar 21 05:15:25 localhost.localdomain systemd[1]: nginx.service: control process exited, code=exited status=1
Mar 21 05:15:25 localhost.localdomain systemd[1]: Failed to start The nginx HTTP and reverse proxy server.
Mar 21 05:15:25 localhost.localdomain systemd[1]: Unit nginx.service entered failed state.
Mar 21 05:15:25 localhost.localdomain systemd[1]: nginx.service failed.

Usually there is no meaningful error in service logs file, all permissions are set in proper way but you keep getting “Permission denied” or similar confusing error. And finally magic command “setenforce Permissive” resolves your problem.

But what if you can’t run your system in Permissive mode?

How to resolve SELinux denials

How to identify problem?

First examine /var/log/audit/audit.log file and try to identify lines related to your problem what is not always obvious.

grep denied /var/log/audit/audit.log

If you do not see any relevant denials in audit.log file, it could be that your system runs with dontaudit rule enabled, which is possible reason of silent denials. This is how you can disable this rule:

semanage dontaudit off

And finally tiny example, Jekyll is a static site generator running inside container failling to read files.

type=AVC msg=audit(1584772223.906:5543): avc:  denied  { read } for  pid=776207 comm="server.rb:286" name="404.html" dev="sda3" ino=580053375 scontext=system_u:system_r:container_t:s0:c717,c753 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1584772735.389:5549): avc:  denied  { read } for  pid=776207 comm="server.rb:286" name="404.html" dev="sda3" ino=580053375 scontext=system_u:system_r:container_t:s0:c717,c753 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1584773463.635:5574): avc:  denied  { noatsecure } for  pid=778375 comm="nm-dispatcher" scontext=system_u:system_r:NetworkManager_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=process permissive=0
type=AVC msg=audit(1584773463.635:5575): avc:  denied  { rlimitinh } for  pid=778375 comm="04-iscsi" scontext=system_u:system_r:NetworkManager_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=process permissive=0
type=AVC msg=audit(1584773463.635:5576): avc:  denied  { siginh } for  pid=778375 comm="04-iscsi" scontext=system_u:system_r:NetworkManager_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=process permissive=0
type=AVC msg=audit(1584773463.649:5577): avc:  denied  { noatsecure } for  pid=778377 comm="nm-dispatcher" scontext=system_u:system_r:NetworkManager_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=process permissive=0
type=AVC msg=audit(1584773587.513:5590): avc:  denied  { getattr } for  pid=776207 comm="thread_pool.rb*" path="/srv/jekyll/_posts/2020-03-21-SELinux_living_with.md" dev="sda3" ino=55976446 scontext=system_u:system_r:container_t:s0:c717,c753 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

Identify problem

SELinux audit.log includes msg=audit value in each and every line, msg=audit value let you decrypt reason of access denials.

We have below line in /var/log/audit/audit.log

type=AVC msg=audit(1584773587.513:5590): avc:  denied  { getattr } for  pid=776207 comm="thread_pool.rb*" path="/srv/jekyll/_posts/2020-03-21-SELinux_living_with.md" dev="sda3" ino=55976446 scontext=system_u:system_r:container_t:s0:c717,c753 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

With message audit id: 1584773587.513:5590. Let’s try to translate that message into something human readable.

[root@photon audit]# grep 1584773587.513:5590 /var/log/audit/audit.log | audit2why
type=AVC msg=audit(1584773587.513:5590): avc:  denied  { getattr } for  pid=776207 comm="thread_pool.rb*" path="/srv/jekyll/_posts/2020-03-21-SELinux_living_with.md" dev="sda3" ino=55976446 scontext=system_u:system_r:container_t:s0:c717,c753 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

        Was caused by:
                Missing type enforcement (TE) allow rule.
                You can use audit2allow to generate a loadable module to allow this access.

Now when we identify the problem let’s try to craft some new SELinux rules for what we need. Here is different example how to generate rules for some Ruby web applicatin

[root@photon audit]# grep ruby /var/log/audit/audit.log | audit2allow -m ryby_app

module ryby_app 1.0;

require {
        type user_tmp_t;
        type container_t;
        type container_runtime_t;
        type user_home_t;
        class dir { add_name create remove_name write };
        class file { create execute ioctl map open setattr unlink write };
}

#============= container_t ==============
allow container_t container_runtime_t:dir { add_name create remove_name write };
allow container_t container_runtime_t:file { create setattr unlink write };

#!!!! This avc is allowed in the current policy
allow container_t user_home_t:dir { add_name create remove_name write };

#!!!! This avc is allowed in the current policy
#!!!! This av rule may have been overridden by an extended permission av rule
allow container_t user_home_t:file { create ioctl open setattr unlink write };
allow container_t user_tmp_t:dir remove_name;

#!!!! This avc can be allowed using the boolean 'domain_can_mmap_files'
allow container_t user_tmp_t:file map;
allow container_t user_tmp_t:file { execute unlink };

Instead of printing the new rules on screen we can generate set of new rules and save them to file:

sudo grep ruby /var/log/audit/audit.log | audit2allow -M new_policy

This will geberate two new files in current working directory:

[root@photon sr]# file new_policy.pp new_policy.te
new_policy.pp: SE Linux modular policy version 1, 1 sections, mod version 19, MLS, module name new_policy
new_policy.te: ASCII text

At this point we can load the new set of rules:

semodule -i new_policy.pp

Above command will install new SELinux module what can be verified with dmesg output.

More info about SELinux here.

tags: SELinux