Чем меньше у системы работающих служб, загруженных модулей ядра и открытых интерфейсов, тем меньше у атакующего точек входа. В этом уроке мы сокращаем поверхность атаки: гасим лишние сервисы и юниты, запрещаем загрузку ненужных модулей ядра, ограничиваем USB через USBGuard, заворачиваем демоны в песочницу systemd и подкручиваем параметры ядра через sysctl. Всё это - повседневная работа администратора, которую на экзамене 303-300 спрашивают именно как набор конкретных механизмов.

Как это работает
Поверхность атаки - это сумма всего, до чего может дотянуться злоумышленник: слушающие порты, suid-бинарники, доступные устройства, код в ядре. Каждый из этих элементов либо нужен для работы, либо его надо убрать. Принцип простой: запрещено всё, что не требуется явно.
Службами управляет systemd. Юнит может быть активен (running), остановлен, но включён в автозапуск (enabled), или замаскирован (masked) - тогда его нельзя запустить даже вручную или по зависимости. Маскирование - это симлинк юнита в /dev/null, самый жёсткий способ выключить службу навсегда. Сокеты systemd могут поднимать демон по первому подключению (socket activation), поэтому проверять надо не только .service, но и .socket.
Модули ядра - это код, исполняемый в кольце 0. Лишний драйвер файловой системы или сетевого протокола (cramfs, dccp, sctp, firewire) - это потенциальная дыра. Модуль можно занести в чёрный список (blacklist), но это лишь мешает автозагрузке. Чтобы запретить намертво, используют директиву install ... /bin/true (или /bin/false), которая подменяет загрузку модуля заведомо безобидной командой.
USBGuard реализует политику для USB-устройств на лету. Демон слушает события udev, сверяет дескрипторы устройства (vendor/product ID, serial, интерфейсы) с белым списком и разрешает или блокирует подключение. Это защита от BadUSB и от того, что кто-то воткнёт в сервер свою флешку или HID-эмулятор.
Песочница systemd - это набор директив юнита, которые ограничивают процесс средствами ядра (namespaces, capabilities, seccomp). ProtectSystem делает /usr и /etc только для чтения, PrivateTmp выдаёт службе изолированный /tmp, NoNewPrivileges запрещает повышение прав через suid и file capabilities. Это сильно сужает урон от скомпрометированного демона без переписывания самого демона.
Параметры ядра через sysctl управляют поведением сети и памяти в рантайме. Тут же лежит защита /proc: монтирование с hidepid скрывает чужие процессы от непривилегированных пользователей. Secure Boot проверяет подпись загрузчика и ядра на этапе старта, не давая подсунуть свой неподписанный код.
Команды и примеры
Найти и погасить лишние слушающие службы и сокеты:
Код: Выделить всё
ss -tulpnH
systemctl list-units --type=socket --state=listening
systemctl disable --now avahi-daemon.service
systemctl mask avahi-daemon.socket
Запрет модуля ядра. Создаём файл в /etc/modprobe.d/:
Код: Выделить всё
# /etc/modprobe.d/hardening.conf
blacklist dccp
install dccp /bin/false
install firewire-core /bin/false
Код: Выделить всё
lsmod
modprobe -r firewire-core # выгрузить сейчас
modinfo firewire-core # откуда модуль, зависимости
Код: Выделить всё
# Debian 13 / Ubuntu 24.04
apt install usbguard
# RHEL 10 / Fedora 41+
dnf install usbguard
Код: Выделить всё
usbguard generate-policy > /etc/usbguard/rules.conf
systemctl enable --now usbguard.service
usbguard list-devices
usbguard allow-device 6 # разрешить устройство с id 6
Код: Выделить всё
systemctl edit myservice.service
Код: Выделить всё
[Service]
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallFilter=@system-service
ReadWritePaths=/var/lib/myservice
Код: Выделить всё
systemd-analyze security myservice.service
Код: Выделить всё
# /etc/sysctl.d/90-hardening.conf
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.unprivileged_bpf_disabled = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 1
Код: Выделить всё
sysctl --system # применить все файлы
sysctl kernel.dmesg_restrict
Код: Выделить всё
proc /proc proc defaults,hidepid=2,gid=proc 0 0
Код: Выделить всё
mokutil --sb-state
find / -xdev -perm -4000 -type f 2>/dev/null # все suid
- disable без mask: служба не стартует сама, но её всё ещё может поднять другой юнит по зависимости или socket activation. Для гарантии - mask.
- Забыли про .socket. Гасите .service, а демон всё равно просыпается при подключении к порту, потому что активен парный сокет.
- blacklist без install. Чёрный список не мешает явной загрузке модуля по имени или подгрузке по зависимости. Нужна именно директива install ... /bin/false.
- ProtectSystem=strict без ReadWritePaths ломает службу: ей некуда писать. Сначала найдите рабочие каталоги, потом откройте только их.
- SystemCallFilter слишком узкий - демон падает с SIGSYS на старте. Снимайте профиль постепенно, смотрите журнал на ошибки seccomp.
- Правки прямо в /usr/lib/systemd/system/. Их затрёт обновление пакета. Используйте systemctl edit и drop-in в /etc.
- hidepid сломал доступ агентам мониторинга. Заведите группу proc и добавьте туда нужные сервисные учётки через gid.
- Отключение Secure Boot ради проприетарного драйвера (NVIDIA) без понимания последствий - теряете цепочку доверия загрузки.
- Снимите карту слушающих сокетов: ss -tulpnH и сопоставьте каждый порт с конкретным юнитом.
- Выберите одну заведомо лишнюю службу (например avahi или cups, если печать не нужна), выполните disable --now, затем mask и проверьте, что она не поднимается.
- Запретите модуль dccp через install ... /bin/false, перезагрузитесь и убедитесь, что modprobe dccp молча ничего не делает.
- Установите usbguard, сгенерируйте политику из текущих устройств, запустите демон и попробуйте подключить новую флешку - она должна заблокироваться.
- Выберите свой демон, прогоните systemd-analyze security, затем через override добавьте ProtectSystem, PrivateTmp, NoNewPrivileges и снова сравните оценку.
- Примените набор sysctl из урока через sysctl --system и проверьте 2-3 значения чтением.
- Найдите все suid-бинарники через find и обоснуйте для себя, зачем нужен каждый.
- Чем mask отличается от disable и почему disable не всегда достаточно?
- Почему blacklist в modprobe.d не гарантирует, что модуль не загрузится, и какая директива решает проблему?
- Что именно ограничивают ProtectSystem=strict и NoNewPrivileges, и какой риск несёт первый без ReadWritePaths?
- Как USBGuard принимает решение разрешить или заблокировать устройство и от какого класса атак он защищает?
- Зачем нужен hidepid на /proc и как не отрезать от данных легитимный мониторинг?
- Что проверяет Secure Boot на этапе загрузки и чем грозит его отключение?