# Задания

Стандартная модель UNIX представляет из себя форму "избирательного контроля доступа" (DAC - Discretionary Access Control), поскольку позволяет владельцам контролируемых объектов устанавливать права на них. Пользователь сам может разрешить другим просмотр содержимого домашнего каталога или написать программу с флагом `setuid`, которая позволит другим пользователям отправлять сигналы его процессам.

Системы мандатного управления доступом (MAC, Mandatory Access Control) позволяют администраторам писать политики контроля доступа, которые переопределяют или дополняют избирательные разрешения традиционной модели. Например, вы можете установить правило, согласно которому домашние каталоги доступны только их владельцам. Это может помочь сохранить конфиденциальные документы, даже если пользователь небрежно относится к его разрешениям. Хотя такие системы разрабатывались для реализации многоуровневой безопасности в государственных организациях с различными грифами секретности, на практике чаще всего вы сталкнётесь с её применением для защиты фоновых служб. Хорошо реализованные политики могут помешать компрометации системы с уязвимым программным обеспечением.

SELinux - одна из старейших реализаций системы MAC для Linux, разработанная в Агентством национальной безопасности США. Система реализует почти все возможности MAC и RBAC (Role Based Access Control). Из-за этого разработка политик является сложной задачей. К счастью, многие общие политики доступны онлайн и имеют разумные значениям по умолчанию. Для упрощения разработки политик могут использоваться специализированные редакторы, например https://seedit.sourceforge.net/.

## 0. Инициализация SELinux

Источник: https://wiki.debian.org/SELinux/Setup

Запустите следующие команды, чтобы инициализировать SELinux:
```
$ sudo apt install selinux-basics selinux-policy-default auditd
$ sudo selinux-activate
$ reboot
```
Вы установили утилиты для работы с selinux, политики по-умолчанию, созданные для популярных пакетов и демон для логирования критичных с точки зрения безопасности системных событий. Активировали selinux, собрав модули политик по-умолчанию и добавив параметры запуска с selinux в grub.

После перезагрузки проверьте, что selinux активирован. Все процессы и файлы помечены контекстом SELinux. Контекст содержит информацию, необходимую SELinux для принятия решений по управлению доступом. Контекст это поля: пользователь, роль, домен (тип), чувствительность (степень секретности). Современная реализация SELinux хранит контекст безопасности в атрибутах `xattrs`. Для установки этих атрибутов в файловой системе может потребоваться какое-то время.
```
$ sudo check-selinux-installation
$ getenforce
```

Действия пользователей (процессов) с объектами (файлами) проверяются в начале Linux DAC, а затем SELinux модулями. В режиме Permissive все нарушения политик SELinux будут только логироваться. 

Установите режим `Enforcing` в конфигурационном файле `/etc/selinux/config` и перезагрузите систему.
```
$ sestatus -v
```

## 1. Документация SELinux
Скомпилируйте в системе документацию из политик по-умолчанию selinux.
```
$ sudo sepolicy manpage -a -p /usr/local/man/man8
$ sudo mandb
```
или установитье её с помощью apt:
```
$ sudo apt install selinux-policy-doc
```
Проверьте, что открывается документация, например для `httpd_selinux`. 


## 2. Базовые команды и определения
Далее потренируемся с базовыми командами.

### 2.1 Изучим установленные контексты безопасности

Для начала давайте проверим контекст файла в домашнем каталоге пользователя `stud`:
```
$ cd 
$ touch file
$ ls -Z
```
Флаг `-Z` присутствует в широком спектре распространенных инструментов CLI, включая `ls` и `ps`. При указании этого флага в дополнении к стандартным разрешениям UNIX, информации о пользователе и группе отображается контекст SELinux файла. 

Мы видим атрибуты:
- пользователь SELinux (`unconfined_u`), 
- роль (`object_r`), 
- тип (`user_home_t`),
- уровень секретности (`s` 0).

Пользователь SELinux отличается от пользователя UNIX и существует исключительно для того, чтобы связать пользователя UNIX с набором ролей. Это позволяет пользователям UNIX быть ограниченными политикой SELinux.

В нашем случае пользователь `unconfined_u` означает, что пользователь сопоставлен с пользователем SELinux по умолчанию. Это означает, что пользователю разрешено запускать любое приложение, разрешенное стандартными разрешениями файловой системы. Однако, если приложение может перейти в другой домен, к нему будут применятся ограничения, задаваемые SELinux.

Список пользователей SELinux в системы можно посмотреть командами:
```
$ seinfo -u
```
Cписок сопоставлений между учетными записями пользователей SELinux и Linux 
```
# semanage login -l
```
### 2.2 Переход в другой домен

Чтобы продемонстрировать разницу между процессами c ограничениями и без, давайте запустим приложение, которого переход в другой домен не определён:
```
# yes >/dev/null &
# ps -Z | grep yes
```

