Month: December 2019

SELinux, systemd and python virtual environments

Recently I wanted to configure a gunicorn backend at our newest server. The aim was to run a flask-based python application using a virtual environment in a users home. The machine is running recent Red Hat 8 and the setup of systemd was straightforward and well documented in the internet (e.g. here). However, after

systemctl daemon-reload
systemctl enable ourservice
systemctl start ourservice

a strange permission denied error occurred (also journalctl helps in that context).

After rechecking the file permission several times I finally remembered, that Red Hat now runs runs SELinux. It allows even finer access control for files, resources and actions using policies. The current status of SELinux can be shown with sudo sestatus. For testing reasons, all policies can be set to permissive using sudo setenforce permissive. The contexts of a file can be inspected with ls -Z file1.

To debug an process that fails, the /var/log/audit/audit.log file is the first place to look at. The audit2why tool translates the error messages to a more understandable format:

>
sudo cat /var/log/audit/audit.log | grep gunicorn | audit2why

Afterwards the audit2allow utility can generate rules, that would allow the denied operations:

>
sudo cat /var/log/audit/audit.log | grep gunicorn | audit2allow -M custom_rule

These rules are now stored in the format of an type enforcement (custom_rule.te) file and a policy package which can be activated using semodule -i custom_rule.pp.

Likely this process has to be repeated, as different kinds of operations might be denied.

For our setup the final type enforcement file looked like

module custom_rule 1.0;
 
require {
        type init_t;
        type unconfined_exec_t;
        type user_home_t;
        class file { append execute execute_no_trans ioctl map open read setattr };
        class lnk_file { getattr read };
}
 
#============= init_t ==============
#!!!! This avc is allowed in the current policy
allow init_t unconfined_exec_t:file { execute open read };
allow init_t user_home_t:file setattr;
 
#!!!! This avc can be allowed using the boolean 'domain_can_mmap_files'
allow init_t user_home_t:file map;
 
#!!!! This avc is allowed in the current policy
#!!!! This av rule may have been overridden by an extended permission av rule
allow init_t user_home_t:file { append execute execute_no_trans ioctl open read };
 
#!!!! This avc is allowed in the current policy
allow init_t user_home_t:lnk_file { getattr read };

The type enforcement rule can be compiled manually

checkmodule -M -m -o custom_rule.mod custom_rule.te
semodule_package -o custom_rule.pp -m custom_rule.mod
sudo semodule -i custom_rule.pp
Further useful resources
 
Disclaimer

When used correctly, SELinux adds a lot to your server’s security. When you run into problems with certain commands being denied, you should first make sure that you truly understand what causes the error. Chances are very high that SELinux complains for a reason. Often you can avoid the problem altogether by rethinking where you put which files. When you are absolutely sure that you need to build a new policy package, do yourself a favor and research thoroughly what each added rule does – it is only too easy to create security holes which would defeat SELinux’ purpose.

Michael Trojanek: How to compile a SELinux policy package

UPDATE