Document general purpose techniques that are useful when deploying services in Linux.
Record tutorial on how to create an SELinux policy for a web service.
Roy Hyunjin Han
SELinux errors can be bewildering and it is tempting to disable SELinux entirely. However, this tutorial will introduce various commands that have simplified the process of working with SELinux.
+ Create an SELinux policy
+ Install the SELinux policy via Bash
+ Install the SELinux policy via Ansible
20170910-1630 - 20170910-1645: 15 minutes
+ Create a sample app
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()
20170913-1130 - 20170913-1145: 15 minutes
+ Create system unit file for app
[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
We have two choices for system unit files:
Option 1: Create /etc/systemd/system/YOUR-APP.service
_ Option 2: Create ~/.config/systemd/user/YOUR-APP.service
ls /etc/systemd/system/*.service
sudo cp app.service /etc/systemd/system
20170915-0530 - 20170915-0600: 30 minutes
After struggling for a bit trying to get systemd working in docker, I realized that additional configuration is required. However, since the additional configuration will significantly increase the configuration complexity, memory footprint and startup time, we won't add systemd to our standard session and instead simply record the video in a separate droplet.
Install packages.
dnf -y update vim-minimal
dnf -y install git tmux vim-enhanced
pip3 install virtualenv
Setup user.
adduser user
echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/user
su user
tmux # CTRL-b " to split window into top and bottom
pushd /tmp
git clone https://github.com/invisibleroads/scripts
cd scripts
bash setup
popd
Setup environment.
virtualenv ~/.virtualenvs/crosscompute
source ~/.virtualenvs/crosscompute/bin/activate
pip install pyramid
Setup app.
mkdir ~/Experiments/xyz -p
cd ~/Experiments/xyz
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
Record tasks.
+ Prepare app service
+ Check that the app service works
20170915-0730 - 20170915-0800: 30 minutes
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
Record tasks.
+ Make selinux enforcing
+ Have the script fail as a systemd unit file
+ Make selinux permissive
+ Have the script work as a systemd unit file
+ Set up example situation on crosscompute notebook machine
+ List selinux commands
getenforce (libselinux-utils)
setenforce (libselinux-utils)
audit2allow (policycoreutils-python-utils)
checkmodule (checkpolicy)
semodule_package (policycoreutils-python-utils)
semodule (policycoreutils)
+ Set selinux flags
setsebool (policycoreutils)
+ Set selinux context
semanage (policycoreutils-python-utils)
restorecon (policycoreutils)
Show SELinux policy that will let our service work under SELinux.
sudo dnf provides audit2allow
sudo dnf install -y \
checkpolicy \
libselinux-utils \
policycoreutils \
policycoreutils-python-utils
sudo audit2allow -a
Install our custom SELinux policy.
cd /tmp
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
Show how to use ansible to automatically configure a custom SELinux policy.
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
main.yml
- include: selinux.yml
with_items:
- xyz
loop_control:
loop_var: module_name
become: true
tags: selinux
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
I tried to run audit2allow
in the docker container, but it doesn't seem to be working.
_ Build simple tool that generates policy given audit log
audit_log_text_path = 'audit.log'
target_folder = '/tmp'
import subprocess
subprocess.check_output(['sudo', 'dnf', '-y', 'install', 'policycoreutils-python-utils'])
policy_text = subprocess.check_output(['audit2allow', '-i', audit_log_text_path])
policy_text
from os.path import join
policy_path = join(target_folder, 'policy.te')
with open(policy_path, 'wt') as policy_file:
policy_file.write(policy_text)
print('policy_text_path = ' + policy_path)
20170915-0930 - 20170915-1000: 30 minutes
+ Draft tutorial
Mention situation (works fine, but systemd service fails) (1 minute)
Mention getenforce and setenforce and how to detect if the problem is selinux (1 minute)
Mention /var/log/audit, audit2allow (1 minute)
Mention how to compile and install custom selinux policy (1 minute)
End with ansible script for selinux (1 minute)
Record video on audit2allow as suggested by Salah