Дадим расшифровку вывода selinux метки процесса.
```
<user>:<role>:<type>:<secrecy_level>:optional
```
- Вначале выводится идентификатор пользователя SELinux `unconfined_u`. 
- Частью SELinux является модель безопасности управления доступом на основе ролей (RBAC). Пользователи SELinux авторизованы для ролей, а роли авторизованы для доменов. Роль служит посредником между доменами и пользователями SELinux. Роли, которые можно ввести, определяют, в какие домены можно войти; в конечном итоге это определяет, к каким типам объектов можно получить доступ. Модель ролей помогает лучше отразить требования к безопасности и тем самым снизить уязвимость к атакам. В данном случае выводится роль `unconfined_r`. 
- Метка типа `unconfined_t` является атрибутом Type Enforcement. Тип определяет домен для процессов и тип для файлов. Правила политики SELinux определяют, как типы могут получать доступ друг к другу, будь то домен, обращающийся к типу, или домен, обращающийся к другому домену. Доступ разрешен только в том случае, если существует определенное правило политики SELinux, которое разрешает это.
- Уровень (секретности) является атрибутом MLS (Multi-Level Security, модель Bell-La Padula) и MCS (Multi-Category Security). Диапазон MLS представляет собой пару уровней, записываемую как `lowlevel-highlevel`, если уровни различаются. Если уровни идентичны `s0-s0` может записываться одним значением `s0`.
- В опциональном поле отображаются категории, которые не являются обязательными. Если есть категории, уровень записывается как `уровень: набор категорий`. Если категорий нет, пишется как `уровень`. Если набор категорий представляет собой непрерывную серию, его можно сократить. Например, `c0.c3`— это то же самое, что `c0,c1,c2,c3`. Файл `/etc/selinux/default/setrans.conf` отображает уровни (`s0:c0`) в удобочитаемую форму (то есть `CompanyConfidential`).

Итак, процесс `yes` помечен доменом `unconfined_t`, что указывает на то, что он по-прежнему обладает полными привилегиями `root` и может делать всё, что пожелает.

Мы увидим другую картину для процесса `auditd`:
```
# ps -eZ | grep audit
```
Обратите внимание, что третье поле - `auditd_t`, означающее, что процесс `auditd` был ограничен доменом `auditd_t`.

Остановим запущенный фоновый процесс `yes` и двинемся дальше.
```
# jobs
# kill %1 
# jobs 
```

Изменение контекста при запуске приложения в наборе правил SELinux называется переход домена (domain transition). Пример записи правила:
```
type_transition <текущий_домен> <тип_программы>: <class> <целевой_домен>
```
Следующее правило гласит, что когда процесс в домене `initrc_t` запускает файл типа `acct_exec_t`, домен процесса должен быть изменен на `acct_t`, если это разрешено политикой. Чтобы это правило перехода домена работало, нужно также присутствие нескольких разрешений.
```
type_transition initrc_t acct_exec_t:process acct_t;

# Разрешение на запуск файла типа acct_exec_t в исходном домене initrc_t
allow initrc_t acct_exec_t:file execute;   

# Разрешение процессу на переход в домен целевой acct_t 
allow initrc_t acct_t:process transition;  

# Разрешение на наличие точки входа файла типа acct_exec_t в домен целевой домен acct_t
allow acct_t acct_exec_t:file entrypoint;  
```
Подробнее в документации правила https://selinuxproject.org/page/TypeRules. 

Обычно в политиках используют макрос `domtrans_pattern` или `domain_transition_pattern` определённый в `/usr/share/selinux/devel/include/support/misc_patterns.spt` для указания правила перехода и разрешений вместе. 

### 2.3 Полезная утилита - restorecon
Если и есть, что посоветовать запомнить новому пользователю SELinux, так это `restorecon`. Restorecon приведет контекст SELinux к тому, который определен в базе данных контекста системы.

Чтобы попробовать, давайте намеренно неправильно установим контекст:
```
# touch testaudit
# ls -Z ./testaudit
system_u:object_r:admin_home_t:s0 ./testaudit
# chcon -t httpd_sys_content_t ./testaudit
# ls -Z ./testaudit
system_u:object_r:httpd_sys_content_t:s0 ./testaudit
```

И мы можем волшебным образом изменить его обратно с помощью `restorecon`:
```
# restorecon -v ./testaudit
Relabeled /root/testaudit from system_u:object_r:httpd_sys_content_t:s0 to system_u:object_r:admin_home_t:s0
# ls -Z ./testaudit
-rw-------. root root system_u:object_r:admin_home_t:s0 ./testaudit
```
Выше также применяется `chcon`, которой вы можете временного изменить контекст файла.


## 3. Установка тестового сервиса 
Подготовим сервис, для которого далее будем писать собственную политику безопасности SELinux. 

Скачайте текущий репозиторий и перейдите в папку `linux_tasks/module2/02_mac_selinux/test_service`.

Настройте окружение разработчика, скачайте необходимые заголовочные файлы и установите сервис `testapp` с помощью `make`:
```
$ sudo apt install build-essential libcurl4-openssl-dev
$ make
$ cat Makefile
$ sudo make install
```

Взгляните на приложение и связанную с ним службу, которые вы только что установили:
```
$ ls -l /usr/local/sbin/testapp
-rwxr-xr-x. 1 root root 48688 Feb 20 18:03 /usr/local/sbin/testapp
$ sudo systemctl status testapp
● testapp.service - Testing SELinux app
   Loaded: loaded (/usr/lib/systemd/system/testapp.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
```

```
$ sudo systemctl enable testapp
$ sudo systemctl start testapp
```

