Upgraded tasks for SELinux.

pull/1/head
Vladimir Protsenko 2 years ago
parent 1a36dc9824
commit 54c7ed0750

@ -1,6 +1,6 @@
# Задания # Задания
### 0. Инициализация SELinux ## 0. Инициализация SELinux
Источник: https://wiki.debian.org/SELinux/Setup Источник: https://wiki.debian.org/SELinux/Setup
@ -10,9 +10,9 @@ $ sudo apt install selinux-basics selinux-policy-default auditd
$ sudo selinux-activate $ sudo selinux-activate
$ reboot $ reboot
``` ```
Вы установили утилиты для работы с selinux, политики по-умолчанию, созданные для популярных пакетов и демон для аудита всех действий пользователя. Активировали selinux, собрав модули политик по-умолчанию и добавив параметры запуска с selinux в grub. Вы установили утилиты для работы с selinux, политики по-умолчанию, созданные для популярных пакетов и демон для логирования критичных с точки зрения безопасности системных событий. Активировали selinux, собрав модули политик по-умолчанию и добавив параметры запуска с selinux в grub.
После перезагрузки проверьте, что selinux активирован. К каждому файлу и процессу SELinux прикрепляет поля: пользователь, роль, домен(тип), чувствительность(степень секретности). Изменение файловой системы может занять какое-то время. После перезагрузки проверьте, что selinux активирован. Все процессы и файлы помечены контекстом SELinux. Контекст содержит информацию, необходимую SELinux для принятия решений по управлению доступом. Контекст это поля: пользователь, роль, домен(тип), чувствительность (степень секретности). Современная реализация SELinux хранит контекст безопасности в атрибутах `xattrs`. Для установки этих атрибутов в файловой системе может потребоваться какое-то время.
``` ```
$ sudo check-selinux-installation $ sudo check-selinux-installation
$ getenforce $ getenforce
@ -25,40 +25,904 @@ $ getenforce
$ sestatus -v $ sestatus -v
``` ```
### 1. ## 1. Документация SELinux
Скачайте проект с примером политик SELinux c помощью `apt`: Скомпилируйте в системе документацию из политик по-умолчанию selinux.
``` ```
$ sudo apt install selinux-policy-src $ sudo sepolicy manpage -a -p /usr/local/man/man8
$ sudo mandb
```
или установитье её с помощью apt:
``` ```
Определите местоположение файла `selinux-policy-src.tar.gz` с помощью `dpkg` и распакуйте себе в домашнюю директорию пользователя. Изучите README и INSTALL. $ sudo apt install selinux-policy-doc
```
Проверьте, что открывается документация, например для `httpd_selinux`.
### 2. ## 2. Базовые команды и определения
Установите в систему документацию для политик selinux. Потренируемся далее с базовыми командами.
### 2.1 Изучим установленные контексты безопасности
Для начала давайте проверим контекст файла в домашнем каталоге пользователя `stud`:
``` ```
$ sudo sepolicy manpage -a -p /usr/local/man/man8 $ cd
$ sudo mandb $ ls -Z
``` ```
Проверьте, что открывается документация для `httpd_selinux`. Обратите внимание на строки с `ngnix`. Флаг `-Z` присутствует в широком спектре распространенных инструментов CLI, включая `ls` и `ps`. При указании этого флага в дополнении к стандартным разрешениям UNIX, информации о пользователе и группе отображается контекст SELinux файла.
Мы видим атрибуты:
- пользователь SELinux (`unconfined_u`),
- роль (`object_r`),
- тип (`user_home_t`),
- уровень секретности (`s` 0).
### 3. Пользователь SELinux отличается от пользователя UNIX и существует исключительно для того, чтобы связать пользователя UNIX с набором ролей. Это позволяет пользователям UNIX быть ограниченными политикой SELinux.
Изучите ряд команд selinux.
Выведите булевые переменные для модуля `httpd_` командами `getsebool -a` и `semanage boolean -l`. По принятому соглашению, глобальные переключатели правил начинаются с `allow_` и `use_`, переключатели модулей начинаются с их названия. В нашем случае пользователь `unconfined_u` означает, что пользователь сопоставлен с логином SELinux по умолчанию. Это означает, что пользователю разрешено запускать любое приложение, разрешенное стандартными разрешениями файловой системы. Однако, если приложение может перейти в другой домен, к нему будут применятся ограничения, задаваемые SELinux.
Выполните поиск правил на которые повлияет переключатель `httpd_enable_ftp_server`. Список пользователей SELinux в системы можно посмотреть командами:
``` ```
sesearch b httpd_enable_ftp_server A $ seinfo -u
$ semanage login -l
``` ```
Попробуйте также команды: ### 2.2 Переход в другой домен
- `semanage fcontext -l` (информация о контекстах),
- `ls -Z` (SELinux настройки для файлов директории),
- `ps Zaux` (SELinux настройки для работающих процессов),
- `seinfo -t` (Вывести список контекстов).
Чтобы продемонстрировать разницу между процессами c ограничениями и без, давайте запустим приложение, которого переход в другой домен не определён:
```
# yes >/dev/null &
# ps -Z | grep yes
```
Процесс `yes` помечен доменом `unconfined_t`, что указывает на то, что он по-прежнему обладает полными привилегиями `root` и может делать всё, что пожелает.
Мы увидим другую картину для процесса `auditd`:
```
# ps -eZ | grep audit
```
Обратите внимание, что третье поле - `auditd_t`, означающее, что процесс `auditd` был ограничен доменом `auditd_t`.
### 4. Остановим запущенный фоновые процессы `yes` и двинемся дальше.
Хостинговые компании часто организуют для своих пользователей доступ к одной из папок в домашней директории пользователя по http по ссылке `http://site/~user/index.html`. Попробуем это повторить с политиками selinux, уменьшающими потенциальный ущерб для системы от взлома сервера. ```
# 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;
allow initrc_t acct_exec_t:file execute; # Разрешение на запуск файла типа acct_exec_t в исходном домене initrc_t
allow acct_t acct_exec_t:file entrypoint; # Разрешение на наличие точки входа файла типа acct_exec_t в домен целевой домен acct_t
allow initrc_t acct_t:process transition; # Разрешение процессу на переход в домен целевой acct_t
```
Подробнее в документации правила 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 к тому, что определено в базе данных контекста системы.
Чтобы попробовать, давайте намеренно неправильно установим контекст в примере SELinux AVC log:
```
# 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`, которой вы можете временного изменить контекст файла.
Установка контекст на `httpd_sys_content_t` также устранила бы проблему, о которой сообщалось в нашем тестовом AVC.
## 3. Установка тестового сервиса
Подготовим сервис, для которого далее будем писать собственную политику безопасности SELinux.
Скачайте текущий репозиторий и перейдите в папку `linux_tasks/modeul2/test_service`.
Настройте окружение разработчика, скачайте необходимые заголовочные файлы и установите сервис `testapp` с помощью `make`:
```
$ sudo apt install build-essential libcurl4-openssl-dev
$ make
$ 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 policy
$ cd 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` это предоставленный Red Hat скрипт, который компилирует и загружает модуль политики SELinux.
### 4.2. Скомпилируйте и загрузите политику
Теперь скомпилируйте и загрузите политику SELinux, запустив `testapp.sh` сценарий. Скрипт необходимо запускать с правами суперпользователя, поскольку он изменяет конфигурацию запущенного SELinux:
```bash
$ sudo ./testapp.sh
```
Первые 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. Создайте папку www в домашней директории. Добавьте пользователя в группу `www-data` и установите права на директорию `~/www` 0755.
@ -78,3 +942,12 @@ setsebool P httpd_enable_homedirs on
``` ```
Проверьте, что доступ появился. Проверьте, что доступ появился.
# Релевантные ссылки
- 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.

