Стандартная модель UNIX представляет из себя форму "избирательного контроля доступа" (DAC - Discretionary Access Control), поскольку позволяет владельцам контролируемых объектов устанавливать права на них. Пользователь сам может разрешить другим просмотр содержимого домашнего каталога или написать программу с флагом `setuid`, которая позволит другим пользователям отправлять сигналы его процессам.
Системы мандатного управления доступом (MAC, Mandatory Access Control) позволяют администраторам писать политики контроля доступа, которые переопределяют или дополняют избирательные разрешения традиционной модели. Например, вы можете установить правило, согласно которому домашние каталоги доступны только их владельцам. Это может помочь сохранить конфиденциальные документы, даже если пользователь небрежно относится к его разрешениям. Хотя такие системы разрабатывались для реализации многоуровневой безопасности в государственных организациях с различными грифами секретности, на практике чаще всего вы сталкнётесь с её применением для защиты фоновых служб. Хорошо реализованные политики могут помешать компрометации системы с уязвимым программным обеспечением.
SELinux - одна из старейших реализаций системы MAC для Linux, разработанная в Агентством национальной безопасности США. Система реализует почти все возможности MAC и RBAC (Role Based Access Control). Из-за этого разработка политик является сложной задачей. К счастью, многие общие политики доступны онлайн и имеют разумные значениям по умолчанию. Для упрощения разработки политик могут использоваться специализированные редакторы, например https://seedit.sourceforge.net/.
Вы установили утилиты для работы с selinux, политики по-умолчанию, созданные для популярных пакетов и демон для логирования критичных с точки зрения безопасности системных событий. Активировали selinux, собрав модули политик по-умолчанию и добавив параметры запуска с selinux в grub.
После перезагрузки проверьте, что selinux активирован. Все процессы и файлы помечены контекстом SELinux. Контекст содержит информацию, необходимую SELinux для принятия решений по управлению доступом. Контекст это поля: пользователь, роль, домен (тип), чувствительность (степень секретности). Современная реализация SELinux хранит контекст безопасности в атрибутах `xattrs`. Для установки этих атрибутов в файловой системе может потребоваться какое-то время.
Действия пользователей (процессов) с объектами (файлами) проверяются в начале Linux DAC, а затем SELinux модулями. В режиме Permissive все нарушения политик SELinux будут только логироваться.
Установите режим `Enforcing` в конфигурационном файле `/etc/selinux/config` и перезагрузите систему.
Флаг `-Z` присутствует в широком спектре распространенных инструментов CLI, включая `ls` и `ps`. При указании этого флага в дополнении к стандартным разрешениям UNIX, информации о пользователе и группе отображается контекст SELinux файла.
Пользователь SELinux отличается от пользователя UNIX и существует исключительно для того, чтобы связать пользователя UNIX с набором ролей. Это позволяет пользователям UNIX быть ограниченными политикой SELinux.
В нашем случае пользователь `unconfined_u` означает, что пользователь сопоставлен с пользователем SELinux по умолчанию. Это означает, что пользователю разрешено запускать любое приложение, разрешенное стандартными разрешениями файловой системы. Однако, если приложение может перейти в другой домен, к нему будут применятся ограничения, задаваемые SELinux.
- Вначале выводится идентификатор пользователя 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` и может делать всё, что пожелает.
Следующее правило гласит, что когда процесс в домене `initrc_t` запускает файл типа `acct_exec_t`, домен процесса должен быть изменен на `acct_t`, если это разрешено политикой. Чтобы это правило перехода домена работало, нужно также присутствие нескольких разрешений.
Подробнее в документации правила https://selinuxproject.org/page/TypeRules.
Обычно в политиках используют макрос `domtrans_pattern` или `domain_transition_pattern` определённый в `/usr/share/selinux/devel/include/support/misc_patterns.spt` для указания правила перехода и разрешений вместе.
Если и есть, что посоветовать запомнить новому пользователю SELinux, так это `restorecon`. Restorecon приведет контекст SELinux к тому, который определен в базе данных контекста системы.
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 и может выполнять любые действия, которые оно захочет:
## 4. Инициализация политики для тестового сервиса
Теперь сгенерируем автоматически структуру политики SELinux. Научимся обрабатывать сообщения AVC, которые генерируются SELinux, когда приложение нарушает политику.
-`testapp.te` - это базовая политика для приложения. Он устанавливает правила для домена `testapp_t`,
-`testapp.if` - это файл интерфейса. Интерфейсы подобны общедоступным функциям в том смысле, что они предоставляют другим модулям SELinux способы взаимодействия с тем, который вы пишете,
-`testapp.fc` - это файл контекстов. Он содержит информацию о маркировке всех объектов файловой системы, на которые ссылается политика,
Теперь скомпилируйте и загрузите политику SELinux, запустив `testapp.sh` сценарий. Скрипт необходимо запускать с правами суперпользователя, поскольку он изменяет конфигурацию запущенного SELinux:
Первые 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. Проверьте политику в действии
Если мы перезагрузим приложение, к нему будет прикреплен недавно скомпилированный и загруженный модуль политики:
Это связано с правилами перехода домена 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
```
Будет (и должно быть!) довольно много отказов, возвращенных из поиска. Вот некоторые из примеров машины:
Если мы внимательно посмотрим на них, то увидим, что процессу тестового приложения отказано в открытии в `/proc/meminfo` и в чтении в `/proc` в целом. Итак, нам нужно найти интерфейс, который позволит осуществлять эти доступы, и добавить его в наш файл `testapp.te`.
Определения интерфейса SELinux находятся в `/usr/share/selinux/devel/include`, если вы установили пакет `selinux-policy-devel`. Если мы ищем `/proc`, в файлах с именем `*.if` (определения интерфейса):
$ 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`, например:
Вы можете получить более одного из этих сообщений, в зависимости от времени.
### 7.1.2 Интерпретация сообщений AVC
В приведенном выше сообщении вы можете видеть, что система попыталась подключиться к удаленному хосту через порт 80. Это то, что мы хотим допустить. Итак, давайте поищем интерфейс, который позволял бы осуществлять такого рода подключение. Поскольку сетевое взаимодействие контролируется ядром, мы будем искать в каталоге ядра файлы интерфейса, связанные с сетевым взаимодействием:
```
$ ls /usr/share/selinux/devel/include/kernel/*.if | grep net
Поскольку это не немаркированная сеть, давайте посмотрим в `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)
```
И скомпилируйте свою политику, и перезапустите службу:
Мы временно перейдём от интерфейсов к настройкам разрешения/запрета в файле `testapp.te`. У нас есть несколько отказов AVC в системных вызовах сокетов TCP и UDP, которые блокирует SELinux.
AVC решения, о которых идет речь, находятся здесь (вероятно, на вашем хостинге их будет больше, но все они относятся к одной категории).:
You can use audit2allow to generate a loadable module to allow this access.
```
Вы можете видеть, что в данном случае `audit2why` не принес существенной пользы, кроме сообщения нам о том, что нам нужно добавить новую инструкцию `allow` в наш файл `testapp.te`.
### 8.3 Используйте audit2allow, чтобы предложить изменения
Итак, теперь мы воспользуемся советом, который дал нам `audit2why`, и позволим `audit2allow` сгенерировать некоторые заявления о разрешении политики. Вы всегда должны быть осторожны с`audit2allow`, поскольку он будет генерировать правила для всего, что вы ему передаете, и не обязательно выберет наиболее оптимальное решение.
Из имеющихся у нас 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`, используя строку, подобную этой:
На данный момент нам осталось обработать всего несколько отказов AVC. Надеюсь, вы разбираетесь в том, как найти подходящие интерфейсы и разрешить правила для SELinux.
В этом заключительном упражнении мы завершим тестовое приложение SELinux policy.
## 10. Последний интерфейс
### 10.1 Проверьте наличие отказов AVC
Давайте снова перезапустим наше приложение, чтобы получить обновленный список отказов.
```
$ sudo systemctl restart testapp
```
У нас осталось всего несколько отказов, и вы увидите, что все они (вероятно!) ссылаются на `/etc/resolv.conf` или на `/etc/hosts`.
Из имеющихся у нас 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
```
Как только мы это сделаем, окончательная версия вашей политики должна выглядеть следующим образом:
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, уменьшающими потенциальный ущерб для системы от взлома сервера.