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
- A sysadmin’s guide to SELinux: 42 answers to the big questions
- Red Hat documentation on SELinux
- Similar fix for ansible
- Related problems with socket permissions (stackoverflow)
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