Мы видим, что приложение было запущено `systemd`:
```
$ sudo systemctl status testapp
● testapp.service - Testing SELinux app
     Loaded: loaded (/lib/systemd/system/testapp.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-10-18 10:27:58 EDT; 1s ago
    Process: 5791 ExecStart=/usr/local/sbin/testapp (code=exited, status=0/SUCCESS)
   Main PID: 5792 (testapp)
      Tasks: 1 (limit: 9496)
     Memory: 3.3M
        CPU: 14ms
     CGroup: /system.slice/testapp.service
             └─5792 /usr/local/sbin/testapp

Oct 18 10:27:58 uefidebian systemd[1]: Starting Testing SELinux app...
Oct 18 10:27:58 uefidebian testapp[5791]: testapp parent process ended
Oct 18 10:27:58 uefidebian systemd[1]: Started Testing SELinux app.
```
И мы также можем видеть, что приложение работает без ограничений, что означает, что оно работает без политики SELinux и может выполнять любые действия, которые оно захочет:
```
# ps -efZ | grep testapp | grep -v grep
system_u:system_r:testapp_t:s0  root        5792       1  0 10:27 ?        00:00:00 /usr/local/sbin/testapp
```

## 4. Инициализация политики для тестового сервиса
Теперь сгенерируем автоматически структуру политики SELinux. Научимся обрабатывать сообщения AVC, которые генерируются SELinux, когда приложение нарушает политику.

### 4.1 Сгенерируйте шаблон политики SELinux
Создайте каталог-проект политики и сгенерируйте в нём шаблонные файлы политики с помощью команды `sepolicy generate`.
```
$ mkdir testapp_policy
$ cd testapp_policy
$ sepolicy generate --init /usr/local/sbin/testapp
```

Обратите внимание на последние несколько строк в выходных данных `sepolicy generate`:
```
Created the following files:
/home/stud/src/policy/testapp.te # Type Enforcement file
/home/stud/src/policy/testapp.if # Interface file
/home/stud/src/policy/testapp.fc # File Contexts file
/home/stud/src/policy/testapp_selinux.spec # Spec file
/home/stud/src/policy/testapp.sh # Setup Script
```

Исходные компоненты политики SELinux:
- `testapp.te` - это базовая политика для приложения. Он устанавливает правила для домена `testapp_t`,
- `testapp.if` - это файл интерфейса. Интерфейсы подобны общедоступным функциям в том смысле, что они предоставляют другим модулям SELinux способы взаимодействия с тем, который вы пишете,
- `testapp.fc` - это файл контекстов. Он содержит информацию о маркировке всех объектов файловой системы, на которые ссылается политика,
- `testapp.sh` это скрипт, который компилирует и загружает модуль политики SELinux.

### 4.2. Скомпилируйте и загрузите политику

Теперь скомпилируйте и загрузите политику SELinux, запустив `testapp.sh` сценарий. Скрипт необходимо запускать с правами суперпользователя, поскольку он изменяет конфигурацию запущенного SELinux:
```bash 
$ sudo ./testapp.sh
```

Примечание. Замените в файле `*.te`слово `pid` на `runtime` в макросах `files_*`.

Первые 9 строк выходных данных показывают компиляцию политики, загрузку политики в память и автоматическое создание справочной страницы для политики:
```bash
Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile testapp.pp
Compiling targeted testapp module
/usr/bin/checkmodule:  loading policy configuration from tmp/testapp.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/testapp.mod
Creating targeted testapp.pp policy package
rm tmp/testapp.mod tmp/testapp.mod.fc
+ /usr/sbin/semodule -i testapp.pp
+ sepolicy manpage -p . -d testapp_t
./testapp_selinux.8
```

В дополнение к компиляции и загрузке политики, `testapp.sh` скрипт создает RPM, содержащий политику, используя файл спецификации, сгенерированный `sepolicy generate`. Это упрощает распространение политики. 

Замечание. Игнорируйте в этом задании ошибку построения пакета rpm.

### 4.3. Проверьте политику в действии
Если мы перезагрузим приложение, к нему будет прикреплен недавно скомпилированный и загруженный модуль политики:
```
$ sudo systemctl stop testapp
$ sudo systemctl start testapp
$ ps -efZ | grep testapp | grep -v grep
system_u:system_r:testapp_t:s0  root      8737     1  0 20:51 ?        00:00:00 /usr/local/sbin/testapp
```

### 4.4. Как приложение в конечном итоге получает testapp_context?

Это связано с правилами перехода домена SELinux. Служба `testapp` запускается `systemd`, которая запускается с контекстом `init_t`, поскольку это наша служба инициализации. Из-за правил перехода он изменяет контексты при запуске службы.

Чтобы просмотреть эти правила, введите:
```
$ sesearch -T -s init_t -t testapp_exec_t
type_transition init_t testapp_exec_t : process testapp_t;
```

Правило гласит, что когда любой процесс, помеченный как `init_t`, выполняет любой ,исполняемый файл, помеченный как `testapp_exec_t`, вновь созданный процесс будет помечен как `testapp_t`.

На данный момент у нас есть общая политика для приложения `testapp`, которая настроена на разрешающий режим. Таким образом, приложение может запускаться, и SELinux будет генерировать предупреждения при нарушении существующей системной политики, но не будет предпринимать никаких действий.

