Запретить выполнение приложения любым локальным пользователем, кроме systemd.

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

Мне нужно запустить бинарный файл, которому я не полностью доверяю.

Я создал юнит systemd для него, где он защищен настолько, насколько это возможно, и он работает отлично.

Но он все еще находится в моей системе (Fedora), и я могу случайно его выполнить. Как я могу этого избежать?

Ему все равно нужно иметь режим 700 или что-то в этом роде, чтобы его можно было выполнить через systemd, что делает его исполняемым для root, и я снова хочу этого избежать.

Сейчас я могу создать три связанных юнита systemd, где первый делает chmod 700, второй фактически выполняет его, а третий делает chmod 644, но это слишком комплексно. Думаю, можно сделать что-то гораздо проще.

Я не уверен, что systemd это одобрит, но один возможный способ решения этой проблемы — это:

chmod 644 /path/to/binary

А затем выполнять его так:

/lib64/ld-linux-x86-64.so.2 /path/to/binary

Это не самый элегантный способ, но он может сработать.

Однако с этим есть большая проблема. Любые возможности, назначенные приложению через setcap, будут автоматически потеряны, и для моего приложения как минимум одна cap_net_bind_service необходима (оно прослушивает порт ниже 1024). Эту проблему тоже можно обойти, используя iptables/nftables/xinetd и т.д., но вся конфигурация становится довольно неустойчивой.

Первый вариант: сделать исполняемым только для запуска, затем отменить

Вы уже используете обертку в своем собственном ответе — а именно ld-linux.so — для вызова вашей программы. Давайте продолжим эту идею!

Ваш файл сервиса systemd может использовать ExecStartPre= для chmod u+x, а ExecStartPost= для chmod u-x сразу после запуска сервиса (примечание: не только после остановки, что делает это гораздо более устойчивым к, например, сбоям питания).

Другой вариант, который мне нравится: монтировать только на время жизни сервиса

Поместите этот файл (исполняемый, как он есть, включая все его возможности setcap, хранящиеся в xattrs) в его собственный маленький образ файловой системы. Чтобы запустить исполняемый файл, сделайте так, чтобы файл .service зависел от того, чтобы эта файловая система была смонтирована в таком месте, где никто не будет искать для выполнения файлов. (Если что-то злонамеренное специально ищет повсюду для выполнения файлов, оно, вероятно, все равно будет использовать трюк с ld-linux.so.)

  • Я бы рекомендовал mksquashfs dubiousexecutable /var/lib/dubiousapplication/fs-image.squash
  • Создайте юнит .mount systemd, например, run-dubiousapplication.mount, с What=/var/lib/dubiousapplication/fs-image.squash и Where=/run/dubiousapplication (примечание: имя .mount файла должно соответствовать Where, при этом / заменяется на -, а ведущий / опускается), и поместите его туда, где systemd ищет такие файлы (наверное, /etc/systemd/system/). Возможно, потребуется Options=suid (не помню, уважаются ли возможности, установленные на файлах, без этого).
  • Создайте файл .service systemd для вашего сервиса. Он может работать как системный сервис с User=, указывающим пользователя, от имени которого он работает, или как пользовательский сервис из вашей пользовательской сессии. Он должен содержать RequiresMountsFor=/run/dubiousapplication. Таким образом, образ файловой системы будет смонтирован только на время жизни этого сервиса.

«Обычный» вариант: контейнеры

Если вам нужна изоляция, вам обычно хочется запустить сервис в контейнере, чтобы он видел только те части вашей файловой системы, которые вы явно укажете. Ура, Linux namespaces!

Podman делает интеграцию контейнеризованных сервисов в systemd особенно простой, так что я выбрал бы этот путь.

Вам нужно будет написать Dockerfile (есть и другие способы, но зачем?). Я обычно основываю минимальные сервисы просто на debian, т.е. мои Dockerfiles начинаются с FROM debian:12. Вы можете выбрать любую дистрибуцию, которая выпускает образы docker (то есть все основные).

Затем вы используете директиву COPY <source file> <target path within>, чтобы поместить свой исполняемый файл внутрь, например, COPY dubiousexecutable /usr/bin/.

Вы EXPOSE <portnumber>[/udp|/tcp] внутренние порты.

Вы устанавливаете то, что запускается, когда контейнер запускается, на ENTRYPOINT /usr/bin/dubiousexecutable. Таким образом, в итоге ваш Dockerfile будет состоять из четырех строк (если вам не нужно сначала установить больше компонентов, что вы можете сделать, например, с помощью RUN apt-get update; apt-get install foobar; apt-get clean)

