SELinux errors can be bewildering and it is tempting to disable SELinux entirely. However, with a couple of simple commands, you can customize an SELinux policy for your web service.
This mini-tutorial deals strictly with policy-based errors (audit2allow, semodule). We will not go into file-based selinux context errors (semanage, restorecon) or selinux flag errors (setsebool) because their solution is well-known.
This tutorial assumes that you are running Fedora 26 on DigitalOcean. Unfortunately, you won't be able to work through this tutorial on our website because some commands are unavailable in our terminal configuration.
Install packages.
dnf -y update vim-minimal
dnf -y install git tmux vim-enhanced
pip3 install virtualenv
Setup user with sudo privileges.
adduser user
echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/user
su user
tmux # CTRL-b " to split window into top and bottom
Configure user environment.
pushd /tmp
git clone https://github.com/invisibleroads/scripts
cd scripts
bash setup
popd
virtualenv ~/.virtualenvs/crosscompute
source ~/.virtualenvs/crosscompute/bin/activate
pip install pyramid
Setup app.
X=~/Experiments/xyz; mkdir $X -p; cd $X
vim app.py
from pyramid.config import Configurator
from pyramid.response import Response
from wsgiref.simple_server import make_server
if __name__ == '__main__':
with Configurator() as config:
config.add_view(lambda request: Response('whee\n'))
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 3000, app)
server.serve_forever()
source ~/.virtualenvs/crosscompute/bin/activate
python app.py
curl http://127.0.0.1:3000
whee
Setup service.
sudo vim /etc/systemd/system/app.service
[Unit]
Description=App
[Service]
ExecStart=/home/user/.virtualenvs/crosscompute/bin/python app.py
WorkingDirectory=/home/user/Experiments/xyz
User=user
Group=user
Restart=on-abnormal
[Install]
WantedBy=multi-user.target
sudo systemctl start app
sudo systemctl status app
curl http://127.0.0.1:3000
whee
Activate selinux.
sudo setenforce 1
sudo getenforce
Enforcing
Check that the app service fails.
sudo systemctl restart app
sudo systemctl status app
app.service: Failed with result 'exit-code'.
Deactivate selinux.
sudo setenforce 0
sudo getenforce
Permissive
Check that the app service works.
sudo systemctl restart app
sudo systemctl status app
curl http://127.0.0.1:3000
whee
Install relevant packages.
sudo dnf provides audit2allow
sudo dnf install -y policycoreutils-python-utils
Review the proposed policy rules. Running audit2allow -a
will automatically generate SELinux policy rules based on error messages logged in /var/log/audit/audit.log*
.
sudo audit2allow -a
It is a good idea to review the generated SELinux policy rules because some error messages in audit.log
might have been the result of unsafe activity on your machine. Many of the SELinux restrictions are in place for a reason and you do not want to disable them unnecessarily.
Once you are satisfied with your custom SELinux policy, it is time to install it.
Install our custom SELinux policy.
X=/tmp/selinux; mkdir $X -p; cd $X
MODULE_NAME=xyz
sudo audit2allow -a -m $MODULE_NAME > $MODULE_NAME.te
sudo checkmodule -M -m -o $MODULE_NAME.mod $MODULE_NAME.te
sudo semodule_package -o $MODULE_NAME.pp $MODULE_NAME.mod
sudo semodule -i $MODULE_NAME.pp
Suppose you have a pre-generated SELinux policy named xyz.te
. We can automatically compile, build and install our policy with Ansible.
Suppose we have the following file structure.
roles/server-configuration/files/xyz.te
roles/server-configuration/tasks/main.yml
roles/server-configuration/tasks/selinux.yml
Then you can automatically install xyz.te
with this task in main.yml
:
---
- include: selinux.yml
with_items:
- xyz
loop_control:
loop_var: module_name
become: true
tags: selinux
Here are the contents of selinux.yml
:
---
- name: make temporary folder
tempfile: state=directory suffix=selinux
register: x_register
- name: upload selinux modules
copy:
dest: '{{ x_register.path }}/{{ module_name }}.te'
src: '{{ module_name }}.te'
- name: compile selinux modules
command: checkmodule -M -m -o {{ module_name }}.mod {{ module_name }}.te
args:
chdir: '{{ x_register.path }}'
changed_when: False
- name: build selinux modules
command: semodule_package -o {{ module_name }}.pp -m {{ module_name }}.mod
args:
chdir: '{{ x_register.path }}'
changed_when: False
- name: install selinux modules
command: semodule -i {{ module_name }}.pp
args:
chdir: '{{ x_register.path }}'
changed_when: False