Контейнер LXD не открывает /dev/net/tun? Проверьте запись unix-char
LXD должен передать устройство TUN хоста в контейнер, прежде чем клиент VPN сможет его использовать. Посмотрите, что добавляет unix-char, чего не добавляет, и как это проверить.
LXD предоставляет контейнеру символьное устройство хоста, добавляя узел устройства, а не обучая гостя создавать его. Если в контейнере нет /dev/net/tun, устройство может быть на хосте, а гость всё равно не сможет его открыть. Запись unix-char — это половина этого пути на стороне менеджера, и она намеренно уже, чем привилегированный контейнер. Поэтому узел хоста и узел контейнера заслуживают отдельных проверок.
Механизм достаточно короток, чтобы держать его в поле зрения: LXD разрешает путь хоста, создаёт соответствующий узел внутри экземпляра и добавляет устройство в cgroup устройств контейнера. Затем процессу гостя нужны собственное разрешение и способность, чтобы использовать узел для чего угодно.
Что запись unix-char действительно меняет
Устройство unix-char сообщает LXD, что нужно предоставить одно символьное устройство хоста по выбранному пути внутри контейнера. У записи есть три важные части: метка устройства, которую использует менеджер, путь source на хосте и path, который видит гость. Только последние две описывают само устройство. Опечатка в любой из них создаёт узел в месте, которое клиент никогда не открывает.
На хосте устройство обычно /dev/net/tun. Внутри контейнера можно сохранить тот же путь или выбрать другой, но путь должен совпадать с тем, что ожидает клиент VPN. Сохранение /dev/net/tun избавляет от второй проблемы конфигурации, потому что большинство клиентов открывают этот путь не спрашивая.
Менеджер применяет запись при запуске экземпляра или при горячем подключении устройства. Он не копирует драйвер в гостя и не создаёт туннельный интерфейс. Он делает узел видимым и разрешает доступ через группу управления устройствами контейнера.
Объявляйте устройство на экземпляре, а не внутри гостя
Добавьте запись из контекста администрирования LXD, назвав экземпляр и метку, которую вы узнаете позже. Для контейнера с именем vpnbox команда такая:
lxc config device add vpnbox tun unix-char source=/dev/net/tun path=/dev/net/tun
lxc config device show vpnbox
Метка tun локальна для конфигурации экземпляра. Это не имя интерфейса, и оно не должно совпадать ни с чем в госте. Если профиль уже предоставляет то же устройство, вторая запись создаёт конфликт, а не избыточность, поэтому сначала просмотрите действующую конфигурацию.
Редактирование файлов внутри контейнера — неверный уровень. mknod в стартовом сценарии может выглядеть работающим на привилегированном контейнере, а затем исчезнуть при пересоздании или не сработать на менее привилегированном. Набором устройств экземпляра владеет менеджер; пусть он владеет и узлом.
Согласуйте владельца и режим с процессом гостя
Запись unix-char может задать uid, gid и mode для узла внутри контейнера. Используйте их, чтобы соответствовать учётной записи, запускающей клиент VPN, вместо того чтобы открывать устройство всем пользователям или переводить контейнер в привилегированный режим.
Видимость — не то же самое, что разрешение. Узел, принадлежащий root с режимом 600, виден службе, работающей как непривилегированный пользователь, и всё равно не может быть ею открыт. Сначала проверьте учётную запись гостя, затем назначьте владельцем эту запись и держите режим не шире, чем нужно клиенту.
Процессу также нужна способность настраивать туннельный интерфейс, обычно CAP_NET_ADMIN в сетевом пространстве имён контейнера. Передача устройства не даёт этой способности, а предоставление способности не даёт устройства; это независимые требования.
Знайте, чего запись не даёт
Видимый узел — не работающий VPN. Пакет клиента всё ещё нужно установить, профиль всё ещё должен быть действительным, и служба всё ещё должна запуститься. Аутентификация, маршруты и DNS остаются работой клиента, и сбой там не является доказательством того, что запись устройства неверна.
systemd может добавить ещё одно ограничение. Модуль с DeviceAllow или ужесточённой политикой устройств может запретить доступ к узлу, который предоставил менеджер контейнеров. Проверьте политику устройств модуля, когда узел существует, но служба сообщает об ошибке разрешения, и измените эту политику, а не расширяйте контейнер.
Горячее подключение необязательно и явно. Записи с required=false нужен путь источника, и она может появляться или исчезать при изменении устройства хоста. Такой жизненный цикл подходит для оборудования, которое приходит и уходит; для TUN-устройства, нужного VPN при загрузке, необязательная запись может оставить службу запускающейся раньше, чем появится её предусловие.
Проверяйте видимость, открытие и интерфейс именно в этом порядке
Сначала подтвердите узел внутри нужного контейнера. Путь, старший и младший номера и владелец должны совпадать с устройством хоста и добавленной записью. Отсутствие узла означает, что менеджер не применил запись или применил её к другому экземпляру.
lxc exec vpnbox -- ls -l /dev/net/tun
lxc exec vpnbox -- sh -c 'test -c /dev/net/tun && echo char-device-ok'
Затем подтвердите, что учётная запись службы может его открыть. Короткая проверка от этой записи отделяет проблему владения от проблемы службы. Наконец, запустите клиент и подтвердите, что появился туннельный интерфейс; только этот третий шаг доказывает весь путь, потому что первые два могут пройти, пока профиль или способность клиента ещё неверны.
Запишите экземпляр, метку устройства, источник на хосте, путь в госте и учётную запись-владельца, когда изменение одобрено. Эти пять значений должны сохранить последующее пересоздание, миграция или смена профиля, и их гораздо проще сравнить, чем ошибку разрешения без контекста.