Публикация портов Docker и источники VPN: два совпадения, которые вы упускаете
IP-адрес VPN-сервера не всегда тот источник, который видит Docker, а порт после DNAT — не опубликованный. Сопоставьте оба факта с реальным путём.
Правило велит разрешить адрес VPN-сервера к опубликованному сервису, а соединение всё равно сбрасывается. Такой результат обычен, и это не ошибка Docker. Два отдельных совпадения неверны: источник, который вы записали, может не быть источником, который получает хост, а порт, который вы записали, может не быть портом, который видит фильтр. Чтобы исправить оба, нужно смотреть на путь пакета, а не на схему сервиса.
Миф: разрешите VPN-сервер и готово
Маршрутизируемый VPN может сохранять адрес клиентского туннеля вплоть до хоста Docker. Шлюз, выполняющий трансляцию источника, может заменить этот адрес своим. Обе схемы законны, и они дают разные источники для одного и того же действия пользователя.
Значит, фраза IP-адрес VPN-сервера описывает предположение, а не наблюдение. Пока вы не знаете, какая из двух схем перед вами, правило остаётся догадкой.
Источник: пул клиентов или транслирующий шлюз
Проверьте четыре вещи по порядку. Во-первых, пул адресов клиентов, например 10.90.0.0/24. Во-вторых, транслирует ли шлюз этот пул до того, как он достигнет хоста. В-третьих, какой источник хост действительно наблюдает для одного нового соединения. В-четвёртых, различает ли аутентификация приложения отдельных пользователей, ведь общий транслированный адрес этого не может.
Если пул сохраняется, правило, совпадающее только с публичным адресом шлюза, пропускает каждого клиента. Если шлюз транслирует намеренно, разрешение транслированного адреса впускает каждого пользователя за ним, что может быть приемлемо для сервиса со своим входом и неприемлемо для всего остального.
Порт: DNAT меняет число, с которым вы совпадаете
При обычной публикации через мост Linux с Docker iptables сопоставление хоста с 8443 на порт контейнера 443 транслируется до запуска фильтра пересылки. Пакет приходит к фильтру с портом назначения 443. Правило, совпадающее с 8443 в этот момент, никогда не срабатывает, хотя клиент ввёл именно 8443.
Опубликованный порт по-прежнему существует в отслеживании соединений, поэтому исходное назначение можно сопоставить там, когда это действительно нужно.
Решите, какое число выражает намерение. Сопоставляйте порт контейнера после DNAT, когда правило защищает сервис внутри контейнера. Сопоставляйте исходное назначение с опцией conntrack, когда правило должно описывать, что запросил клиент, и примите дополнительные затраты на сопоставление и аккуратность с порядком правил, которые с этим приходят.
Цепочка: DOCKER-USER, а не INPUT
Руководство Docker по iptables помещает политику пересылки пользователя в цепочку DOCKER-USER, которая оценивается до собственных правил пересылки Docker. Правило, скопированное из службы хоста в цепочку INPUT, фильтрует трафик, адресованный хосту, а не трафик, пересылаемый в контейнер. Эта ошибка даёт правило, которое выглядит правильным и не влияет на путь контейнера.
Держите диапазон источника, действующий порт назначения, протокол и входной интерфейс вместе в одном правиле. Разрешение только по источнику для каждого пересылаемого назначения шире, чем разрешение одного опубликованного сервиса.
Проверяйте новыми соединениями
Проверьте оба направления новыми соединениями: один разрешённый клиент VPN и один источник, который должен остаться запрещённым. Существующее состояние соединения может поддерживать сеанс живым после изменения политики, поэтому старое соединение ничего не доказывает о новом правиле. Затем проверьте путь ответа, потому что контейнер всё ещё должен маршрутизировать обратно к тому источнику, который он видит.
Порядок цепочек и поведение трансляции портов за этими проверками описаны в руководстве Docker по iptables. Прочитайте его, прежде чем копировать рецепт, и подтвердите, какой backend использует установленный Docker, потому что backend nftables интегрирует политику иначе.