## 5. Выявление проблем с SELinux политикой по умолчанию 

### 5.1 Проверьте наличие отказов AVC
Теперь, когда наше приложение запущено, мы можем проверить системные журналы на наличие сообщений об отказе AVC (access vector cache, где SELinux кэширует решения о предоставлении или отказе в доступе).
```
$ sudo ausearch -m AVC -ts today
```

Будет (и должно быть!) довольно много отказов, возвращенных из поиска. Вот некоторые из примеров машины:
```
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.393:375): avc:  denied  { read } for  pid=5792 comm="testapp" laddr=192.168.1.3 lport=53423 faddr=1.1.1.1 fport=53 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.393:376): avc:  denied  { create } for  pid=5792 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.393:377): avc:  denied  { setopt } for  pid=5792 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.393:378): avc:  denied  { connect } for  pid=5792 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.433:379): avc:  denied  { getopt } for  pid=5792 comm="testapp" laddr=192.168.1.3 lport=40030 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.433:380): avc:  denied  { getattr } for  pid=5792 comm="testapp" laddr=192.168.1.3 lport=40030 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.433:381): avc:  denied  { write } for  pid=5792 comm="testapp" laddr=192.168.1.3 lport=40030 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:27:59 2022
type=AVC msg=audit(1666103279.473:382): avc:  denied  { read } for  pid=5792 comm="testapp" laddr=192.168.1.3 lport=40030 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
----
time->Tue Oct 18 10:44:03 2022
type=AVC msg=audit(1666104243.178:383): avc:  denied  { getattr } for  pid=5792 comm="testapp" path="/etc/resolv.conf" dev="sda2" ino=6855 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
----
time->Tue Oct 18 10:44:03 2022
type=AVC msg=audit(1666104243.178:384): avc:  denied  { open } for  pid=5792 comm="testapp" path="/etc/hosts" dev="sda2" ino=9026 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
type=AVC msg=audit(1666104243.178:384): avc:  denied  { read } for  pid=5792 comm="testapp" name="hosts" dev="sda2" ino=9026 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
```

## 6. Создание правил политики для доступа к /proc

### 6.1 Интерпретация сообщений AVC
В первом сообщении аудита мы видим эти сообщения AVC:
```
type=AVC msg=audit(1552588580.764:25971): avc:  denied  { open } for  pid=1022 comm="testapp" path="/proc/meminfo" dev="proc" ino=4026532040 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:proc_t:s0 tclass=file permissive=1
type=AVC msg=audit(1552588580.764:25971): avc:  denied  { read } for  pid=1022 comm="testapp" name="meminfo" dev="proc" ino=4026532040 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:proc_t:s0 tclass=file permissive=1
```

Если мы внимательно посмотрим на них, то увидим, что процессу тестового приложения отказано в открытии в `/proc/meminfo` и в чтении в `/proc` в целом. Итак, нам нужно найти интерфейс, который позволит осуществлять эти доступы, и добавить его в наш файл `testapp.te`.

