Systemd предоставляет сервисам несколько инструментов для ограничения привилегий.
Во-первых, это директивы "User=" и "Group=".
Во-вторых, предоставляется множество директив для более тонкой настройки — их полный список доступен в https://www.freedesktop.org/software/systemd/man/systemd.exec.html — в основном, в разделах Credentials, Security, Sandboxing и System Call Filtering.
Однако самым простым и распространённым является понижение привилегий через "User=".
Тем неприятнее для нас было столкнуться с ограничением: systemd отказывается принимать имена, если в них содержится точка.
Обсуждение проблемы: "systemd does not allow user names with a dot in them" — https://github.com/systemd/systemd/issues/12754
Создатель systemd отказался исправлять поведение своего детища под тем предлогом, что некоторые утилиты (например, chown) воспринимают точку как разделитель между именем пользователя и группы, поэтому systemd также обязан воспринимать её как недопустимый служебный символ.
Не помогли отсылки к многолетней общепринятой практике использования логинов наподобие "b.gates" или "elon.m".
Осталось неуслышанным предложение создать настраиваемый список недопустимых символов, который будет по умолчанию содержать точку.
Нам остаётся только один вариант: не использовать "User=", а понижать привилегии непосредственно в команде, запускаемой в "ExecStartPre=".
Какими утилитами это можно сделать?
На ум приходят стандартные системные su и sudo, но они имеют серьёзные недостатки.
Главный из них — по ряду архитектурных причин они запускают указанное приложение через fork, т.е. продолжают висеть в памяти, оставаясь его родителем и дожидаясь завершения.
Это означает почти неизбежные проблемы с отправкой сигналов: su и sudo могут и «забыть» передать сигнал запущенному из-под них приложению, и обработать сигнал отличным от приложения образом, получив его вместе или вместо приложения.
Кроме того, sudo представляет из себя сложный универсальный комбайн с разветвлёнными настройками и не всегда очевидными параметрами по умолчанию. Например, без параметра "-E" он не передаст приложению никакие переменные окружения, кроме самых базовых (PATH, SHELL и так далее).
Нам нужен более простой инструмент:
- без дополнительных настроек — только указанные в командной строке,
- без дополнительных действий — только смена пользователя и группы перед запуском команды через exec.
Мы нашли его в лице gosu:
- Написана на Go, то есть состоит из одного файла и не имеет библиотечных зависимостей.
- Делает то, что требуется.
- Не делает то, что не требуется.
- Документация перечисляет те же самые предпосылки отказа от su и sudo, с которыми столкнулись мы.
- Нам понравилось, что документация содержит не только описание самого gosu, но и список возможных альтернатив (на данный момент это setpriv, chroot и su-exec — не исключено, что одной из них мы воспользуемся в дальнейшем).