Короткий ответ
Единого шаблона нет: модель зависит от того, какие реальные системные привилегии нужны конкретному сервису (например, только бинд на порт ниже 1024, или доступ к конкретным устройствам), и решение всегда балансирует между максимальным ограничением (лучшая защита при компрометации) и операционной сложностью настройки/отладки более строгих ограничений.
Как это работает подробнее
Классический подход «запускать всё под root для простоты» даёт процессу полный набор привилегий ядра; правильная практика — выделенный непривилегированный сервисный пользователь, Linux capabilities (например, CAP_NET_BIND_SERVICE вместо полного root, чтобы разрешить бинд на привилегированный порт) и systemd sandboxing-директивы (ProtectSystem, ProtectHome, PrivateTmp, NoNewPrivileges, ReadOnlyPaths, CapabilityBoundingSet), которые ограничивают видимость файловой системы, запрещают получение новых привилегий через SUID-бинарники и урезают набор системных вызовов через SystemCallFilter (seccomp).
Пример кода
[Service]
User=webapp
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/webappЧто использовать на практике
- Практика. внедрение начинают с наименее рискованных директив (ProtectSystem=strict, PrivateTmp=yes, NoNewPrivileges=yes), затем итеративно сужают CapabilityBoundingSet и SystemCallFilter, проверяя в стейджинге, что сервис продолжает нормально функционировать под нагрузкой и при восстановлении после сбоя — слишком агрессивное урезание без постепенного тестирования легко ломает нестандартные операции (запись pid-файла, DNS-резолвинг, временные файлы) неожиданным образом.
- Компромисс. Сервис сможет забиндиться на порт 80 без запуска под root, но не сможет писать куда-либо кроме /var/lib/webapp, получить новые привилегии через SUID или изменять системные файлы — это проверяется попыткой записи в произвольный системный путь из процесса сервиса, которая должна завершиться ошибкой доступа.
Подводные камни
- Риск. оставлять CAP_SYS_ADMIN или полный root 'на всякий случай', нивелируя весь смысл harden-инга; включать строгие ограничения сразу в проде без тестирования в стейджинге, что приводит к падениям сервиса из-за заблокированных системных вызовов или путей; не документировать, зачем нужна каждая привилегия, что усложняет аудит и будущие изменения.