### 6.2 Найдите правильный интерфейс
Определения интерфейса SELinux находятся в `/usr/share/selinux/devel/include`, если вы установили пакет `selinux-policy-devel`. Если мы ищем `/proc`, в файлах с именем `*.if` (определения интерфейса):
```
$ cd /usr/share/selinux/devel/include
$ find . -type f -name "*.if" -exec grep -H '/proc' {} \; | grep "system state information"
```
Мы видим, что в результатах есть строка, которая гласит:
```
./kernel/kernel.if:##	Allows caller to read system state information in /proc.
```
Это имеет смысл, так что давайте посмотрим, в каком интерфейсе есть этот комментарий. В самом файле:
```bash
########################################
## <summary>
##      Allows caller to read system state information in /proc.
## </summary>
## <desc>
##      <p>
##      Allow the specified domain to read general system
##      state information from the proc filesystem (/proc).
##      </p>
##      <p>
##      Generally it should be safe to allow this access.  Some
##      example files that can be read based on this interface:
##      </p>
##      <ul>
##              <li>/proc/cpuinfo</li>
##              <li>/proc/meminfo</li>
##              <li>/proc/uptime</li>
##      </ul>
##      <p>
##      This does not allow access to sysctl entries (/proc/sys/*)
##      nor process state information (/proc/pid).
##      </p>
## </desc>
## <param name="domain">
##      <summary>
##      Domain allowed access.
##      </summary>
## </param>
## <infoflow type="read" weight="10"/>
## <rolecap/>
#
interface(`kernel_read_system_state',`
        gen_require(`
                attribute kernel_system_state_reader;
        ')

        typeattribute $1 kernel_system_state_reader;
')
```

Этот интерфейс принимает один параметр (смотрите в разделе `<param...>`), который является именем домена SELinux, к которому должен быть разрешен доступ. В этом случае доменное имя - `testapp_t`, соответствующее имени вашего приложения и модуля политики.

### 6.3 Добавьте интерфейс к политике
Чтобы разрешить это, добавьте интерфейс в `testapp.te` со строкой в разделе локальной политики `testapp`, например:
```
kernel_read_system_state(testapp_t)
```

Теперь файл должен выглядеть следующим образом:
```
$ cat ~/src/policy/testapp.te
policy_module(testapp, 1.0.0)


#
# Declarations
#

type testapp_t;
type testapp_exec_t;
init_daemon_domain(testapp_t, testapp_exec_t)

permissive testapp_t;

type testapp_var_run_t;
files_pid_file(testapp_var_run_t)


#
# testapp local policy
#
allow testapp_t self:process { fork };
allow testapp_t self:fifo_file rw_fifo_file_perms;
allow testapp_t self:unix_stream_socket create_stream_socket_perms;

manage_dirs_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_lnk_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
files_pid_filetrans(testapp_t, testapp_var_run_t, { dir file lnk_file })

domain_use_interactive_fds(testapp_t)

files_read_etc_files(testapp_t)

kernel_read_system_state(testapp_t)

logging_send_syslog_msg(testapp_t)

miscfiles_read_localization(testapp_t)
```

Хранение данных в алфавитном порядке не имеет никакого значения для SELinux, но облегчает чтение файла.

### 6.4 Перекомпилируйте и перезагрузите политику
Теперь давайте перекомпилируем политику и перезагрузим ее в память.
```
$ sudo ./testapp.sh
```

### 6.5 Перезапустите приложение
Чтобы посмотреть, устранило ли это проблему, давайте перезапустим приложение:
```
$ sudo systemctl restart testapp
```
...и посмотрите, есть ли какие-либо сообщения AVC о `/proc`:
```
$ sudo ausearch -m AVC -ts recent | grep meminfo | wc -l
0
```
Ура! Одна AVC проблема решена.

Далее наша цель - разрешить тестовому приложению генерировать сетевой трафик. 

## 7.1 Разрешение подключения к HTTP-порту
### 7.1.1 Проверьте наличие отказов AVC
Давайте перезапустим наше приложение, чтобы получить обновленный список отказов:
```
$ sudo systemctl restart testapp
```
Начнем с одного из отказов, которые будут присутствовать в результатах поиска аудита, связанного с подключением по TCP-порту 80:
```
$ sudo ausearch -m AVC -ts recent | grep 'dest=80'
type=AVC msg=audit(1553181380.518:2594): avc:  denied  { name_connect } for  pid=31221 comm="testapp" dest=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=1
```

Вы можете получить более одного из этих сообщений, в зависимости от времени.

### 7.1.2 Интерпретация сообщений AVC
В приведенном выше сообщении вы можете видеть, что система попыталась подключиться к удаленному хосту через порт 80. Это то, что мы хотим допустить. Итак, давайте поищем интерфейс, который позволял бы осуществлять такого рода подключение. Поскольку сетевое взаимодействие контролируется ядром, мы будем искать в каталоге ядра файлы интерфейса, связанные с сетевым взаимодействием:
```
$ ls /usr/share/selinux/devel/include/kernel/*.if | grep net
/usr/share/selinux/devel/include/kernel/corenetwork.if
/usr/share/selinux/devel/include/kernel/unlabelednet.if
```

Поскольку это не немаркированная сеть, давайте посмотрим в `corenetwork.if`. Поскольку это действие `name_connect`, давайте посмотрим на `connect` и `http`. Используя эти критерии, мы находим:
```
########################################
## <summary>
##      Make a TCP connection to the http port.
## </summary>
## <param name="domain">
##      <summary>
##      Domain allowed access.
##      </summary>
## </param>
#
interface(`corenet_tcp_connect_http_port',`
        gen_require(`
                type http_port_t;
        ')

        allow $1 http_port_t:tcp_socket name_connect;
')
```

#### 7.1.3 Добавьте интерфейс
Итак, как и раньше, добавьте интерфейс в свой файл `testapp.te`, используя строку, подобную этой:
```
corenet_tcp_connect_http_port(testapp_t)
```

И скомпилируйте свою политику, и перезапустите службу:
```
$ sudo ./testapp.sh
$ sudo systemctl restart testapp
```

...и последнее, проверьте, есть ли еще отказ AVC:
```
$ sudo systemctl restart testapp
$ sudo ausearch -m AVC -ts recent | grep 'dest=80' | wc -l
0
```

Да! Исправлен еще один отказ AVC.


## 8.2. Разрешения для сокетов

### 8.1 Воспользуемся коротким путём!
Мы временно перейдём от интерфейсов к настройкам разрешения/запрета в файле `testapp.te`. У нас есть несколько отказов AVC в системных вызовах сокетов TCP и UDP, которые блокирует SELinux.

AVC решения, о которых идет речь, находятся здесь (вероятно, на вашем хостинге их будет больше, но все они относятся к одной категории).:
```
$ sudo ausearch -m AVC -ts recent | egrep 'tcp|udp'
type=AVC msg=audit(1553190979.189:2931): avc:  denied  { create } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.189:2932): avc:  denied  { connect } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.379:2933): avc:  denied  { getopt } for  pid=2042 comm="testapp" laddr=10.0.2.5 lport=60848 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553190979.379:2934): avc:  denied  { getattr } for  pid=2042 comm="testapp" laddr=10.0.2.5 lport=60848 faddr=5.9.243.187 fport=80 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1553191083.531:2965): avc:  denied  { create } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
type=AVC msg=audit(1553191083.531:2966): avc:  denied  { connect } for  pid=2042 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
type=AVC msg=audit(1553191083.548:2967): avc:  denied  { getattr } for  pid=2042 comm="testapp" path="socket:[314409]" dev="sockfs" ino=314409 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1
```

### 8.2 Позвольте audit2why интерпретировать для вас
Далее, давайте попросим систему преобразовать AVC в английский для нас:
```
$ sudo ausearch -m AVC | egrep 'udp' | head -1 | audit2why
type=AVC msg=audit(1553112150.906:909): avc:  denied  { create } for  pid=9772 comm="testapp" scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:system_r:testapp_t:s0 tclass=udp_socket permissive=1

	Was caused by:
		Missing type enforcement (TE) allow rule.

		You can use audit2allow to generate a loadable module to allow this access.
```

Вы можете видеть, что в данном случае `audit2why` не принес существенной пользы, кроме сообщения нам о том, что нам нужно добавить новую инструкцию `allow` в наш файл `testapp.te`.

### 8.3 Используйте audit2allow, чтобы предложить изменения
Итак, теперь мы воспользуемся советом, который дал нам `audit2why`, и позволим `audit2allow` сгенерировать некоторые заявления о разрешении политики. Вы всегда должны быть осторожны с `audit2allow`, поскольку он будет генерировать правила для всего, что вы ему передаете, и не обязательно выберет наиболее оптимальное решение.
```
$ sudo ausearch -m AVC -ts recent | egrep 'tcp|udp' | audit2allow
#============= testapp_t ==============
allow testapp_t self:tcp_socket { connect create getattr getopt setopt };
allow testapp_t self:udp_socket { connect create getattr setopt };
```

Эти правила выглядят хорошо, так что продолжайте и добавьте их в свой тестовый файл `app.te`.

Теперь файл должен выглядеть следующим образом:
```
$ cat ~/src/policy/testapp.te
policy_module(testapp, 1.0.0)


#
# Declarations
#

type testapp_t;
type testapp_exec_t;
init_daemon_domain(testapp_t, testapp_exec_t)

permissive testapp_t;

type testapp_var_run_t;
files_pid_file(testapp_var_run_t)


#
# testapp local policy
#
allow testapp_t self:process { fork };
allow testapp_t self:fifo_file rw_fifo_file_perms;
allow testapp_t self:tcp_socket { connect create getattr getopt };
allow testapp_t self:udp_socket { connect create getattr };
allow testapp_t self:unix_stream_socket create_stream_socket_perms;

manage_dirs_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_lnk_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
files_pid_filetrans(testapp_t, testapp_var_run_t, { dir file lnk_file })

corenet_tcp_connect_http_port(testapp_t)

domain_use_interactive_fds(testapp_t)

files_read_etc_files(testapp_t)

kernel_read_system_state(testapp_t)

logging_send_syslog_msg(testapp_t)

miscfiles_read_localization(testapp_t)
```

Хранение данных в алфавитном порядке не имеет никакого значения для SELinux, но облегчает чтение файла.

Примечание: Пожалуйста, обратите внимание, что исходные разрешающие правила заканчиваются точкой с запятой, но для правил интерфейса нет ограничителя.

### 8.4 Перекомпилируйте и перезагрузите политику

Теперь давайте перекомпилируем политику и перезагрузим ее в память.
```
$ sudo ./testapp.sh
```

Чтобы посмотреть, устранило ли это проблему, давайте перезапустим приложение:
```
$ sudo systemctl restart testapp
```

...и посмотрите, есть ли какие-либо сообщения AVC о TCP или UDP:
```
$ sudo ausearch -m AVC -ts recent | egrep 'tcp|udp' | wc -l
0
```

Фантастика!

Нам осталось обработать всего несколько отказов AVC.

## 9. Создание правил политики для чтения сертификатов

Разрешим политике testapp SELinux считывать сертификаты в системе.

### 9.1 Проверьте наличие отказов AVC
Давайте снова перезапустим наше приложение, чтобы получить обновленный список отказов.
```
$ sudo systemctl restart testapp
```

Мы сосредоточимся на отказах SELinux, связанных с чтением файла конфигурации openssl.
```
$ sudo ausearch -m AVC -ts recent | egrep 'cert_t'
type=AVC msg=audit(1585733765.866:2797): avc:  denied  { search } for  pid=26898 comm="testapp" name="pki" dev="xvda2" ino=12583606 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=dir permissive=1
type=AVC msg=audit(1585733765.866:2797): avc:  denied  { read } for  pid=26898 comm="testapp" name="openssl.cnf" dev="xvda2" ino=320001 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=file permissive=1
type=AVC msg=audit(1585733765.866:2797): avc:  denied  { open } for  pid=26898 comm="testapp" path="/etc/pki/tls/openssl.cnf" dev="xvda2" ino=320001 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=file permissive=1
type=AVC msg=audit(1585733765.867:2798): avc:  denied  { getattr } for  pid=26898 comm="testapp" path="/etc/pki/tls/openssl.cnf" dev="xvda2" ino=320001 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:cert_t:s0 tclass=file permissive=1
```

### 9.2 Интерпретация сообщений AVC
Из имеющихся у нас AVCS кажется очевидным, что приложение хочет попытаться разрешить сертификаты доступа и файлы, связанные с SSL. Большинству приложений, получающих доступ к сети, необходимо иметь возможность читать эти файлы, и интерфейс для этого находится в `/usr/share/selinux/devel/include/system/miscfiles.if` и называется `miscfiles_read_generic_certs`.
```
########################################
## <summary>
##      Read generic SSL certificates.
## </summary>
## <param name="domain">
##      <summary>
##      Domain allowed access.
##      </summary>
## </param>
## <rolecap/>
#
interface(`miscfiles_read_generic_certs',`
        gen_require(`
                type cert_t;
        ')

        allow $1 cert_t:dir list_dir_perms;
        read_files_pattern($1, cert_t, cert_t)
        read_lnk_files_pattern($1, cert_t, cert_t)
')
```

### 9.3 Добавьте интерфейс
Итак, как и раньше, добавьте интерфейс в свой файл `testapp.te`, используя строку, подобную этой:
```
miscfiles_read_generic_certs(testapp_t)
```

Теперь файл должен выглядеть следующим образом:
```
$ cat ~/src/policy/testapp.te
policy_module(testapp, 1.0.0)


#
# Declarations
#

type testapp_t;
type testapp_exec_t;
init_daemon_domain(testapp_t, testapp_exec_t)

permissive testapp_t;

type testapp_var_run_t;
files_pid_file(testapp_var_run_t)


#
# testapp local policy
#
allow testapp_t self:process { fork };
allow testapp_t self:fifo_file rw_fifo_file_perms;
allow testapp_t self:tcp_socket { connect create getattr getopt };
allow testapp_t self:udp_socket { connect create getattr };
allow testapp_t self:unix_stream_socket create_stream_socket_perms;

manage_dirs_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_lnk_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
files_pid_filetrans(testapp_t, testapp_var_run_t, { dir file lnk_file })

corenet_tcp_connect_http_port(testapp_t)

domain_use_interactive_fds(testapp_t)

files_read_etc_files(testapp_t)

kernel_read_system_state(testapp_t)

logging_send_syslog_msg(testapp_t)

miscfiles_read_localization(testapp_t)

miscfiles_read_generic_certs(testapp_t)
```

И скомпилируйте свою политику, и перезапустите службу:
```
$ sudo ./testapp.sh
$ sudo systemctl restart testapp
```

...и последнее, проверьте, есть ли еще какие-либо отказы AVC:
```
$ sudo ausearch -m AVC -ts recent | grep cert_t | wc -l
0
```
На данный момент нам осталось обработать всего несколько отказов AVC. Надеюсь, вы разбираетесь в том, как найти подходящие интерфейсы и разрешить правила для SELinux.

В этом заключительном упражнении мы завершим тестовое приложение SELinux policy.

## 10. Последний интерфейс

### 10.1 Проверьте наличие отказов AVC

Давайте снова перезапустим наше приложение, чтобы получить обновленный список отказов.
```
$ sudo systemctl restart testapp
```

У нас осталось всего несколько отказов, и вы увидите, что все они (вероятно!) ссылаются на `/etc/resolv.conf` или на `/etc/hosts`.
```
$ sudo ausearch -m AVC -ts recent | egrep '^type=AVC'
type=AVC msg=audit(1553195947.854:3270): avc:  denied  { getattr } for  pid=4651 comm="testapp" path="/etc/resolv.conf" dev="dm-0" ino=9311517 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
type=AVC msg=audit(1553195947.854:3271): avc:  denied  { open } for  pid=4651 comm="testapp" path="/etc/hosts" dev="dm-0" ino=8389746 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
type=AVC msg=audit(1553195947.854:3271): avc:  denied  { read } for  pid=4651 comm="testapp" name="hosts" dev="dm-0" ino=8389746 scontext=system_u:system_r:testapp_t:s0 tcontext=system_u:object_r:net_conf_t:s0 tclass=file permissive=1
```

### 10.2 Интерпретация AVC сообщений

Из имеющихся у нас AVCS кажется очевидным, что приложение хочет попытаться преобразовать некоторые имена хостов в IP-адреса. Большинству приложений, получающих доступ к сети, необходимо иметь возможность считывать конфигурацию системной сети, и интерфейс для этого находится в `/usr/share/selinux/devel/include/system/sysnetwork.if` и называется `sysnet_read_config`.
```
#######################################
## <summary>
##      Read network config files.
## </summary>
## <desc>
##      <p>
##      Allow the specified domain to read the
##      general network configuration files.  A
##      common example of this is the
##      /etc/resolv.conf file, which has domain
##      name system (DNS) server IP addresses.
##      Typically, most networking processes will
##      require the access provided by this interface.
##      </p>
##      <p>
##      Higher-level interfaces which involve
##      networking will generally call this interface,
##      for example:
##      </p>
##      <ul>
##              <li>sysnet_dns_name_resolve()</li>
##              <li>sysnet_use_ldap()</li>
##              <li>sysnet_use_portmap()</li>
##      </ul>
## </desc>
## <param name="domain">
##      <summary>
##      Domain allowed access.
##      </summary>
## </param>
#
interface(`sysnet_read_config',`
        gen_require(`
                type net_conf_t;
        ')

        files_search_etc($1)
        allow $1 net_conf_t:file read_file_perms;

        ifdef(`distro_debian',`
                files_search_pids($1)
                allow $1 net_conf_t:dir list_dir_perms;
                read_files_pattern($1, net_conf_t, net_conf_t)
        ')

        ifdef(`distro_redhat',`
        files_search_all_pids($1)
        init_search_pid_dirs($1)
                allow $1 net_conf_t:dir list_dir_perms;
                allow $1 net_conf_t:lnk_file read_lnk_file_perms;
                read_files_pattern($1, net_conf_t, net_conf_t)
        ')
')
```

#### 10.3 Добавьте интерфейс
Итак, как и раньше, добавьте интерфейс в свой файл `testapp.te`, используя строку, подобную этой:
```
sysnet_read_config(testapp_t)
```

И скомпилируйте свою политику, и перезапустите службу:
```
$ sudo ./testapp.sh
$ sudo systemctl restart testapp
```

...и последнее, проверьте, есть ли еще какие-либо отказы AVC:
```
$ sudo systemctl restart testapp
$ sudo ausearch -m AVC -ts recent | wc -l
0
```

Разве это не здорово - быть почти готовым?

## 11. Установите Принудительный режим

### 11.1 Change the Domain to Enforcing

Последний шаг, который нам нужно предпринять, - это изменить ваш файл `testapp.te`, чтобы домен применял принудительные меры. Все, что нам нужно сделать, чтобы достичь этого, - это закомментировать строку, в которой говорится:
```
permissive testapp_t;
```

Также вывод семантики команды `permissive -l` говорит, что она разрешающая:
```
$ sudo semanage permissive -l

Builtin Permissive Types
testapp_t

Customized Permissive Types
```

Как только мы это сделаем, окончательная версия вашей политики должна выглядеть следующим образом:
```
$ cat testapp.te
policy_module(testapp, 1.0.0)


#
# Declarations
#

type testapp_t;
type testapp_exec_t;
init_daemon_domain(testapp_t, testapp_exec_t)

# permissive testapp_t;

type testapp_var_run_t;
files_pid_file(testapp_var_run_t)


#
# testapp local policy
#
allow testapp_t self:process { fork };
allow testapp_t self:fifo_file rw_fifo_file_perms;
allow testapp_t self:tcp_socket { connect create getattr getopt };
allow testapp_t self:udp_socket { connect create getattr };
allow testapp_t self:unix_stream_socket create_stream_socket_perms;

manage_dirs_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
manage_lnk_files_pattern(testapp_t, testapp_var_run_t, testapp_var_run_t)
files_pid_filetrans(testapp_t, testapp_var_run_t, { dir file lnk_file })

corenet_tcp_connect_http_port(testapp_t)

domain_use_interactive_fds(testapp_t)

files_read_etc_files(testapp_t)

kernel_read_system_state(testapp_t)

logging_send_syslog_msg(testapp_t)

miscfiles_read_localization(testapp_t)

miscfiles_read_generic_certs(testapp_t)

sysnet_read_config(testapp_t)
```

### 11.2 Перекомпилируйте и перезагрузите политику

Теперь давайте перекомпилируем политику и перезагрузим ее в память.
```
$ sudo ./testapp.sh
```
Чтобы подтвердить, что политика testapp_t находится в принудительном режиме, пожалуйста, повторите:
```
$ sudo semanage permissive -l
```
Как вы можете видеть, выходных данных нет, что означает отсутствие политики в разрешающем режиме.

### 11.3 Перезапустите приложение

Чтобы посмотреть, устранило ли это проблему, давайте перезапустим приложение:
```
$ sudo systemctl restart testapp
```

...и посмотрите, остались ли какие-нибудь сообщения AVC:
```
$ sudo ausearch -m AVC -ts recent | wc -l
0
```

Ура! Мы завершили конфигурацию политики для нашего сервиса.


### 12.
Упражнение на закуску. Хостинговые компании часто организуют для своих пользователей доступ к одной из папок в домашней директории пользователя по http по ссылке `http://site/~user/index.html`. Попробуем это повторить с политиками selinux, уменьшающими потенциальный ущерб для системы от взлома сервера. 

Создайте папку www в домашней директории. Добавьте пользователя в группу `www-data` и установите права на директорию `~/www` 0755. 

Установите nginx, если он ещё не установлен. Сконфигурируйте http веб-север со следующими настройками:
```
location ~ ^/~(.+?)(/.*)?$ {
  alias /home/$1/www$2;
  autoindex on;
}
```

Обратитесь к сайту командой `curl`. При правильно работающей политике, доступ должен быть запрещён.

Включите доступ к домашним директориям из контекста `httpd`
```
setsebool –P httpd_enable_homedirs on
```

Проверьте, что доступ появился.


# Релевантные ссылки
- https://security.stackexchange.com/questions/63518/mac-vs-dac-vs-rbac,
- https://wiki.debian.org/SELinux/Setup
- https://github.com/SELinuxProject/refpolicy/wiki/GettingStarted,
- https://debian-handbook.info/browse/en-US/stable/sect.selinux.html,
- https://selinuxproject.org,
- https://seedit.sourceforge.net/,
- https://selinux-ide.sourceforge.net/download.php.