Затем вы podman build -t dubiouscontainerimage . соберете образ контейнера.

Затем вы podman create -p <externalport>:<internalport> --name dubiouscontainer dubiouscontainerimage создадите контейнер с именем dubiouscontainer из образа dubiouscontainerimage. (вы можете сделать это столько раз, сколько захотите, с разными портами, например).

Теперь вы можете podman start dubiouscontainer запустить вручную – ИЛИ вы можете использовать podman generate-systemd dubiouscontainer для генерации файлов сервиса systemd, которые затем можете использовать с systemd.

Ограничьте приложение с помощью пользовательского модуля политики SELinux.

Учитывая, что вы на Fedora, которая работает с включенным SELinux, очевидный ответ будет заключаться в том, чтобы дать программе собственную доменную политику SELinux, в которую только initrc_t может переходить.

Это можно выразить чем-то вроде этого в вашем файле myapp.te:

allow initrc_t myapp_t:process { transition };
type_transition initrc_t myapp_exec_t:process myapp_t;

Затем убедитесь, что вы добавили правила allow myapp_t something, перечисляющие точно тот тип доступа, который приложению необходимо иметь, но не больше. Наконец, присвойте бинарному файлу метку myapp_exec_t с помощью chcon и все готово.

Если вы никогда не создавали модуль SELinux раньше, Краткое руководство по написанию пользовательской политики SELinux должно помочь вам начать.

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

Ограничение выполнения неподтвержденного приложения только для systemd на Fedora

При работе с ненадежными бинарными файлами на системах Linux, таких как Fedora, важно обеспечить их безопасность от случайного или злонамеренного выполнения. Существует несколько подходов, которые могут помочь с этой задачей. Рассмотрим основные стратегии, которые позволят запустить бинарный файл только из контекста systemd, избегая прямого доступа к нему для других пользователей, включая root.

1. Использование системных единиц systemd для настройки прав доступа

Вы можете создать систему единиц (systemd unit), которая будет запускать ваш бинарный файл, но не позволит его запускать напрямую вне этого контекста. Для этого можно использовать комбинацию команд chmod.

Пример конфигурации unit файла

Создайте unit файл в /etc/systemd/system/myapp.service:

[Unit]
Description=Запуск неподтвержденного приложения

[Service]
Type=simple
ExecStartPre=/bin/chmod 700 /path/to/binary
ExecStart=/path/to/binary
ExecStartPost=/bin/chmod 644 /path/to/binary
User=nobody  # Укажите пользователя, от имени которого будет запускаться сервис
PrivateTmp=true
ProtectSystem=full
ProtectHome=yes

[Install]
WantedBy=multi-user.target

2. Создание образа файловой системы

Другой подход — создание специального образа файловой системы (например, с помощью squashfs), куда вы поместите ваш бинарный файл. Этот образ будет монтироваться только во время работы сервиса. Для этого вам нужно:

  1. Создать образ файловой системы:
mksquashfs /path/to/dubiousbinary /var/lib/dubiousapplication/fs-image.squash
  1. Настроить unit файл для монтирования этого образа во время выполнения:
[Unit]
Description=Монтирование образа для неподтвержденного приложения
Before=myapp.service

[Mount]
What=/var/lib/dubiousapplication/fs-image.squash
Where=/run/dubiousapplication
Options=suid

[Install]
WantedBy=multi-user.target

3. Использование контейнеров

Контейнеризация — это один из наиболее надежных способов изоляции приложения. Используйте Podman для создания контейнера, в котором будет находиться ваш бинарный файл. Создайте Dockerfile:

FROM fedora:latest
COPY /path/to/binary /usr/local/bin/myapp
RUN chmod 700 /usr/local/bin/myapp
ENTRYPOINT ["/usr/local/bin/myapp"]

Затем справьтесь с необходимыми портами и настройками:

podman build -t myapp-container .
podman create --name myapp-instance -p <externalport>:<internalport> myapp-container
podman start myapp-instance

4. Конфайнмент с помощью SELinux

На системах с SELinux вы можете создать отдельный домен для вашего приложения, что позволит контролировать его действия. Создайте файл политики myapp.te:

module myapp 1.0;

require {
    type initrc_t;
    type myapp_exec_t;
    class process transition;
}

allow initrc_t myapp_exec_t:process { transition };
type_transition initrc_t myapp_exec_t:process myapp_t;

Компилируйте и загружайте политику, используя checkmodule, semodule_package, и semodule.

Заключение

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

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

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