@ -0,0 +1,9 @@
all: testapp.c
g++ -std=c++11 -g -o testapp testapp.c -lcurl
clean:
rm testapp
install: testapp
install -m 0755 testapp /usr/local/sbin
install -m 0644 testapp.service /usr/lib/systemd/system/

@ -0,0 +1,200 @@
#include <stdio.h> //printf(3)
#include <stdlib.h> //exit(3)
#include <unistd.h> //fork(3), chdir(3), sysconf(3)
#include <signal.h> //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h> //syslog(3), openlog(3), closelog(3)
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#include <string>
#include <curl/curl.h>
#define MAXHOSTNAME 256
#define DESTURL "http://wttr.in/"
#define DESTURLEND "?format=3"
using namespace std;
const char* offices[] = {
"Brisbane",
"Canberra",
"Melbourne",
"Perth",
"Sydney",
"Beijing",
"Shanghai",
"Shenzhen",
"Hong Kong",
"Bangalore",
"New%20Delhi",
"Kolkata",
"Mumbai",
"Pune",
"Jakarta",
"Tokyo",
"Nagoya",
"Osaka",
"Fukuoka",
"Seoul",
"Kuala%20Lumpur",
"Auckland",
"Wellington",
"Makati%20City",
"Singapore",
"Taipei",
"Bangkok",
"London",
"Amsterdam",
"Barcelona",
"Berlin",
"Brno",
"Brussels",
"Copenhagen",
"Berlin",
"Cork",
"Dublin",
"Düsseldorf",
"Farnborough",
"Frankfurt%20am%20Main",
"Helsinki",
"Madrid",
"Milan",
"Moscow",
"Grasbrunn",
"Neuchâtel",
"Newcastle%20Upon%20Tyne",
"Oslo",
"Prague",
"Puteaux",
"Rome",
"Stockholm",
"Stuttgart",
"Cherkasy",
"Vienna",
"Warsaw",
"Waterford",
"Zürich",
"Dubai",
"İstanbul",
"Raanana",
"Riyadh",
"Sandton",
"Buenos%20Aires",
"Brasília",
"Fortaleza",
"Rio%20de%20Janeiro",
"São%20Paulo",
"Santiago",
"Bogota",
"Ciudad%20de%20México",
"Monterrey",
"Lima",
"Montreal",
"Toronto",
"Raleigh",
"Boston",
"Ann%20Arbor",
"Atlanta",
"Washington",
"North Charleston",
"Charlotte",
"Chicago",
"San%20Francisco",
"New%20York",
"Dallas",
"Denver",
"Durham",
"Huntsville",
"Los%20Angeles",
"Mahwah",
"Minneapolis",
"Richmond",
"St.%20Louis",
"Sunnyvale",
"McLean",
"Westford",
NULL };
int curlUrl( const char* myOffice, size_t myUrlLength )
{
std::string fullUrl;
CURL *curl;
CURLcode res;
// create a URL from 2 constants and an office string
fullUrl.append( DESTURL );
fullUrl.append( myOffice );
fullUrl.append( DESTURLEND );
// make a C string of the same size and copy to it
int fullUrlSize = fullUrl.length();
char charFullArray[ fullUrlSize + 1 ];
strcpy( charFullArray, fullUrl.c_str() );
// setup curl, grab the combined URL, and clean up
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, charFullArray);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return res;
}
int main()
{
int ttl=999999;
int delay=10;
pid_t pid = fork();
if (pid == 0)
{
// child process
openlog("testapp",LOG_PID,LOG_DAEMON);
// iterate over the list of Red Hat offices and get the weather for each
for ( int i=0; offices[i] != NULL; i++ )
{
curlUrl( ( "http://wttr.in?%sformat=3", offices[i]), sizeof( ( "http://wttr.in?%sformat=3", offices[i]) ) );
sleep(delay);
}
syslog(LOG_NOTICE,"testapp child process ended");
closelog();
return(EXIT_SUCCESS);
}
if (pid > 0)
{
// parent process
string filename = "/var/run/testapp.pid";
ofstream o(filename.c_str());
o << pid << endl;
ifstream meminfo;
meminfo.open("/proc/meminfo");
meminfo.close();
openlog("testapp",LOG_PID,LOG_DAEMON);
/* disabling this due to startup timeout
do
{
syslog(LOG_NOTICE,"daemon ttl %d",ttl);
sleep(delay);
ttl-=delay;
} while ( ttl > 0 );
syslog(LOG_NOTICE,"daemon ttl expired");*/
syslog(LOG_NOTICE,"testapp parent process ended");
closelog();
return(EXIT_SUCCESS);
}
}

@ -0,0 +1,10 @@
[Unit]
Description=Testing SELinux app
[Service]
Type=forking
ExecStart=/usr/local/sbin/testapp
ExecStop=/usr/bin/killall testapp
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save