Как повысить безопасность сервисов systemd
В настоящее время все основные дистрибутивы Linux используют Systemd в качестве менеджера системы/сервисов инициализации. Для создания службы systemd достаточно написать модуль «.service» в соответствующем каталоге и управлять им с помощью утилиты systemctl. При запуске службы или процесса в целом мы хотим убедиться, что он работает с минимально возможным набором привилегий, необходимых для выполнения задачи. Systemd предоставляет ряд опций, которые мы можем использовать для точной настройки поведения службы, детального предоставления или отказа в привилегиях и обеспечения определенного уровня изоляции от остальной системы.
В этой статье мы увидим, как повысить безопасность службы systemd и как получить оценку уровня ее уязвимости с помощью утилиты systemd-analyze.
В этом уроке вы узнаете:
- Как повысить безопасность службы systemd
- Как получить предполагаемый уровень воздействия сервиса с помощью systemd-analyze
Тестовый пример: написание службы резервного копирования
В предыдущем уроке мы говорили о Restic, эффективной программе резервного копирования с дедупликацией, написанной на Go. Для этой статьи давайте представим, что мы хотим написать «restic»-сервис для планирования резервного копирования через системный таймер. Начнем с написания раздела «Unit»:
[Unit]
Description=restic backup
Wants=network-online.target
After=network-online.target
Первым делом мы предоставили описание услуги, используя опцию Description
. Поскольку мы хотим иметь возможность использовать удаленные репозитории для наших резервных копий, используя параметры Wants
и After
, мы соответственно объявили (слабую) зависимость службы от network-online.target
и установлен, его необходимо запускать только после достижения указанной цели и настройки сетевых интерфейсов.
Теперь давайте заполним раздел «Сервис» устройства. Здесь мы определяем поведение нашего сервиса:
[Service]
Type=oneshot
User=restic
ExecStart=/usr/local/bin/restic_backup.sh
Мы использовали опцию «Тип», чтобы определить наш сервис как «oneshot». Это влияет на то, как systemd обрабатывает службу: она будет считать ее «активной» только после завершения основного процесса.
По очевидным соображениям безопасности мы хотим избежать запуска службы от имени пользователя root, поэтому мы создали непривилегированного пользователя «restic» и с помощью опции User
указали, что процесс должен запускаться с его привилегиями. Наконец, с помощью опции ExecStart
мы определили команду/исполняемый файл, который должен вызываться при запуске службы; в данном случае это сценарий /usr/local/bin/restic_backup.sh, который содержит основную логику резервного копирования.
В разделе «Сервис» блока мы можем использовать несколько других опций для дальнейшей настройки привилегий нашего сервиса. Давайте посмотрим на некоторые из них.
Запуск процесса с возможностями
Наш сервис будет работать с привилегиями пользователя «restic». Это хорошая мера безопасности, однако мы должны убедиться, что Restic может читать всю файловую систему. Чтобы достичь нашей цели, мы можем гарантировать, что процесс работает с соответствующими возможностями:
AmbientCapabilities=CAP_DAC_READ_SEARCH
CapabilityBoundingSet=CAP_DAC_READ_SEARCH
Параметр AmbientCapabilities
принимает в качестве значения список возможностей, разделенных запятыми, которые мы хотим включить в набор внешних возможностей процесса. В данном случае мы просто использовали возможность «CAP_DAC_READ_SEARCH», которая позволяет обходить проверки разрешений на чтение файлов и каталогов. В качестве меры безопасности мы также использовали параметр CapabilityBoundingSet
, чтобы ограничить набор возможностей, которые процессу разрешено получить.
Усиление сервиса
Два варианта, связанных с возможностями, которые мы использовали выше, являются лишь небольшим подмножеством тех, которые мы можем использовать для «изолирования» службы. Большинство из них принимают логическое значение. Давайте посмотрим несколько примеров.
ПриватТмп
Опция «PrivateTmp» защищает временные файлы, созданные службой, чтобы другие процессы не могли получить к ним доступ. Когда эта опция активна, systemd создает изолированные каталоги /tmp и /var/tmp и монтирует их в частном пространстве имен.
ProtectKernelModules, ProtectKernelLogs и ProtectKernelTunables
Эти параметры защищают состояние ядра. Активный элемент ProtectKernelModules
запрещает службе возможность загружать и выгружать модули ядра, а ProtectKernelLogs
запрещает доступ к буферу журнала ядра. Поведение определенных модулей ядра можно изменить, записав соответствующие значения в файлы, представленные в псевдофайловых системах /proc и /sys; опция ProtectKernelTunables
, если она активна, запрещает такие действия.
НетНовыеПривилегии
Эту опцию можно использовать, чтобы гарантировать, что служба и ее дочерние процессы не смогут получить новые привилегии путем выполнения других программ с помощью системного вызова execve, который является частью стандартной библиотеки C. Когда эта опция активна, она запрещает выполнение двоичных файлов с установленными битами SETUID или SETGID.
ОграничитьSUIDSGID
Если этот параметр имеет значение true, он не позволяет процессу, вызываемому службой, устанавливать биты SETUID или SETGID в файлах и каталогах.
Ограничить адрессемейства
Эта опция принимает разделенный пробелами список имен семейств адресов, к которым процесс может получить доступ (например: AF_UNIX, AF_INET, AF_INET6); «none» также является допустимым значением: оно запрещает доступ ко всем из них.
Частные устройства
Когда эта опция активна, процессу запрещается доступ к необработанным устройствам, таким как /dev/sda или /dev/mem.
Защитить часы
Если установлено значение true, доступ к системным часам запрещен.
Защитить имя хоста
Эта опция защищает имя хоста системы, гарантируя, что процесс не сможет его изменить.
УдалитьIPC
Когда он активен, он вызывает автоматическое удаление ресурсов IPC (межпроцессного взаимодействия), выделенных службе.
ПриватМаунтс
Когда эта опция активна, процесс выполняется в собственной частной и изолированной файловой системе, недоступной с хоста.
Получение примерного уровня безопасности сервиса
Вот как в итоге выглядит наш сервис:
[Unit]
Description=restic backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=restic
ExecStart=/usr/local/bin/restic_backup.sh
AmbientCapabilities=CAP_DAC_READ_SEARCH
CapabilityBoundingSet=CAP_DAC_READ_SEARCH
PrivateTmp=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectKernelTunables=yes
NoNewPrivileges=yes
RestrictSUIDSGID=yes
RestrictAddressFamilies=yes
PrivateDevices=yes
ProtectClock=yes
ProtectHostname=yes
RemoveIPC=yes
PrivateMounts=yes
Как только мы поместим модуль в один из каталогов, распознаваемых systemd (например, /etc/systemd/system), чтобы получить его предполагаемый уровень безопасности, нам просто нужно запустить «systemd-analyze» с командой «security», передав имя устройства в качестве аргумента. Предположим, мы сохранили модуль как «restic.service», и запустим:
$ systemd-analyze security restic.service
Команда возвращает список доступных опций безопасности с указанием присутствующих и отсутствующих в устройстве, а также общий уровень воздействия: чем он ниже, тем лучше. Каждая неиспользованная опция увеличивает значение риска на сумму, указанную в столбце «ЭКСПОЗИЦИЯ». Вот результат, который мы получаем, выполнив команду для нашего сервисного модуля:
NAME DESCRIPTION EXPOSURE
✓ RemoveIPC= Service user cannot leave SysV IPC objects around
✗ RootDirectory=/RootImage= Service runs within the host's root directory 0.1
✓ User=/DynamicUser= Service runs under a static non-root user identity
✓ CapabilityBoundingSet=~CAP_SYS_TIME Service processes cannot change the system clock
✓ NoNewPrivileges= Service processes cannot acquire new privileges
✗ AmbientCapabilities= Service process receives ambient capabilities 0.1
✗ CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER) Service may override UNIX file/IPC permission checks 0.2
✗ ProtectControlGroups= Service may modify the control group file system 0.2
✓ CapabilityBoundingSet=~CAP_BPF Service may load BPF programs
✗ SystemCallArchitectures= Service may execute system calls with all ABIs 0.2
✗ MemoryDenyWriteExecute= Service may create writable executable memory mappings 0.1
✗ RestrictNamespaces=~user Service may create user namespaces 0.3
✗ RestrictNamespaces=~pid Service may create process namespaces 0.1
✗ RestrictNamespaces=~net Service may create network namespaces 0.1
✗ RestrictNamespaces=~uts Service may create hostname namespaces 0.1
✗ RestrictNamespaces=~mnt Service may create file system namespaces 0.1
✗ RestrictNamespaces=~cgroup Service may create cgroup namespaces 0.1
✗ RestrictNamespaces=~ipc Service may create IPC namespaces 0.1
✗ LockPersonality= Service may change ABI personality 0.1
✗ RestrictRealtime= Service may acquire realtime scheduling 0.1
✓ SupplementaryGroups= Service has no supplementary groups
✓ CapabilityBoundingSet=~CAP_SYS_RAWIO Service has no raw I/O access
✓ CapabilityBoundingSet=~CAP_SYS_PTRACE Service has no ptrace() debugging abilities
✓ CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE) Service has no privileges to change resource use parameters
✓ CapabilityBoundingSet=~CAP_NET_ADMIN Service has no network configuration privileges
✓ CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW) Service has no elevated networking privileges
✓ CapabilityBoundingSet=~CAP_AUDIT_* Service has no audit subsystem access
✓ CapabilityBoundingSet=~CAP_SYS_ADMIN Service has no administrator privileges
✓ PrivateTmp= Service has no access to other software's temporary files
✓ CapabilityBoundingSet=~CAP_SYSLOG Service has no access to kernel logging
✓ PrivateDevices= Service has no access to hardware devices
✗ ProtectSystem= Service has full access to the OS file hierarchy 0.2
✗ ProtectProc= Service has full access to process tree (/proc hidepid=) 0.2
✗ ProcSubset= Service has full access to non-process /proc files (/proc subset=) 0.1
✗ ProtectHome= Service has full access to home directories 0.2
✗ PrivateNetwork= Service has access to the host's network 0.5
✗ PrivateUsers= Service has access to other users 0.2
✗ DeviceAllow= Service has a device ACL with some special devices: char-rtc:r 0.1
✓ KeyringMode= Service doesn't share key material with other services
✓ Delegate= Service does not maintain its own delegated control group subtree
✗ SystemCallFilter=~@clock Service does not filter system calls 0.2
✗ SystemCallFilter=~@cpu-emulation Service does not filter system calls 0.1
✗ SystemCallFilter=~@debug Service does not filter system calls 0.2
✗ SystemCallFilter=~@module Service does not filter system calls 0.2
✗ SystemCallFilter=~@mount Service does not filter system calls 0.2
✗ SystemCallFilter=~@obsolete Service does not filter system calls 0.1
✗ SystemCallFilter=~@privileged Service does not filter system calls 0.2
✗ SystemCallFilter=~@raw-io Service does not filter system calls 0.2
✗ SystemCallFilter=~@reboot Service does not filter system calls 0.2
✗ SystemCallFilter=~@resources Service does not filter system calls 0.2
✗ SystemCallFilter=~@swap Service does not filter system calls 0.2
✗ IPAddressDeny= Service does not define an IP address allow list 0.2
✓ NotifyAccess= Service child processes cannot alter service state
✓ ProtectClock= Service cannot write to the hardware clock or system clock
✓ CapabilityBoundingSet=~CAP_SYS_PACCT Service cannot use acct()
✓ CapabilityBoundingSet=~CAP_KILL Service cannot send UNIX signals to arbitrary processes
✓ ProtectKernelLogs= Service cannot read from or write to the kernel log ring buffer
✓ CapabilityBoundingSet=~CAP_WAKE_ALARM Service cannot program timers that wake up the system
✓ CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE Service cannot mark files immutable
✓ CapabilityBoundingSet=~CAP_IPC_LOCK Service cannot lock memory into RAM
✓ ProtectKernelModules= Service cannot load or read kernel modules
✓ CapabilityBoundingSet=~CAP_SYS_MODULE Service cannot load kernel modules
✓ CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG Service cannot issue vhangup()
✓ CapabilityBoundingSet=~CAP_SYS_BOOT Service cannot issue reboot()
✓ CapabilityBoundingSet=~CAP_SYS_CHROOT Service cannot issue chroot()
✓ PrivateMounts= Service cannot install system mounts
✓ CapabilityBoundingSet=~CAP_BLOCK_SUSPEND Service cannot establish wake locks
✓ CapabilityBoundingSet=~CAP_LEASE Service cannot create file leases
✓ CapabilityBoundingSet=~CAP_MKNOD Service cannot create device nodes
✓ ProtectHostname= Service cannot change system host/domainname
✓ CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP) Service cannot change file ownership/access mode/capabilities
✓ CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP) Service cannot change UID/GID identities/capabilities
✓ ProtectKernelTunables= Service cannot alter kernel tunables (/proc/sys, …)
✓ RestrictAddressFamilies=~AF_PACKET Service cannot allocate packet sockets
✓ RestrictAddressFamilies=~AF_NETLINK Service cannot allocate netlink sockets
✓ RestrictAddressFamilies=~AF_UNIX Service cannot allocate local sockets
✓ RestrictAddressFamilies=~… Service cannot allocate exotic sockets
✓ RestrictAddressFamilies=~AF_(INET|INET6) Service cannot allocate Internet sockets
✓ CapabilityBoundingSet=~CAP_MAC_* Service cannot adjust SMACK MAC
✓ RestrictSUIDSGID= SUID/SGID file creation by service is restricted
✗ UMask= Files created by service are world-readable by default 0.1
→ Overall exposure level for restic.service: 4.6 OK 🙂
Наша оценка – «4,6»: неплохо, но мы еще можем ее улучшить!
Выводы
В этом уроке мы узнали, как повысить уровень безопасности службы, используя некоторые параметры Systemd. Мы также узнали, как использовать утилиту systemd-analyze для проверки уровня уязвимости службы. По понятным причинам мы не рассмотрели здесь все доступные варианты усиления защиты; Чтобы получить полный список и узнать больше по этой теме, просмотрите онлайн-документацию или просто проверьте справочную страницу «systemd.exec».