Advanced Linux Tips




Pay Notebook Creator: Roy Hyunjin Han0
Set Container: Numerical CPU with TINY Memory for 10 Minutes 0
Total0

Vision

Document general purpose techniques that are useful when deploying services in Linux.

Mission

Record tutorial on how to create an SELinux policy for a web service.

Owner

Roy Hyunjin Han

Context

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.

Timeframe

20170910-1530 - 20170910-1730: 2 hours estimated

20170910-1530 -

Objectives

+ Create an SELinux policy
+ Install the SELinux policy via Bash
+ Install the SELinux policy via Ansible

Log

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
In [6]:
ls /etc/systemd/system/*.service
/etc/systemd/system/console-getty.service
/etc/systemd/system/syslog.service
/etc/systemd/system/systemd-logind.service
/etc/systemd/system/systemd-remount-fs.service
In [7]:
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

Tasks

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