Как запускать сервисы не от root
Узнайте, почему запуск от root опасен и как ограничить доступ с помощью создания системных пользователей или настройки DynamicUser в systemd.
Почему не стоит запускать всё от имени root
Root может делать на машине что угодно: читать любые файлы, изменять любые настройки, удалить всю систему. Когда вы запускаете сервис от имени root, вы передаете всю эту власть этому сервису. Если в сервисе есть ошибка, которую может использовать злоумышленник, он получает не только доступ к сервису, он получает root, а это — весь сервер. Запуск от имени непривилегированного пользователя ограничивает ущерб. Ошибка в сервисе, работающем под ограниченной учетной записью, дает злоумышленнику только те ресурсы, к которым имеет доступ эта запись, а их должно быть почти ничего.
Это принцип наименьших привилегий: предоставляйте каждой части системы только тот доступ, который необходим для выполнения её задач, и ничего больше. Это самый эффективный способ ограничить радиус поражения при компрометации, и на современном сервере внедрение этого принципа практически ничего вам не стоит.
Выделенная учетная запись для каждого сервиса
Классический подход заключается в создании отдельного системного пользователя для каждого сервиса — пользователя, который владеет только файлами этого сервиса и не может войти в систему. Системная учетная запись для веб-приложения может выглядеть так:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvcКаждый флаг имеет значение. --system делает её системной учетной записью, а не учетной записью пользователя. --no-create-home пропускает создание домашнего каталога, который не требуется. --shell /usr/sbin/nologin означает, что даже если злоумышленник каким-то образом завладеет учетной записью, он не сможет открыть с её помощью shell. Учетная запись существует только для владения процессом и его файлами.
Затем предоставьте этому пользователю только необходимые ему файлы и ничего больше:
sudo chown -R appsvc:appsvc /opt/myappТеперь сервис читает и записывает данные в свой собственный каталог и не имеет доступа ни к чему другому на диске. Если сервис будет взломан, файлы, которые сможет изменить злоумышленник, будут ограничены каталогом /opt/myapp; учетная запись по-прежнему сможет читать любые файлы, доступные для чтения всем пользователям (world-readable), но она не сможет изменить остальную часть системы.
Позвольте systemd запускать сервис от имени этого пользователя
После создания учетной записи укажите systemd запускать сервис от её имени. В unit-файле это делает одна строка:
[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvcUser=appsvc означает, что процесс запускается с ограниченными привилегиями этой учетной записи вместо привилегий root. Это стандартный, проверенный способ запуска приложения в systemd, и это стоит делать для каждого сервиса, для которого вы пишете unit-файл.
Или полностью пропустите создание учетной записи с помощью DynamicUser
systemd может пойти еще дальше и создать для вас временного пользователя, который существует только во время работы сервиса. Установите DynamicUser=yes, и вам не придется управлять учетной записью вовсе:
[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myappПри запуске systemd выделяет неиспользуемый ID пользователя; при остановке — освобождает его. Сервис также получает частный /tmp (read-only представление большей части файловой системы) и записываемый каталог состояния в /var/lib/myapp, который настраивает и предоставляет StateDirectory=. Для автономного сервиса, которому нужен только его собственный каталог состояния, DynamicUser=yes является самым простым способом обеспечить сильную изоляцию, так как для атаки вообще нет долгоживущей учетной записи.
Ручное написание unit-файлов — трудоемкий процесс, а основная ценность заключается в правильной настройке директив защиты. Генератор в руководстве по сервисам и таймерам systemd может заполнить эти параметры за вас, чтобы unit-файл был корректным с первого раза.
Как это сочетается с остальными мерами
Принцип наименьших привилегий — это один уровень защиты, который работает вместе с другими, а не заменяет их. Межсетевой экран по умолчанию (default-deny) контролирует, что может достичь сервиса; запуск от имени непривилегированного пользователя контролирует, что может сделать сервис в случае взлома; а усиленный SSH предотвращает проникновение злоумышленников на машину. Ни один из этих методов сам по себе не является достаточным, но вместе они гарантируют, что ошибка в одном сервисе не приведет к компрометации всего сервера.
Прежде чем продолжить, изучите контрольный список (checklist) по защите всей системы и создайте его персональную копию для работы:
FAQ
Почему мне не следует запускать сервис от имени root?
Потому что root может делать на машине что угодно. Если сервис, запущенный от root, будет взломан, злоумышленник получит контроль над всем сервером, а не только над сервисом. Запуск сервиса от имени ограниченной, непривилегированной учетной записи ограничивает ущерб только теми ресурсами, к которым имеет доступ эта запись. Оставьте root для администрирования, а каждый долгоживущий сервис запускайте от имени ограниченного пользователя.
Как мне создать пользователя, который не может войти в систему?
Выполните sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME. Shell nologin означает, что учетная запись не может открыть интерактивную сессию, даже если её учетные данные будут украдены; --system помечает её как системную учетную запись, а --no-create-home пропускает создание ненужного домашнего каталога. Предоставьте ей владение только её собственными файлами с помощью chown.
Что такое systemd DynamicUser?
DynamicUser=yes указывает systemd создать временного пользователя для сервиса, который существует только во время его работы, поэтому вам не нужно управлять долгоживущей учетной записью. Это также предоставляет сервису частный /tmp (в основном read-only представление файловой системы) и управляемый каталог состояния. Это самый простой способ запуска автономного сервиса под временной учетной записью с низкими привилегиями.
Заменяет ли запуск от имени не-root пользователя межсетевой экран?
Нет. Они защищают разные вещи. Запуск от имени непривилегированного пользователя ограничивает возможности сервиса в случае взлома, в то время как межсетевой экран ограничивает то, что вообще может достичь сервиса. Используйте оба метода вместе с усиленным SSH, чтобы каждый уровень защиты покрывал то, что не могут покрыть другие.
Какими файлами должен владеть пользователь сервиса?
Только теми файлами, которые действительно необходимы сервису, и ничем больше. Предоставьте учетной записи владение её собственной рабочей директорией и данными, а все остальное оставьте за root. Хорошим шаблоном является sudo chown -R svc-app:svc-app /opt/svc-app для каталога приложения, в то время как конфигурация в /etc остается принадлежащей root и доступной для чтения только сервису. Цель состоит в том, чтобы при компрометации процесса файлы, которые он может изменить, ограничивались только его собственными данными, а не остальной системой.