Установка пользовательской политики SELinux для сервиса systemd

Вопрос или проблема

В AlmaLinux9 у меня есть небольшой сервис systemd ExectStart, который запускает процесс Java (Java Temurin 21). У меня возникают ошибки SELinux из-за того, что у сервиса нет разрешения execmem (уже проверил журналы AVC).

Инструмент audit2allow рекомендует предоставить домену unit_t разрешения execmem, это рекомендованная политика для установки:

module my-java 1.0;

require {
        type init_t;
        class process execmem;
}

#============= init_t ==============
allow init_t self:process execmem;

Я не хочу давать каждому процессу init_t привилегии execmem, так как я могу определить собственную политику SELinux / переход политики, который будет применяться только к этому конкретному сервису?

Кроме того, исполняемый файл java работает нормально, если я запускаю его напрямую, это связанные домены SELinux.

system_u:object_r:lib_t:s0 /usr/lib/jvm/temurin-21-jre/bin/java

Из вашего вопроса я делаю следующие предположения:

  • Ваш сервис работает как init_t
  • Файл юнита вашего сервиса имеет ExecStart=/usr/lib/jvm/temurin-21-jre/bin/java -jar something.jar
  • Тип /usr/lib/jvm/temurin-21-jre/bin/java – это lib_t.

В таком случае первое, что нужно исправить, это убедиться, что ваш сервис не работает как init_t. init_t должен использоваться только системой инициализации. Отдельные сервисы должны использовать типы, созданные специально для них (например, веб-сервера должны работать как httpd_t). Существует тип для пользовательских сервисов, называемый unconfined_service_t, и, как следует из названия, он не подвержен строгому ограничению.

Итак, почему ваш сервис работает как init_t? Это происходит потому, что исполняемый файл помечен как lib_t, и нет правила перехода типа, которое вызывает автоматический переход от init_t к чему-то еще, когда исполняемый файл с меткой lib_t выполняется:

# sesearch -T -s init_t -c process -t lib_t | wc -l
0

Давайте поищем правила переходов, которые позволят init_t перейти к unconfined_service_t:

$ sudo sesearch -T -s init_t -c process -D unconfined_service_t
type_transition init_t bin_t:process unconfined_service_t;
type_transition init_t usr_t:process unconfined_service_t;

Следовательно, если ваш исполняемый файл помечен как bin_t или usr_t, то ваш сервис будет запускаться как unconfined_service_t. Это логично, так как эти типы используются для файлов в /usr/bin и /usr/libexec и так далее. Поэтому следующая задача – убедиться, что исполняемый файл помечен одним из этих типов.

Здесь вступает в силу одно из самых важных руководств по управлению системой SELinux. Резюме: не помещайте вещи в странные места.

Например, httpd_t разрешено читать файлы с меткой httpd_sys_content_t, и для этого по умолчанию настроена метка файловой системы, так что эта метка применяется к файлам в /var/www. Следовательно, это плохая идея помещать файлы, предназначенные для обслуживания веб-сервером, за пределами этой директории. Это не говорит о том, что вы не можете так делать; только о том, что если вы настаиваете на этом, то вы создали дополнительную работу для себя, чтобы убедиться, что файлы получают правильные метки.

То же самое касается вашей JVM. Она находится в нетрадиционном расположении, и поэтому метки файловой системы по умолчанию для ее исполняемых файлов – lib_t (ошибка), вместо bin_t (правильно). Чтобы продемонстрировать это, сравните результаты следующих двух команд, которые проверяют заданные пути для определения их меток файлов по умолчанию. Первый путь поставляется с java-21-openjdk-headless, второй – из вашей JVM:

$ matchpathcon /usr/lib/jvm/java-21-openjdk-21.0.5.0.11-1.fc41.x86_64/bin/java
/usr/lib/jvm/java-21-openjdk-21.0.5.0.11-1.fc41.x86_64/bin/java system_u:object_r:bin_t:s0

$ matchpathcon /usr/lib/jvm/temurin-21-jre/bin/java
/usr/lib/jvm/temurin-21-jre/bin/java    system_u:object_r:lib_t:s0

Итак, если ваша JVM находится в неправильном месте, куда нам нужно ее переместить? Мы можем выяснить это, изучив список путей и их меток файлов по умолчанию:

$ sudo semanage fcontext -l | grep /usr/lib/jvm | grep bin_t
/usr/lib/jvm/java(.*/)bin(/.*)                     all files          system_u:object_r:bin_t:s0 

Таким образом, если вы согласны с перемещением вашей JVM, все, что вам нужно сделать, это поместить ее в папку, которая соответствует этому регулярному выражению, выполнить restorecon -rv на /usr/lib/jvm (что сбрасывает метки файлов на диске, чтобы они соответствовали их значениям по умолчанию) и перезапустить ваш сервис, и он теперь должен правильно перейти к unconfined_service_t. Мы можем протестировать подходящий путь файла с помощью matchpathcon:

$ matchpathcon /usr/lib/jvm/java-temurin-21-jre/bin/java
/usr/lib/jvm/java-temurin-21-jre/bin/java   system_u:object_r:bin_t:s0

Если вы не можете переместить JVM, то вы можете настроить метку файловой системы по умолчанию для исполняемых файлов в их существующем пути с помощью команды вроде semanage fcontext --add -t bin_t /usr/lib/jvm/temurin-\d+-jre/bin(/.*), за которой следует restorecon и перезапуск вашего сервиса, как описано выше.

Теперь, когда ваш сервис запускается с правильной меткой (unconfined_service_t), давайте проверим, разрешено ли ему разрешение execmem:

# sesearch -A -s unconfined_service_t -c process -p execmem
allow unconfined_service_t unconfined_service_t:process execmem; [ deny_execmem ]:False

Похоже, что это будет разрешено, само при условии, что булевый флаг deny_execmem не установлен. Этот булевый флаг описан в мануале unconfined_service_selinux(8):

Если вы хотите запретить приложениям доменов пользователя отображать область памяти как исполняемую и записываемую, это опасно, и исполняемый файл должен быть сообщен в bugzilla, вам нужно включить булевый флаг deny_execmem. Отключен по умолчанию.

setsebool -P deny_execmem 1

Вы можете проверить, включён ли он в вашей системе с помощью getsebool deny_ptrace; пока он выключен, SELinux не будет препятствовать вашему сервису, работающему как unconfined_service_t, отображать свои страницы как записываемые и исполняемые.

(Меня на самом деле удивляет, что это не отключено по умолчанию в 2024 году…)

Ответ или решение

Настройка пользовательской политики SELinux для службы systemd в AlmaLinux 9

При работе с SELinux в AlmaLinux 9 и запуске Java-приложений через службы systemd, особенно в среде, где SELinux включен, может возникнуть ряд сложностей. Одна из них заключается в получении ошибок, связанных с недостатком разрешений, таких как execmem. В этом руководстве мы подробно рассмотрим, как правильно настроить пользовательскую политику SELinux для специфической службы, не ухудшая общую безопасность системы.

Проблемный контекст

Как вы уже заметили, ваша служба вызывает ошибки SELinux из-за отсутствия разрешения execmem. Это обычно вызвано тем, что служба исполняется в контексте, который не позволяет выполнение кода с выделением памяти, что может быть критично для работы Java-процессов, стремящихся динамически загружать код.

Факты о текущем состоянии:
  • Ваша служба использует init_t в качестве контекста, что неправильно, так как init_t предназначен только для процесса инициализации системы.
  • Java-приложение запускается из расположения, которое не соответствует стандартным требованиям для контекстов SELinux.

Шаги по решению проблемы

1. Определение типа для службы

Первое, что стоит сделать — это убедиться, что ваша служба не работает под init_t. Определите правильный тип для вашей службы:

  • Возможно использование типа unconfined_service_t, который предоставляет больше свобод и не накладывает строгих ограничений.
2. Изменение меток файлов

Для того чтобы ваша служба могла правильно переходить в нужный контекст, необходимо изменить метки для исполняемого файла Java. Самый оптимальный вариант — переместить вашу Java-установку в стандартное место, например, /usr/lib/jvm, чтобы система автоматически назначала ей тип bin_t.

Если перемещение невозможно, вы можете вручную изменить метки:

sudo semanage fcontext --add -t bin_t "/usr/lib/jvm/temurin-\d+-jre/bin(/.*)"
sudo restorecon -Rv /usr/lib/jvm

После выполнения этой команды ваши Java-бинарники будут иметь нужный тип, позволяющий выполнять службы с контекстом unconfined_service_t.

3. Проверка разрешений

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

sesearch -A -s unconfined_service_t -c process -p execmem

Если всё настроено правильно, вывод должен указать, что ваши службы имеют разрешение execmem, пока флаг deny_execmem отключен.

4. Обновление SELinux Booleans

Важно проверить статус боoleans, связанных с execmem. Вы можете использовать следующую команду:

getsebool deny_execmem

Чтобы отключить deny_execmem, выполните команду:

setsebool -P deny_execmem 0

Это гарантирует, что ваша служба может корректно выделять память для выполнения кода.

Заключение

Эта последовательность шагов поможет вам настроить SELinux для работы с вашей Java-службой в AlmaLinux 9 без потери функциональности и увеличения уязвимости системы. Применение правил и меток позволяет не только избежать ошибок, но и поддерживать высокий уровень безопасности.

Как только вы реализовали предложенные рекомендации, рекомендуется проверить работу вашей службы и внимательно следить за AVC логами на предмет любых новых ошибок, чтобы удостовериться, что ваши настройки рабочие и эффективные.

Оцените материал
Добавить комментарий

Капча загружается...