NET_ADMIN в контейнере VPN: что и где он может изменить
Эта возможность разрешает менять интерфейсы, маршруты и межсетевой экран внутри одного сетевого пространства имён. При сети хоста это пространство — собственный стек хоста.
NET_ADMIN — это возможность Linux, позволяющая процессу выполнять сетевую администрацию, и в контейнере она делает именно это внутри того сетевого пространства имён, в котором работает процесс. Это не лицензия на открытие сокета и не замена устройству или маршруту. Интересный вопрос никогда не в том, выдана ли возможность, а в том, какое пространство имён получает изменение.
Что охватывает возможность
Внутри своего пространства имён процесс с NET_ADMIN может поднимать и опускать интерфейсы, назначать адреса, добавлять и удалять маршруты и писать правила межсетевого экрана, то есть группу операций, которую capabilities(7) собирает под этой возможностью. Он также может настраивать параметры уровня интерфейса, управляющие тем, как ядро ставит трафик в очередь и фильтрует его. VPN-клиенту обычно нужны несколько из них при запуске и больше ничего после. Поэтому большинство образов запрашивают более широкий доступ, чем используют.
Операции за пределами этого пространства имён не охватываются, а смежные привилегии отдельны. Чтение и запись необработанных пакетов относится к NET_RAW, а создание устройства tun требует и возможности, и доступа к узлу устройства. Процесс без устройства получает ошибку разрешения, которая выглядит как сетевая проблема, но ею не является.
Пространство имён определяет радиус поражения
Контейнер в собственной мостовой сети достигает своего loopback, своих интерфейсов и своей таблицы маршрутизации. Выдача там NET_ADMIN позволяет VPN-клиенту построить туннель, не затрагивая решения маршрутизации хоста, — именно это расположение предполагает большинство образов контейнеров.
Переключите этот контейнер на сеть хоста, и та же выдача теперь редактирует маршруты хоста и его межсетевой экран, потому что отдельного пространства имён больше не осталось. Клиент, который должен был управлять одним туннелем, может затем перенаправить трафик всей машины, а ошибка в его скрипте демонтажа оставляет на хосте правила, которых никто не писал. В самой возможности ничего не изменилось; изменился контекст. Поэтому один и тот же compose-файл ведёт себя по-разному на двух хостах.
Три предпосылки, сливающиеся в одну
Сбои запуска в контейнерах VPN обычно связаны с одной из трёх отдельных вещей: возможностью, туннельным устройством и поддержкой протокола ядром. Выдача всех трёх сразу работает, но и делает следующий сбой невозможным для отнесения. Минимальный фрагмент compose выдаёт возможность и явно передаёт узел устройства:
services:
vpn:
image: example/vpn-client:latest
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
sysctls:
- net.ipv4.conf.all.src_valid_mark=1Читайте документацию самого образа о том, какие sysctl ему нужны, потому что sysctl в контейнере может требовать дополнительной привилегии или настройки на уровне хоста. Там, где образ поддерживает только режим пользовательского пространства, устройство вообще не нужно, и список возможностей соответственно сокращается.
Выдавайте узкую операцию, а не режим
Привилегированный режим передаёт каждую возможность и одновременно расширяет доступ к устройствам, поэтому скрывает, какой из трёх предпосылок не хватало. Он также делает режимы отказа контейнера неотличимыми от изменений хоста. Предпочитайте явный список, даже если его дольше писать.
Развёртывания без root и в пространствах имён пользователя меняют практический смысл возможности, поскольку процесс может держать её только внутри отображённого пространства имён. Копирование списка возможностей из примера с root в среду без root и ожидание той же широты — распространённый способ потерять полдня.
Секреты относятся к отдельному решению. Наличие NET_ADMIN ничего не говорит о том, какую идентичность партнёра представляет клиент, какие учётные данные загружает и как они меняются. Сетевая администрация не даёт доверия.
Проверьте изменение и запишите, зачем существует выдача
Прежде чем отлаживать туннель, убедитесь, что контейнер действительно получил. Проверка ниже сообщает добавленные возможности и режим сети, а затем показывает, как список интерфейсов выглядит изнутри:
docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.NetworkMode}} {{.HostConfig.Privileged}}' vpn
docker exec vpn sh -c 'grep CapEff /proc/self/status'
docker exec vpn ip -br addr
docker exec vpn ip routeЕсли возможность присутствует, а список интерфейсов по-прежнему пуст, клиент отказал ещё до того, как смог что-либо создать, и следующее место для просмотра — журналы контейнера. Если туннель есть, но трафик не идёт, на следующий вопрос отвечает таблица маршрутизации внутри контейнера.
Проверьте и путь демонтажа, потому что клиент, пишущий правила межсетевого экрана, может оставить их после себя. Остановите контейнер и ещё раз проверьте набор правил хоста, если используется сеть хоста. Выдачу, которая нужна только две секунды при запуске, не следует описывать в compose-файле как постоянное требование без примечания, объясняющего это.
Запишите причину рядом с каждой возможностью: какая операция в ней нуждалась и что ломается без неё. Это примечание переживает обновления образа, и именно оно отличает проверенный список привилегий от списка, выросшего копированием и вставкой.