Дать OpenVPN CAP_NET_ADMIN, не выдавая ему root
Юнит, запускаемый от непривилегированного пользователя, всё равно нуждается в правильном наборе возможностей. Проверьте юнит, живой процесс и границу exec именно в этом порядке.
Служба запускается, журнал жалуется, что не может открыть устройство tun, а маршруты так и не появляются. Выполните capsh --print в своей оболочке, и cap_net_admin окажется тут же, из-за чего сбой кажется невозможным. Разница между вашей оболочкой и юнитом — это переход привилегий, а не готовность ядра выдать возможность. Пройдите четыре проверки ниже по порядку и остановитесь на первой, которая объясняет разрыв.
Отделите отсутствующую возможность от скрытого устройства
Два сбоя выглядят одинаково в строке журнала. Процесс без CAP_NET_ADMIN не может создать или настроить интерфейс туннеля, а процесс, чья песочница скрывает /dev/net/tun, вообще не может его открыть. Добавление возможностей не исправит второй случай.
Прежде чем что-либо менять, проверьте, существует ли устройство внутри пространства имён службы. PrivateDevices=yes — распространённая строка ужесточения, которая полностью удаляет узел tun, а ограничительный ProtectSystem может сделать путь конфигурации доступным только для чтения так, что это читается как ошибка прав. Оба дают строки журнала, где упоминается туннель, поэтому вину возлагают на возможности. Сначала убедитесь, что узел присутствует и открывается, затем двигайтесь дальше.
Знайте, на какой набор возможностей вы смотрите
Linux держит наборы разрешённых, действующих, наследуемых, ограничивающих и окружающих возможностей, и именно окружающий набор переживает exec от пользователя не-root. Файловые возможности на бинарнике могут передать разрешённые и действующие права при exec, но только пока ничто в цепочке не блокирует эту передачу.
Именно здесь sudo вводит людей в заблуждение. Команда, запущенная через sudo -u, начинает новый переход привилегий, и окружающий набор исчезает, поэтому та же проверка, которая успешна как root, там проваливается. Менеджер служб — ещё один переход, и он ничего окружающего не даёт, если юнит этого не говорит.
Дайте возможность в юните, а не в обёртке
Минимальная секция службы для туннеля, которому нужно создать устройство и установить маршруты, выглядит так.
[Unit]
Description=OpenVPN client, office profile
After=network-online.target
[Service]
Type=notify
User=openvpn
Group=openvpn
NoNewPrivileges=yes
AmbientCapabilities=CAP_NET_ADMIN
CapabilityBoundingSet=CAP_NET_ADMIN
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/office.confAmbientCapabilities помещает право в окружающий набор до запуска процесса, а CapabilityBoundingSet не даёт процессу держать что-либо вне этого списка. NoNewPrivileges=yes блокирует получение привилегий через setuid или файловые возможности, а возможность, которую юнит даёт явно, всё равно действует. Добавляйте CAP_NET_BIND_SERVICE только когда службе нужно занять низкий порт, и никогда не добавляйте CAP_SYS_ADMIN только потому, что так сделала тема на форуме.
Если юнит оборачивает бинарник в sudo, уберите обёртку. systemd уже применяет User=, и именно этот лишний слой — самая вероятная причина пустого окружающего набора.
Проверяйте на живом процессе, а не на файле юнита
Прочитайте, что менеджер фактически применил, а затем прочитайте процесс, который он породил.
unit=openvpn-client@office
systemctl show -p User -p AmbientCapabilities "$unit"
systemctl show -p MainPID --value "$unit" | xargs -r getpcaps
grep -E '^Cap(Eff|Amb)' /proc/$(systemctl show -p MainPID --value "$unit")/statusЕсли юнит перечисляет возможность, а CapEff всё ещё её не имеет, какой-то drop-in переопределяет настройку или шаг ExecStartPre меняет окружение. Прежде чем считать основной файл главным, поищите имя юнита в каталогах drop-in, потому что более поздний файл тихо побеждает. Если и юнит, и процесс показывают возможность, а туннель всё равно не работает, вернитесь к проверкам устройства и пути, а не расширяйте ограничивающий набор. Его расширение скрывает настоящую причину и оставляет службу более привилегированной, чем она была.
Предпочитайте сбрасывать привилегии после запуска
OpenVPN может стартовать с привилегиями, а затем перейти на непривилегированную учётную запись через собственные опции user и group, а persist-key и persist-tun сохраняют туннель пригодным дальше. Такая схема сужает окно, в котором процесс держит права, и часто подходит лучше, чем запуск всего демона без привилегий с постоянными возможностями.
Какую бы модель вы ни выбрали, перезапустите юнит и повторите проверки после перезагрузки, потому что служба, которая работает из интерактивной оболочки root и сбоит при загрузке, обычно отличается именно возможностью, устройством или правами файла. Запишите настройки юнита рядом с выводом возможностей процесса, чтобы следующий рецензент видел, сдвинуло ли границу обновление пакета или drop-in.