Добраться до веб-интерфейса внутри контейнера VPN: выберите владельца порта
Приложение делит пространство имён VPN, поэтому порт принадлежит контейнеру VPN. Правильно привяжите слушатель, публикуйте на владельце и держите путь узким.
С приложением всё в порядке, и с туннелем всё в порядке, а панель всё равно не загружается. Когда служба работает с network_mode: service:vpn, она делит сетевое пространство имён контейнера VPN, то есть её слушатель и опубликованные порты принадлежат этому пространству имён, а не службе. Отсюда следуют три решения: где привязывается приложение, какой контейнер публикует порт и насколько узок путь управления. Расставьте их в неверном порядке, и каждая отдельная настройка выглядит правильной, хотя сочетание работать не может.
Кому принадлежит пространство имён порта
В общем пространстве имён добавление записи ports: зависимой службе ничего полезного не даёт, а несколько сетевых параметров для неё не поддерживаются по замыслу. Публикуйте вместо этого на контейнере, которому принадлежит пространство имён. Этот же контейнер — тот, чей адрес появляется на хосте, поэтому сопоставление читается как одна запись, а не одна на каждую службу.
services:
vpn:
image: vpn-client-image
cap_add: [NET_ADMIN]
ports:
- "127.0.0.1:8080:8080"
app:
network_mode: "service:vpn"
Обратите внимание на адрес слева от сопоставления. Привязка к 127.0.0.1 оставляет опубликованный порт на loopback хоста, что обычно и нужно интерфейсу управления. Привязка к 0.0.0.0 открывает его на всех интерфейсах хоста, и это другое решение по безопасности, а не удобство.
Заставьте приложение слушать там, где пространство имён этого ждёт
Слушатель, привязанный к 127.0.0.1 внутри пространства имён, доступен только изнутри этого пространства. Опубликованные порты хоста приходят снаружи, поэтому приложение должно привязать более широкий адрес, чтобы сопоставление работало. На большинстве серверов это правка в одну строку, и именно этот шаг пропускают после копирования руководства по усилению, написанного для развёртывания без контейнеров.
docker compose exec app ss -lntp
docker port vpn
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
Список слушателей внутри приложения говорит правду об адресе привязки, а docker port показывает, что владелец действительно опубликовал. Если первый показывает 127.0.0.1, а второй показывает сопоставление на хосте, вы нашли несоответствие, не трогая конфигурацию VPN.
Выберите, как входит путь управления
Есть три разумных варианта. Публикуйте на контейнере VPN и добирайтесь до него с хоста или из локальной сети. Поставьте обратный прокси перед пространством имён и отдайте прокси единственный открытый порт. Либо держите панель доступной только изнутри пространства имён через сеанс exec или туннель SSH, что даёт наименьшее раскрытие и наименьшие удобства.
Какой подойдёт, зависит от того, кому и откуда нужен интерфейс. Одному администратору на том же хосте достаточно привязки к loopback. Команде, которая добирается через VPN, нужен продуманный путь и названный владелец. Выбор варианта с прокси даёт TLS и журналы доступа, но требует ещё одного компонента, который нужно поддерживать в актуальном состоянии.
Откройте порт управления, не открывая всё
Собственная политика локального доступа или блокировки VPN-клиента может отбросить входящий запрос управления, даже когда исходящий туннель работает безупречно. Разрешите одно документированное исключение для нужного источника и порта, затем подтвердите, что обычная защита туннеля не изменилась. Отключение аварийного выключателя ради загрузки панели меняет реальный контроль на вкладку браузера.
Оставьте собственную аутентификацию приложения включённой. Нахождение внутри стека, подключённого к VPN, не даёт прав каждому локальному пользователю и каждому устройству, которое может добраться до хоста, а панель администратора без аутентификации за туннелем остаётся панелью без аутентификации.
Проверьте с устройства управления, а затем проверьте ещё раз позже
Проверяйте с машины, которая действительно будет управлять службой, а не с хоста, затем отдельно проверьте требуемый приложению путь в интернет. Успех в управлении доказывает одно направление и ничего не говорит об исходящем трафике.
Проверьте снова после пересоздания стека. Заменённый владелец сети меняет пространство имён, к которому подключается приложение, и URL, работавший до обновления, после него может никуда не указывать. Запишите опубликованное сопоставление и адрес привязки, чтобы следующая пересборка была двухминутной проверкой, а не повторным расследованием.