You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

50 KiB

Задания

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 
$ 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
$ semanage login -l

2.2 Переход в другой домен

Чтобы продемонстрировать разницу между процессами c ограничениями и без, давайте запустим приложение, которого переход в другой домен не определён:

# yes >/dev/null &
# ps -Z | grep yes

Процесс 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;
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:

$ sudo ./testapp.sh

Первые 9 строк выходных данных показывают компиляцию политики, загрузку политики в память и автоматическое создание справочной страницы для политики:

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.

Это имеет смысл, так что давайте посмотрим, в каком интерфейсе есть этот комментарий. В самом файле:

########################################
## <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

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

Релевантные ссылки