[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"/ru/kb/how-to/vps/docker-bez-ipv4-natKbData":3},{"page":4,"pages":134,"recommendedPages":135,"category":70,"categoryTree":70,"total":16,"currentOrder":163,"activeManualCategoryId":70,"tag":-1},{"route":5,"uuid":8,"title":9,"excerpt":10,"content":11,"view_count":12,"published_at":13,"modified_at":14,"like":15,"category":18,"hero_image":63,"seo_metadata":64,"schema_org_metadata":68,"open_graph_metadata":71,"sections":72,"tags":125,"breadcrumbs":126,"images":133},{"language":6,"path":7},"ru","/kb/how-to/vps/docker-bez-ipv4-nat","f0ec4594-6db5-3693-7f2f-2c6be1be8c62","Docker без IPv4-NAT: выдаем контейнерам глобальные IPv6-адреса из /64","В этой статье мы разберем, как выдать контейнерам Docker публичные адреса IPv6 из подсети, выделенной виртуальному серверу, чтобы каждый сервис работал на собственном адресе и стандартном порту без трансляции адресов.","\n\u003Cp>В этой статье мы разберем, как выдать контейнерам Docker публичные адреса IPv6 из подсети, выделенной виртуальному серверу, чтобы каждый сервис работал на собственном адресе и стандартном порту без трансляции адресов. Также мы рассмотрим, как распределить подсеть между сетями Docker, как защитить контейнеры правилами фильтрации и как проверить результат с внешнего устройства.&nbsp;\u003C/p>\n\n\n\n\u003Ch2 id=\"naznachenie\">Назначение\u003C/h2>\n\n\n\n\u003Cp>Каждому виртуальному серверу может быть выделена подсеть IPv6 размером \u003Ccode>/64\u003C/code>. Это 18 квинтиллионов адресов, поэтому собственный публичный адрес можно выдать каждому контейнеру Docker.\u003C/p>\n\n\n\n\u003Cp>Настройка решает задачу разделения сервисов. При работе только по IPv4 контейнеры делят один публичный адрес: занять порт 443 может лишь один сервис, остальные переносятся на нестандартные порты или размещаются за реверс-прокси. С подсетью IPv6 каждый контейнер получает собственный адрес и слушает стандартный порт, а трансляция адресов и проброс портов не используются.\u003C/p>\n\n\n\n\u003Cp>IPv6 не заменяет IPv4: часть пользователей интернета подключается только по нему, поэтому публичные сайты обычно должны оставаться доступны по обоим протоколам. Описанная схема полезна там, где собственный адрес у контейнера Docker упрощает работу: при разделении сервисов между собой, доступе к служебным интерфейсам и тестовым стендам, взаимодействии с системами, которые уже работают по IPv6.&nbsp;\u003C/p>\n\n\n\n\u003Ch2 id=\"princip-raboty\">Принцип работы\u003C/h2>\n\n\n\n\u003Cp>От привычной работы Docker схема отличается тем, как контейнер обменивается трафиком с интернетом. Ниже разобраны оба режима – знание этой разницы понадобится при создании сетей и запуске контейнеров.\u003C/p>\n\n\n\n\u003Cp>При работе по IPv4 контейнер получает внутренний адрес вида \u003Ccode>172.17.0.2\u003C/code>, недоступный из интернета. Команда \u003Ccode>-p 8080:80\u003C/code> создает правило трансляции: запросы на порт \u003Ccode>8080\u003C/code> адреса сервера перенаправляются на порт \u003Ccode>80\u003C/code> контейнера. Исходящие соединения контейнера подменяются адресом сервера. Такой режим называется \u003Ccode>NAT\u003C/code> и включен по умолчанию.\u003C/p>\n\n\n\n\u003Cp>При работе по IPv6 контейнер получает адрес из вашей подсети. Этот адрес маршрутизируется в интернете так же, как адрес сервера, поэтому перенаправление не требуется: запрос приходит напрямую на адрес контейнера и на его порт. Такой режим называется \u003Ccode>routed\u003C/code>.\u003C/p>\n\n\n\n\u003Cp>Режим задается отдельно для каждого протокола, поэтому в одной сети адрес IPv6 может работать в режиме \u003Ccode>routed\u003C/code>, а IPv4 – в режиме \u003Ccode>NAT\u003C/code>. Меняется при этом и смысл публикации порта: в режиме \u003Ccode>routed\u003C/code> она не перенаправляет порт, а разрешает доступ к нему, и порт сервера не используется.\u003C/p>\n\n\n\n\u003Cp>Обе настройки выполняются штатными командами Docker: режим задается при создании сети, публикация порта – при запуске контейнера. Как именно, описано в разделах «Создание сетей» и «Запуск контейнеров».\u003C/p>\n\n\n\n\u003Ch2 id=\"podklyuchenie-podseti\">Подключение подсети\u003C/h2>\n\n\n\n\u003Cp>Подсеть подключается в панели управления, в настройках виртуального сервера. После подключения первый адрес подсети назначается на публичный интерфейс автоматически.\u003C/p>\n\n\n\n\u003Cp>Проверьте, что адрес назначен:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 addr show dev eth0\n2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 state UP qlen 1000\n    inet6 2001:db8:1a2b:3c4d::1/64 scope global\n       valid_lft forever preferred_lft forever\n    inet6 fe80::249:1fff:fe68:18b3/64 scope link\n       valid_lft forever preferred_lft forever\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Адрес \u003Ccode>2001:db8:1a2b:3c4d::1/64\u003C/code> – первый адрес выделенной подсети. Здесь и далее в примерах используется подсеть \u003Ccode>2001:db8:1a2b:3c4d::/64\u003C/code>, подставляйте собственную. Адрес, начинающийся с \u003Ccode>fe80:\u003C/code>, – служебный, он есть у каждого интерфейса и в настройке не участвует.\u003C/p>\n\n\n\n\u003Cp>Проверьте маршрут по умолчанию и связность:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 route show default\ndefault via fe80::1 dev eth0 metric 1024 onlink pref medium\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ping -6 -c3 -n ipv6.google.com\nPING ipv6.google.com (2a00:1450:4008:803::200e) 56 data bytes\n64 bytes from 2a00:1450:4008:803::200e: icmp_seq=1 ttl=115 time=27.0 ms\n64 bytes from 2a00:1450:4008:803::200e: icmp_seq=2 ttl=115 time=26.3 ms\n64 bytes from 2a00:1450:4008:803::200e: icmp_seq=3 ttl=115 time=26.5 ms\n--- ipv6.google.com ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2002ms\nrtt min/avg/max/mdev = 26.294/26.613/27.011/0.297 ms\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Ключ \u003Ccode>-n\u003C/code> отключает разрешение имен: в выводе остаются только адреса.\u003C/p>\n\n\n\n\u003Cp>Первый адрес подсети должен постоянно оставаться на публичном интерфейсе. Без него перестает работать вся подсеть, а не только сам адрес. Отсюда три ограничения:\u003C/p>\n\n\n\n\u003Cul>\u003Cli>префикс \u003Ccode>/64\u003C/code> у этого адреса не изменяется;\u003C/li>\u003Cli>адрес не переносится на сетевой мост Docker;\u003C/li>\u003Cli>адрес не удаляется и не заменяется другим адресом подсети.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>По этой причине неприменим прием, встречающийся в руководствах по маршрутизируемому IPv6, – передать всю подсеть \u003Ccode>/64\u003C/code> одному мосту Docker. Такие руководства рассчитаны на конфигурации, где адрес сервера находится за пределами подсети.\u003C/p>\n\n\n\n\u003Ch2 id=\"kak-zapisyvaetsya-adres-ipv6\">Как записывается адрес IPv6\u003C/h2>\n\n\n\n\u003Cp>Адрес состоит из 128 бит и записывается восемью группами по четыре шестнадцатеричные цифры, разделенными двоеточиями. Одна группа – это 16 бит; такую группу также называют хекстетом. В полной записи первый адрес подсети из примера выглядит так:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>2001:0db8:1a2b:3c4d:0000:0000:0000:0001\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Запись \u003Ccode>/64\u003C/code> означает, что первые 64 бита адреса – это первые четыре группы – определены при выделении подсети и не изменяются. Оставшиеся четыре группы находятся в вашем распоряжении.\u003C/p>\n\n\n\n\u003Cp>Сокращенная запись отбрасывает ведущие нули в группах, а одну последовательность нулевых групп заменяет на два двоеточия. Поэтому тот же адрес записывается как \u003Ccode>2001:db8:1a2b:3c4d::1\u003C/code>.\u003C/p>\n\n\n\n\u003Ch2 id=\"raspredelenie-podsetey\">Распределение подсетей\u003C/h2>\n\n\n\n\u003Cp>Свою часть адреса удобно делить по границе группы. Если зафиксировать пятую группу, получится подсеть \u003Ccode>/80\u003C/code>: 64 бита выделенной подсети и 16 бит пятой группы.\u003C/p>\n\n\n\n\u003Cp>Пятая группа принимает значения от \u003Ccode>0000\u003C/code> до \u003Ccode>ffff\u003C/code>. Таким образом, из подсети \u003Ccode>/64 \u003C/code>получается 65 536 подсетей \u003Ccode>/80\u003C/code>, и пятая группа выполняет роль номера сети. Это основной принцип разметки, он не зависит от того, какая подсеть выделена вашему серверу.\u003C/p>\n\n\n\n\u003Cp>Номер \u003Ccode>0000\u003C/code> использовать нельзя: подсеть \u003Ccode>2001:db8:1a2b:3c4d::/80\u003C/code> содержит первый адрес, закрепленный за сервером. Нумерация начинается с единицы.\u003C/p>\n\n\n\n\u003Cp>Сети создаются не под отдельный контейнер, а под группу контейнеров с одинаковыми требованиями к доступу извне. Количество сетей определяется количеством таких групп, а не количеством контейнеров. Для типового проекта достаточно трех. Например:\u003C/p>\n\n\n\n\u003Cul>\u003Cli>\u003Ccode>2001:db8:1a2b:3c4d:1::/80\u003C/code> – сеть web, сервисы, доступные из интернета;\u003C/li>\u003Cli>\u003Ccode>2001:db8:1a2b:3c4d:2::/80\u003C/code> – сеть app, приложения без прямого доступа извне;\u003C/li>\u003Cli>\u003Ccode>2001:db8:1a2b:3c4d:3::/80\u003C/code> – сеть data, базы данных и кеши, изолированная сеть.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>Адреса контейнерам назначаются вручную, последней группой: \u003Ccode>2001:db8:1a2b:3c4d:1::10\u003C/code>, далее \u003Ccode>…:1::20\u003C/code>, \u003Ccode>…:1::30\u003C/code>. Значения произвольны, но их следует записывать: на эти адреса указывают записи DNS и правила фильтрации.\u003C/p>\n\n\n\n\u003Cp>Разметку удобно хранить в документации проекта. Дальнейшие примеры используют приведенный вариант.\u003C/p>\n\n\n\n\u003Ch2 id=\"podgotovka-servera\">Подготовка сервера\u003C/h2>\n\n\n\n\u003Cp>Чтобы сервер передавал пакеты между публичным интерфейсом и сетевыми мостами Docker, включается маршрутизация IPv6. Docker задает этот параметр самостоятельно при создании сетей, но указать его явно надежнее.\u003C/p>\n\n\n\n\u003Cp>Создайте файл конфигурации:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>cat &gt; /etc/sysctl.d/99-docker-ipv6.conf &lt;&lt;'EOF'\nnet.ipv6.conf.all.forwarding = 1\nnet.ipv6.conf.default.forwarding = 1\nEOF\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Примените параметры:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>sysctl --system\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте значения:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>sysctl net.ipv6.conf.all.forwarding\nnet.ipv6.conf.all.forwarding = 1\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Убедитесь, что маршрут по умолчанию сохранился:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 route show default\ndefault via fe80::1 dev eth0 metric 1024 onlink pref medium\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Ch2 id=\"nastroyka-docker\">Настройка Docker\u003C/h2>\n\n\n\n\u003Cp>Статья рассчитана на готовое решение \u003Ca href=\"https://beget.com/ru/cloud/marketplace/docker\">Docker\u003C/a>: в него входят Ubuntu 24.04, Docker CE и Docker Compose. Проверьте версию на своем сервере:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker version --format '{{.Server.Version}}'\n29.8.0\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-alert alert-block alert-block-info\">\u003Cdiv class=\"alert-block-title\">Обратите внимание!\u003C/div>\u003Cdiv class=\"alert-block-content\">В версиях до 28.0 контейнер, до адреса которого есть маршрут извне, мог быть доступен на всех портах, которые слушает, независимо от публикации. Начиная с 28.0 снаружи доступны только опубликованные порты. Руководства, написанные раньше, исходят из прежнего поведения, поэтому переносить их команды без изменений не следует.\u003C/div>\u003C/div>\n\n\n\n\u003Cp>Поведение Docker настраивается в файле \u003Ccode>/etc/docker/daemon.json\u003C/code>. Полный перечень параметров приведен в документации Docker, в разделе о конфигурации демона. Для описанной схемы достаточно одного параметра:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>cat /etc/docker/daemon.json\n{\n  \"ip6tables\": true\n}\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Перезапустите Docker:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>systemctl restart docker\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте, что цепочка правил фильтрации создана:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip6tables -L DOCKER-USER -n\nChain DOCKER-USER (1 references)\ntarget     prot opt source               destination\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Цепочка пустая – это нормально, правила в нее добавляются в разделе «Правила фильтрации».\u003C/p>\n\n\n\n\u003Cp>Параметр \u003Ccode>ip6tables\u003C/code> включает управление правилами фильтрации IPv6. В текущих версиях он включен по умолчанию, но указывается явно: при выключенном параметре сети остаются без фильтрации и все порты всех контейнеров становятся доступны из интернета.\u003C/p>\n\n\n\n\u003Cp>Еще три параметра из документации Docker часто встречаются в руководствах по IPv6, но в описанной схеме не используются.\u003C/p>\n\n\n\n\u003Cp>Параметры \u003Ccode>ipv6\u003C/code> и \u003Ccode>fixed-cidr-v6\u003C/code> включают IPv6 на стандартной сети \u003Ccode>bridge\u003C/code> – той, к которой подключаются контейнеры, запущенные без указания сети. Публичный адрес такому контейнеру не нужен. Сетям, которые создаются в следующем разделе, эти параметры не требуются.\u003C/p>\n\n\n\n\u003Cp>Параметр \u003Ccode>default-address-pools\u003C/code> задает диапазоны, из которых Docker выбирает подсети для сетей, созданных без указания подсети. В описанной схеме подсеть указывается всегда. При отсутствии пула сеть без указанной подсети получает служебный адрес из диапазона \u003Ccode>fd00::/8\u003C/code> – по такому адресу сразу видно, что подсеть не была задана.\u003C/p>\n\n\n\n\u003Cp>Если в другом руководстве встречается \u003Ccode>default-address-pools\u003C/code> с подстановкой собственной подсети, обратите внимание на значение \u003Ccode>base\u003C/code>. Пример в документации Docker рассчитан на подсеть \u003Ccode>/56\u003C/code>, из которой нарезаются подсети \u003Ccode>/64\u003C/code>. При переносе на \u003Ccode>/64\u003C/code> в \u003Ccode>base\u003C/code> попадает вся выделенная подсеть, и первая же автоматически созданная сеть получает подсеть \u003Ccode>2001:db8:1a2b:3c4d::/80\u003C/code> со шлюзом \u003Ccode>2001:db8:1a2b:3c4d::1\u003C/code>. Docker занимает адрес, закрепленный за сервером, и подсеть перестает работать.\u003C/p>\n\n\n\n\u003Ch2 id=\"sozdanie-setey\">Создание сетей\u003C/h2>\n\n\n\n\u003Cp>Сети создаются по разметке из раздела «Распределение подсетей». Каждой сети задаются подсеть IPv6, подсеть IPv4 для внутреннего взаимодействия и имя моста. Подставьте собственные подсети:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker network create web --ipv6 --subnet 2001:db8:1a2b:3c4d:1::/80 --subnet 172.30.1.0/24 -o com.docker.network.bridge.name=br-web -o com.docker.network.bridge.gateway_mode_ipv6=routed\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker network create app --ipv6 --subnet 2001:db8:1a2b:3c4d:2::/80 --subnet 172.30.2.0/24 -o com.docker.network.bridge.name=br-app -o com.docker.network.bridge.gateway_mode_ipv6=routed\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker network create data --internal --ipv6 --subnet 2001:db8:1a2b:3c4d:3::/80 --subnet 172.30.3.0/24 -o com.docker.network.bridge.name=br-data\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Назначение параметров:\u003C/p>\n\n\n\n\u003Cul>\u003Cli>\u003Ccode>--ipv6\u003C/code> – включает IPv6 в создаваемой сети;\u003C/li>\u003Cli>\u003Ccode>--subnet\u003C/code> – подсеть из вашей разметки; указывается дважды, для IPv6 и для IPv4;\u003C/li>\u003Cli>\u003Ccode>gateway_mode_ipv6=routed\u003C/code> – отключает трансляцию адресов для IPv6. Контейнер доступен извне под собственным адресом и на собственном порту, исходящие соединения устанавливаются с адреса контейнера. Для IPv4 сохраняется режим NAT;\u003C/li>\u003Cli>\u003Ccode>--internal\u003C/code> – сеть без выхода в интернет и недоступная извне. Применяется для баз данных, к которым обращаются только приложения из той же сети. Режим шлюза такой сети не задается;\u003C/li>\u003Cli>\u003Ccode>bridge.name\u003C/code> – имя сетевого моста. Без него Docker присваивает имена вида \u003Ccode>br-35e78414358b\u003C/code>, что затрудняет чтение маршрутов и правил фильтрации.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>Проверьте параметры созданной сети:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker network inspect web --format '{{json .Options}}'\n{\"com.docker.network.bridge.gateway_mode_ipv6\":\"routed\",\"com.docker.network.bridge.name\":\"br-web\"}\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте маршруты:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 route show | grep br-\n2001:db8:1a2b:3c4d:1::/80 dev br-web proto kernel metric 256 linkdown pref medium\n2001:db8:1a2b:3c4d:2::/80 dev br-app proto kernel metric 256 linkdown pref medium\n2001:db8:1a2b:3c4d:3::/80 dev br-data proto kernel metric 256 linkdown pref medium\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Каждой подсети соответствует свой маршрут. Пометка \u003Ccode>linkdown\u003C/code> означает, что в сети пока нет контейнеров, – после их запуска она исчезнет.\u003C/p>\n\n\n\n\u003Cp>Параметры существующей сети не изменяются. Чтобы изменить режим шлюза или подсеть, сеть удаляется и создается заново.\u003C/p>\n\n\n\n\u003Ch2 id=\"zapusk-konteynerov\">Запуск контейнеров\u003C/h2>\n\n\n\n\u003Cp>В примере используется контейнер \u003Ccode>traefik/whoami\u003C/code>: он отвечает на HTTP-запросы сведениями о себе и удобен для проверки. В своем проекте подставьте нужный образ, адрес из своей разметки и порты своего приложения.\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run -d --name whoami --restart unless-stopped --network web --ip6 2001:db8:1a2b:3c4d:1::10 -p '[::]::80' traefik/whoami\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Назначение параметров:\u003C/p>\n\n\n\n\u003Cul>\u003Cli>\u003Ccode>--restart unless-stopped\u003C/code> – контейнер запускается после перезагрузки сервера. Без этого параметра он остается остановленным;\u003C/li>\u003Cli>\u003Ccode>--network\u003C/code> – сеть из предыдущего раздела;\u003C/li>\u003Cli>\u003Ccode>--ip6\u003C/code> – закрепляет за контейнером конкретный адрес. Без закрепления адрес меняется при каждом пересоздании контейнера, а запись DNS остается прежней;\u003C/li>\u003Cli>\u003Ccode>-p '[::]::80'\u003C/code> – публикует порт контейнера по IPv6. Запись расшифровывается как \u003Ccode>[адрес хоста]:[порт хоста]:[порт контейнера]\u003C/code>. Порт хоста не указан, поскольку в режиме \u003Ccode>routed\u003C/code> для IPv6 он не используется: входящие подключения направляются непосредственно на IPv6-адрес контейнера.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>Значение \u003Ccode>-p 8080:80\u003C/code> дополнительно создаст проброс по IPv4 через порт \u003Ccode>8080\u003C/code> сервера.\u003C/p>\n\n\n\n\u003Cp>Проверьте, что контейнер запущен:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker ps\nCONTAINER ID   IMAGE            COMMAND     CREATED          STATUS         PORTS     NAMES\n519c362068b0   traefik/whoami   \"/whoami\"   11 minutes ago   Up 5 minutes             whoami\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте, как опубликован порт:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker inspect whoami --format '{{json .NetworkSettings.Ports}}'\n{\"80/tcp\":[{\"HostIp\":\"::\",\"HostPort\":\"\"}]}\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Пустое значение \u003Ccode>HostPort\u003C/code> подтверждает, что режим \u003Ccode>routed\u003C/code> применен: порт сервера не занят.\u003C/p>\n\n\n\n\u003Cp>Проверьте ответ сервиса с самого сервера:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>curl -s \"http://[2001:db8:1a2b:3c4d:1::10]/\" | head -3\nHostname: 519c362068b0\nIP: 127.0.0.1\nIP: ::1\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Ch2 id=\"proverka-raboty\">Проверка работы\u003C/h2>\n\n\n\n\u003Cp>Проверка выполняется с устройства, имеющего адрес IPv6.\u003C/p>\n\n\n\n\u003Cp>Проверьте доступность контейнера извне:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ping -6 -c3 2001:db8:1a2b:3c4d:1::10\n64 bytes from 2001:db8:1a2b:3c4d:1::10: icmp_seq=1 ttl=62 time=0.889 ms\n--- 2001:db8:1a2b:3c4d:1::10 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2061ms\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>curl -s \"http://[2001:db8:1a2b:3c4d:1::10]/\" | head -2\nHostname: 519c362068b0\nIP: 127.0.0.1\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Отдельно проверяется, с какого адреса контейнер устанавливает исходящие соединения. Запустите на проверочном устройстве наблюдение за трафиком:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>tcpdump -ni eth0 'icmp6 and host 2001:db8:1a2b:3c4d:1::60'\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>На сервере выполните команду, подставив адрес проверочного устройства:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run --rm --network web --ip6 2001:db8:1a2b:3c4d:1::60 alpine ping -6 -c3 2001:db8:c0ff:ee::1\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>В выводе \u003Ccode>tcpdump\u003C/code> на проверочном устройстве появится:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>IP6 2001:db8:1a2b:3c4d:1::60 &gt; 2001:db8:c0ff:ee::1: ICMP6, echo request, id 1, seq 0, length 64\nIP6 2001:db8:c0ff:ee::1 &gt; 2001:db8:1a2b:3c4d:1::60: ICMP6, echo reply, id 1, seq 0, length 64\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Отправитель – адрес контейнера. Если вместо него указан адрес сервера, применяется трансляция адресов и режим \u003Ccode>routed\u003C/code> не был задан.\u003C/p>\n\n\n\n\u003Cp>Выход контейнеров в Интернет:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run --rm --network web alpine ping -6 -c3 ipv6.google.com\nPING ipv6.google.com (2a00:1450:4008:803::200e): 56 data bytes\n64 bytes from 2a00:1450:4008:803::200e: seq=0 ttl=116 time=27.490 ms\n64 bytes from 2a00:1450:4008:803::200e: seq=1 ttl=116 time=26.846 ms\n64 bytes from 2a00:1450:4008:803::200e: seq=2 ttl=116 time=26.924 ms\n--- ipv6.google.com ping statistics ---\n3 packets transmitted, 3 packets received, 0% packet loss\nround-trip min/avg/max = 26.846/27.086/27.490 ms\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Ch2 id=\"zashchita-konteynerov\">Защита контейнеров\u003C/h2>\n\n\n\n\u003Cp>Контейнер с публичным адресом доступен из интернета напрямую. При работе через трансляцию адресов недоступность непроброшенных портов обеспечивалась самой схемой, здесь за это отвечают правила фильтрации.\u003C/p>\n\n\n\n\u003Cp>Начиная с версии 28.0 Docker блокирует доступ извне к неопубликованным портам контейнеров. Опубликованные порты остаются доступны, в режиме \u003Ccode>routed\u003C/code> – на адресе контейнера. Контейнеры из разных сетей не видят порты друг друга.\u003C/p>\n\n\n\n\u003Cp>Эта защита не покрывает распространенную ситуацию, когда порт публикуется во время отладки и остается опубликованным. Второй уровень настраивается вручную.\u003C/p>\n\n\n\n\u003Cp>\u003Ccode>UFW\u003C/code> для этого не подходит. Утилита обрабатывает трафик, адресованный самому серверу, – цепочки \u003Ccode>INPUT\u003C/code> и \u003Ccode>OUTPUT\u003C/code>. Пакеты к контейнерам проходят транзитом через цепочку FORWARD, куда Docker добавляет собственные правила. Правило вида \u003Ccode>ufw deny 5432\u003C/code> отображается в выводе \u003Ccode>ufw status\u003C/code> как действующее, но порт контейнера остается доступен. \u003Ccode>UFW\u003C/code> применяется для сервисов самого сервера: SSH, веб-сервера на хосте.\u003C/p>\n\n\n\n\u003Cp>Для контейнеров в документации Docker предусмотрена цепочка \u003Ccode>DOCKER-USER\u003C/code>. Она создана специально для пользовательских правил: Docker обрабатывает ее раньше собственных правил и не очищает при перезапуске.\u003C/p>\n\n\n\n\u003Ch2 id=\"pravila-filtracii\">Правила фильтрации\u003C/h2>\n\n\n\n\u003Cp>Правила размещаются в цепочке \u003Ccode>DOCKER-USER\u003C/code> и строятся по принципу разрешительного списка: явно разрешается то, что должно быть доступно извне, остальной трафик к вашей подсети отбрасывается. Набор ниже основан на рекомендациях документации Docker и подходит для большинства проектов – при переносе на свой проект изменяются только значения переменных и перечень разрешенных портов.\u003C/p>\n\n\n\n\u003Cp>Создайте скрипт, подставив собственную подсеть, подсеть сети web и имя публичного интерфейса:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>cat /usr/local/sbin/docker-ipv6-firewall.sh\n#!/bin/bash\nset -e\n\nPREFIX=\"2001:db8:1a2b:3c4d::/64\"\nWEB=\"2001:db8:1a2b:3c4d:1::/80\"\nWAN=\"eth0\"\n\n# очистить цепочку, чтобы правила не дублировались при повторном запуске\nip6tables -F DOCKER-USER\n# пропустить ответы на установленные соединения:\n# без этого правила контейнеры не получат ответы на собственные запросы\nip6tables -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN\n# пропустить ICMPv6: на нем работают поиск соседей и определение размера пакета\nip6tables -A DOCKER-USER -p ipv6-icmp -j RETURN\n# не обрабатывать трафик, пришедший не из интернета:\n# связь между контейнерами и обращения с самого сервера\nip6tables -A DOCKER-USER ! -i \"$WAN\" -j RETURN\n# разрешить нужные порты – этот блок вы изменяете при добавлении сервисов\nip6tables -A DOCKER-USER -i \"$WAN\" -d \"$WEB\" -p tcp --dport 80  -j RETURN\nip6tables -A DOCKER-USER -i \"$WAN\" -d \"$WEB\" -p tcp --dport 443 -j RETURN\n# отбросить остальной трафик к вашей подсети\nip6tables -A DOCKER-USER -i \"$WAN\" -d \"$PREFIX\" -j DROP\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Сделайте скрипт исполняемым:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>chmod +x /usr/local/sbin/docker-ipv6-firewall.sh\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Выполните скрипт – команда применяет правила к текущей конфигурации:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>/usr/local/sbin/docker-ipv6-firewall.sh\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте результат:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip6tables -L DOCKER-USER -n -v --line-numbers\nChain DOCKER-USER (1 references)\nnum   pkts bytes target     prot opt in     out    source   destination\n1        0     0 RETURN     0    --  *      *      ::/0     ::/0          ctstate RELATED,ESTABLISHED\n2        0     0 RETURN     58   --  *      *      ::/0     ::/0\n3        0     0 RETURN     0    --  !eth0  *      ::/0     ::/0\n4        0     0 RETURN     6    --  eth0   *      ::/0     2001:db8:1a2b:3c4d:1::/80  tcp dpt:80\n5        0     0 RETURN     6    --  eth0   *      ::/0     2001:db8:1a2b:3c4d:1::/80  tcp dpt:443\n6        0     0 DROP       0    --  eth0   *      ::/0     2001:db8:1a2b:3c4d::/64\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Шесть правил в том же порядке, что и в скрипте. Столбец \u003Ccode>pkts\u003C/code> показывает количество обработанных пакетов – он используется при проверке в разделе «Проверка защиты».\u003C/p>\n\n\n\n\u003Cp>Назначение правил:\u003C/p>\n\n\n\n\u003Cul>\u003Cli>\u003Cstrong>правило 1\u003C/strong> пропускает ответы на уже установленные соединения. Это единственный способ сопоставить трафик с исходным запросом: к моменту, когда пакет доходит до цепочки \u003Ccode>DOCKER-USER\u003C/code>, он уже прошел трансляцию адресов, и исходный адрес назначения в нем недоступен. Без этого правила контейнеры не получат ответы на собственные запросы;\u003C/li>\u003Cli>\u003Cstrong>правило 2\u003C/strong> пропускает ICMPv6. В IPv6 на этом протоколе работают поиск соседей – аналог ARP – и определение размера пакета на маршруте. При его блокировке возникают трудно диагностируемые сбои: небольшие страницы загружаются, а крупные файлы передаются не полностью;\u003C/li>\u003Cli>\u003Cstrong>правило 3\u003C/strong> возвращает без обработки трафик, пришедший не с публичного интерфейса. Это связь между контейнерами и обращения с самого сервера – ограничивать их не требуется;\u003C/li>\u003Cli>\u003Cstrong>правила 4 и 5\u003C/strong> разрешают порты \u003Ccode>80\u003C/code> и \u003Ccode>443\u003C/code> в сети \u003Ccode>web\u003C/code>. Этот блок вы изменяете при добавлении сервисов: адрес подсети берется из вашей разметки, порты – из требований приложения;\u003C/li>\u003Cli>\u003Cstrong>правило 6\u003C/strong> отбрасывает остальной трафик, адресованный вашей подсети. Именно оно закрывает порты, опубликованные по ошибке. Обратите внимание: правило ограничено вашей подсетью, поэтому транзитные пакеты и трафик к другим адресам под него не попадают.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>Порядок правил имеет значение: они просматриваются сверху вниз до первого совпадения. Поэтому в скрипте используется ключ \u003Ccode>-A\u003C/code>, добавляющий правило в конец цепочки. При использовании ключа \u003Ccode>-I\u003C/code>, вставляющего правило в начало, порядок оказался бы обратным и последнее правило отбросило бы весь трафик.\u003C/p>\n\n\n\n\u003Cp>Чтобы открыть доступ к новому сервису, добавьте строку в блок разрешенных портов и выполните скрипт повторно.\u003C/p>\n\n\n\n\u003Cp>Доступ можно ограничить по адресу источника. Например, панель администрирования на порту \u003Ccode>9000\u003C/code>, доступная только из вашей домашней сети:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip6tables -A DOCKER-USER -i eth0 -s 2001:db8:c0ff:ee::/64 -d 2001:db8:1a2b:3c4d:2::/80 -p tcp --dport 9000 -j RETURN\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Домашним подключениям обычно выделяется постоянная подсеть IPv6, поэтому такое правило не требует регулярного обновления.\u003C/p>\n\n\n\n\u003Ch2 id=\"sohranenie-pravil\">Сохранение правил\u003C/h2>\n\n\n\n\u003Cp>Правила цепочки \u003Ccode>DOCKER-USER\u003C/code> не сохраняются автоматически. Пакет \u003Ccode>iptables-persistent\u003C/code> для этой задачи не подходит: сохраненные правила восстанавливаются до запуска Docker, который затем пересоздает собственные цепочки. Скрипт должен выполняться после запуска Docker – для этого создается юнит \u003Ccode>systemd\u003C/code>:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>cat /etc/systemd/system/docker-ipv6-firewall.service\n[Unit]\nDescription=Custom DOCKER-USER rules for IPv6\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/local/sbin/docker-ipv6-firewall.sh\n\n[Install]\nWantedBy=multi-user.target\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Активируйте юнит:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>systemctl daemon-reload\nsystemctl enable --now docker-ipv6-firewall.service\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте состояние:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>systemctl status docker-ipv6-firewall --no-pager\n● docker-ipv6-firewall.service - Custom DOCKER-USER rules for IPv6\n     Loaded: loaded (/etc/systemd/system/docker-ipv6-firewall.service; enabled; preset: enabled)\n     Active: active (exited) since Tue 2026-09-15 10:36:57 UTC; 12s ago\n    Process: 1892 ExecStart=/usr/local/sbin/docker-ipv6-firewall.sh (code=exited, status=0/SUCCESS)\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Состояние \u003Ccode>active (exited)\u003C/code> и код \u003Ccode>0/SUCCESS\u003C/code> означают, что скрипт отработал без ошибок.\u003C/p>\n\n\n\n\u003Ch2 id=\"parametry-otklyuchayushchie-zashchitu\">Параметры, отключающие защиту\u003C/h2>\n\n\n\n\u003Cp>В документации Docker описаны параметры, упрощающие доступ к контейнерам извне. Они снимают фильтрацию и в описанной схеме не применяются.\u003C/p>\n\n\n\n\u003Cul>\u003Cli>\u003Ccode>allow-direct-routing\u003C/code> в \u003Ccode>daemon.json\u003C/code> – отключает фильтрацию пакетов, приходящих извне и адресованных напрямую контейнерам, для всех сетей сервера;\u003C/li>\u003Cli>\u003Ccode>gateway_mode_ipv6=nat-unprotected\u003C/code> – снимает фильтрацию портов для выбранной сети: из интернета становится доступен каждый порт, который слушают контейнеры этой сети;\u003C/li>\u003Cli>\u003Ccode>\"ip6tables\": false\u003C/code> – не даёт Docker создавать большую часть правил фильтрации.\u003C/li>\u003C/ul>\n\n\n\n\u003Cp>Если во время отладки требуется доступ к дополнительному порту, разрешите этот порт в скрипте.\u003C/p>\n\n\n\n\u003Ch2 id=\"proverka-zashchity\">Проверка защиты\u003C/h2>\n\n\n\n\u003Cp>Проверка выполняется только с внешнего устройства. С самого сервера результат недостоверен: порты контейнеров не отображаются в выводе \u003Ccode>ss -tlnp\u003C/code>, поскольку открыты не на адресе сервера.\u003C/p>\n\n\n\n\u003Cp>Запустите три проверочных контейнера: без публикации портов, с намеренно опубликованным лишним портом и в изолированной сети.\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run -d --name redis-closed --restart unless-stopped --network web --ip6 2001:db8:1a2b:3c4d:1::50 redis:7\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run -d --name redis-open --restart unless-stopped --network web --ip6 2001:db8:1a2b:3c4d:1::51 -p '[::]::6379' redis:7\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker run -d --name pg-test --restart unless-stopped --network data --ip6 2001:db8:1a2b:3c4d:3::10 -e POSTGRES_PASSWORD=testonly postgres:16\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Убедитесь, что все контейнеры находятся в состоянии \u003Ccode>Up\u003C/code>:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker ps\nCONTAINER ID   IMAGE            COMMAND                  CREATED          STATUS          PORTS      NAMES\n463586d2e2df   postgres:16      \"docker-entrypoint.s…\"   6 seconds ago    Up 5 seconds    5432/tcp   pg-test\needc942e94de   redis:7          \"docker-entrypoint.s…\"   53 seconds ago   Up 52 seconds              redis-open\naaebd40b5794   redis:7          \"docker-entrypoint.s…\"   1 minute ago     Up 1 minute     6379/tcp   redis-closed\n519c362068b0   traefik/whoami   \"/whoami\"                1 minute ago     Up 1 minute                whoami\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Если контейнер не запустился, сканирование покажет закрытые порты из-за отсутствия сервиса по адресу, и результат будет истолкован неверно.\u003C/p>\n\n\n\n\u003Cp>Обнулите счетчики правил – повторный запуск скрипта очищает цепочку и добавляет правила заново:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>/usr/local/sbin/docker-ipv6-firewall.sh\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>На проверочном устройстве установите nmap:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>apt install -y nmap\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте сервис, который должен быть доступен:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>nmap -6 -Pn -p 80,443,6379 2001:db8:1a2b:3c4d:1::10\nNmap scan report for 2001:db8:1a2b:3c4d:1::10\nHost is up (0.00097s latency).\nPORT     STATE    SERVICE\n80/tcp   open     http\n443/tcp  filtered https\n6379/tcp filtered redis\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Открыт только порт \u003Ccode>80\u003C/code>. Порт \u003Ccode>443\u003C/code> разрешен правилами, но его не публиковал ни один контейнер, поэтому доступ заблокирован средствами Docker. Уровней защиты два, и разрешение в правилах само по себе порт не открывает.\u003C/p>\n\n\n\n\u003Cp>Проверьте контейнер без публикации портов:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>nmap -6 -Pn -p 80,443,6379 2001:db8:1a2b:3c4d:1::50\nNmap scan report for 2001:db8:1a2b:3c4d:1::50\nHost is up.\nPORT     STATE    SERVICE\n80/tcp   filtered http\n443/tcp  filtered https\n6379/tcp filtered redis\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте контейнер с опубликованным портом \u003Ccode>6379\u003C/code>:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>nmap -6 -Pn -p 80,443,6379 2001:db8:1a2b:3c4d:1::51\nNmap scan report for 2001:db8:1a2b:3c4d:1::51\nHost is up.\nPORT     STATE    SERVICE\n80/tcp   filtered http\n443/tcp  filtered https\n6379/tcp filtered redis\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Это основная проверка. Порт \u003Ccode>6379\u003C/code> опубликован, Docker его открыл, и недоступным он остается только благодаря правилам фильтрации. Так воспроизводится ситуация, когда порт публикуется при отладке и остается опубликованным.\u003C/p>\n\n\n\n\u003Cp>Проверьте контейнер в изолированной сети:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>nmap -6 -Pn -p 5432 2001:db8:1a2b:3c4d:3::10\nNmap scan report for 2001:db8:1a2b:3c4d:3::10\nHost is up.\nPORT     STATE    SERVICE\n5432/tcp filtered postgresql\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Состояние \u003Ccode>filtered\u003C/code>, в отличие от \u003Ccode>closed\u003C/code>, означает, что пакет отброшен без ответа.\u003C/p>\n\n\n\n\u003Cp>Сразу после сканирования посмотрите счетчики на сервере:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip6tables -L DOCKER-USER -n -v --line-numbers\nnum   pkts bytes target     prot opt in     out    source   destination\n1       11  1237 RETURN     0    --  *      *      ::/0     ::/0          ctstate RELATED,ESTABLISHED\n2        0     0 RETURN     58   --  *      *      ::/0     ::/0\n3        0     0 RETURN     0    --  !eth0  *      ::/0     ::/0\n4        6   400 RETURN     6    --  eth0   *      ::/0     2001:db8:1a2b:3c4d:1::/80  tcp dpt:80\n5        6   384 RETURN     6    --  eth0   *      ::/0     2001:db8:1a2b:3c4d:1::/80  tcp dpt:443\n6        8   512 DROP       0    --  eth0   *      ::/0     2001:db8:1a2b:3c4d::/64\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Восемь пакетов в шестом правиле – обращения к портам \u003Ccode>6379\u003C/code> и \u003Ccode>5432\u003C/code>, каждое из которых сканер повторил из-за отсутствия ответа. Шесть пакетов в четвертом правиле – сканирование порта \u003Ccode>80\u003C/code> по трем адресам. Одиннадцать пакетов в первом правиле – обратный трафик.\u003C/p>\n\n\n\n\u003Cp>Ненулевой счетчик шестого правила подтверждает, что порты закрыты правилами, а не отсутствием сервиса по адресу. При нулевом значении проверку следует повторить.\u003C/p>\n\n\n\n\u003Cp>Изоляция внутренней сети проверяется изнутри контейнера:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker exec pg-test sh -c 'timeout 5 getent hosts ipv6.google.com' || echo \"изоляция работает\"\nизоляция работает\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Ch2 id=\"proverka-posle-perezagruzki\">Проверка после перезагрузки\u003C/h2>\n\n\n\n\u003Cp>Перезагрузите сервер командой \u003Ccode>reboot\u003C/code> или через панель управления, раздел управления виртуальным сервером.\u003C/p>\n\n\n\n\u003Cp>После подключения убедитесь, что контейнеры запустились:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker ps\nCONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS              PORTS      NAMES\n463586d2e2df   postgres:16      \"docker-entrypoint.s…\"   6 minutes ago   Up About a minute   5432/tcp   pg-test\needc942e94de   redis:7          \"docker-entrypoint.s…\"   6 minutes ago   Up About a minute              redis-open\naaebd40b5794   redis:7          \"docker-entrypoint.s…\"   7 minutes ago   Up About a minute   6379/tcp   redis-closed\n519c362068b0   traefik/whoami   \"/whoami\"                7 minutes ago   Up About a minute              whoami\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Проверьте, что правила восстановлены:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip6tables -L DOCKER-USER -n | wc -l\n8\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Восемь строк – это заголовок цепочки, строка с названиями столбцов и шесть правил. Счетчики после перезагрузки нулевые.\u003C/p>\n\n\n\n\u003Cp>Проверьте, что первый адрес подсети на месте и мосты пересозданы:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 addr show dev eth0 | grep global\n    inet6 2001:db8:1a2b:3c4d::1/64 scope global\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>ip -6 route show | grep br-\n2001:db8:1a2b:3c4d:1::/80 dev br-web proto kernel metric 256 pref medium\n2001:db8:1a2b:3c4d:2::/80 dev br-app proto kernel metric 256 linkdown pref medium\n2001:db8:1a2b:3c4d:3::/80 dev br-data proto kernel metric 256 pref medium\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Пометка \u003Ccode>linkdown\u003C/code> осталась только у сети \u003Ccode>app\u003C/code>, в которой нет контейнеров.\u003C/p>\n\n\n\n\u003Cp>Повторите сканирование с проверочного устройства и сравните результат с предыдущим – он должен совпасть. После этого удалите проверочные контейнеры:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker rm -f redis-closed redis-open pg-test\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Список адресов работающих контейнеров выводится командой:\u003C/p>\n\n\n\n\u003Cdiv class=\"wp-block-beget-code code-block\">\u003Cpre data-options=\"{&quot;mode&quot;:&quot;&quot;}\">\u003Ccode>docker ps -q | xargs -I{} docker inspect {} --format '{{.Name}}{{range .NetworkSettings.Networks}} {{.GlobalIPv6Address}}{{end}}'\n/whoami 2001:db8:1a2b:3c4d:1::10\u003C/code>\u003C/pre>\u003C/div>\n\n\n\n\u003Cp>Периодическое сканирование этих адресов позволяет вовремя обнаружить порт, открытый при отладке и оставшийся открытым.\u003C/p>\n\n\n\n\u003Ch2 id=\"chastye-voprosy\">Частые вопросы\u003C/h2>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Перестала работать вся подсеть\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>Проверьте, находится ли первый адрес подсети на публичном интерфейсе: \u003Ccode>ip -6 addr show dev eth0\u003C/code>. Без него подсеть не работает целиком. Чаще всего адрес теряется при попытке передать подсеть мосту Docker или изменить префикс адреса.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Новая сеть получила подсеть, шлюзом которой стал первый адрес подсети\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>В параметре \u003Ccode>default-address-pools\u003C/code> указана вся выделенная подсеть. Уберите пул IPv6 из \u003Ccode>daemon.json\u003C/code> и указывайте подсеть каждой сети явно.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Контейнер получил адрес, начинающийся с fd00\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>Сеть создана без указания подсети. Укажите параметр \u003Ccode>--subnet\u003C/code>; существующую сеть потребуется пересоздать.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Маршрут сети помечен linkdown\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>В сети нет запущенных контейнеров. После запуска первого контейнера пометка исчезает.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>После перезагрузки сервисы недоступны, хотя правила фильтрации есть\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>Контейнеры запущены без параметра \u003Ccode>--restart unless-stopped\u003C/code>. При проверке защиты это особенно опасно: сканирование покажет закрытые порты, и результат можно принять за работающую фильтрацию.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Правило добавлено в UFW, но порт контейнера остается открытым\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>\u003Ccode>UFW\u003C/code> не обрабатывает транзитный трафик к контейнерам. Используйте цепочку \u003Ccode>DOCKER-USER\u003C/code>.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Команда ip6tables -L выводит пустые цепочки, хотя контейнеры работают\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>Либо в \u003Ccode>daemon.json\u003C/code> задано \u003Ccode>\"ip6tables\": false\u003C/code>, либо включен режим работы через \u003Ccode>nftables\u003C/code>. В последнем случае правила из \u003Ccode>DOCKER-USER\u003C/code> не применяются.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cdiv class=\"wp-block-beget-collapse collapse-block _has-background\">\u003Cdiv class=\"collapse-block__trigger\">\u003Cdiv>Сканирование показывает открытый порт базы данных.\u003C/div>\u003C/div>\u003Cdiv class=\"collapse-block__body-wrapper\">\u003Cdiv class=\"collapse-block__body\">\n\u003Cp>Порт опубликован явно, проверьте вывод \u003Ccode>docker inspect\u003C/code>. Уберите публикацию и подключите контейнер к сети, созданной с параметром \u003Ccode>--internal\u003C/code>.\u003C/p>\n\u003C/div>\u003C/div>\u003C/div>\n\n\n\n\u003Cp>Если возникнут вопросы, напишите нам, пожалуйста, тикет из панели управления аккаунта (раздел “\u003Ca href=\"https://cp.beget.com/support?utm_source=kb&amp;utm_medium=article&amp;utm_campaign=docker_without_ipv4_nat&amp;utm_content=cp_support\">Помощь и поддержка\u003C/a>”), а если вы захотите обсудить эту статью или наши продукты с коллегами по цеху и сотрудниками Beget – ждем вас в нашем \u003Ca href=\"https://t.me/beget_chat\">сообществе\u003C/a> в Telegram.\u003C/p>\n",34,"2026-09-23T13:53:38.000Z","2026-09-23T17:04:45.000Z",{"count":16,"liked":17},0,false,{"route":19,"title":21,"tags":22,"breadcrumbs":55,"uuid":56,"parent_uuid":57,"default_page_path":26,"seo_metadata":58},{"language":6,"path":20},"/kb/how-to/vps","VPS",[23,27,31,35,39,43,47,51],{"id":24,"title":21,"seo_metadata":25},"vps",{"title":26,"description":26,"header":26,"keywords":26},"",{"id":28,"title":29,"seo_metadata":30},"apache","Apache",{"title":26,"description":26,"header":26,"keywords":26},{"id":32,"title":33,"seo_metadata":34},"lets-encrypt","Let's Encrypt",{"title":26,"description":26,"header":26,"keywords":26},{"id":36,"title":37,"seo_metadata":38},"nginx","Nginx",{"title":26,"description":26,"header":26,"keywords":26},{"id":40,"title":41,"seo_metadata":42},"ssl","SSL",{"title":26,"description":26,"header":26,"keywords":26},{"id":44,"title":45,"seo_metadata":46},"vestacp","VestaCP",{"title":26,"description":26,"header":26,"keywords":26},{"id":48,"title":49,"seo_metadata":50},"perenos-sajta","Перенос сайта",{"title":26,"description":26,"header":26,"keywords":26},{"id":52,"title":53,"seo_metadata":54},"terminal","Терминал",{"title":26,"description":26,"header":26,"keywords":26},[],"957abe7d-4a90-1475-bfb0-6439507ed391","fa9a1354-631a-4206-df34-291f0db8f42a",{"title":59,"description":60,"header":61,"keywords":62},"VPS. Полезные статьи – Beget","Полезные статьи о VPS на сайте Beget","VPS. Полезные статьи","vps, вопрос ответ",{"src":26,"text":26},{"title":65,"description":66,"keywords":67},"Docker без IPv4-NAT: глобальные IPv6-адреса из /64","Рассказываем, как выдать глобальные IPv6-адреса из /64 | Настройка подключения Docker без IPv4-NAT","Docker IPv6, как настроить Docker для выдачи реальных (глобальных) IPv6-адресов контейнерам напрямую, без использования механизма NAT",{"faq":69,"how_to":70,"product":70},[],null,{},[73,77,80,83,86,89,92,95,98,101,104,107,110,113,116,119,122],{"level":74,"name":75,"anchor":76},1,"Назначение","naznachenie",{"level":74,"name":78,"anchor":79},"Принцип работы","princip-raboty",{"level":74,"name":81,"anchor":82},"Подключение подсети","podklyuchenie-podseti",{"level":74,"name":84,"anchor":85},"Как записывается адрес IPv6","kak-zapisyvaetsya-adres-ipv6",{"level":74,"name":87,"anchor":88},"Распределение подсетей","raspredelenie-podsetey",{"level":74,"name":90,"anchor":91},"Подготовка сервера","podgotovka-servera",{"level":74,"name":93,"anchor":94},"Настройка Docker","nastroyka-docker",{"level":74,"name":96,"anchor":97},"Создание сетей","sozdanie-setey",{"level":74,"name":99,"anchor":100},"Запуск контейнеров","zapusk-konteynerov",{"level":74,"name":102,"anchor":103},"Проверка работы","proverka-raboty",{"level":74,"name":105,"anchor":106},"Защита контейнеров","zashchita-konteynerov",{"level":74,"name":108,"anchor":109},"Правила фильтрации","pravila-filtracii",{"level":74,"name":111,"anchor":112},"Сохранение правил","sohranenie-pravil",{"level":74,"name":114,"anchor":115},"Параметры, отключающие защиту","parametry-otklyuchayushchie-zashchitu",{"level":74,"name":117,"anchor":118},"Проверка защиты","proverka-zashchity",{"level":74,"name":120,"anchor":121},"Проверка после перезагрузки","proverka-posle-perezagruzki",{"level":74,"name":123,"anchor":124},"Частые вопросы","chastye-voprosy",[],[127,131],{"route":128,"title":130},{"language":6,"path":129},"/kb/how-to","Полезные статьи",{"route":132,"title":21},{"language":6,"path":20},[],[],[136,145,154],{"route":137,"uuid":139,"title":140,"excerpt":26,"content":26,"view_count":16,"published_at":26,"modified_at":26,"like":70,"category":70,"hero_image":70,"seo_metadata":70,"schema_org_metadata":70,"open_graph_metadata":70,"sections":141,"tags":142,"breadcrumbs":143,"images":144},{"language":6,"path":138},"/kb/how-to/vps/kak-ustanovit-server-igry-terraria-linux","30ef21ff-ff33-23e9-1442-1ea89a70be9e","Установка сервера Terraria на Ubuntu",[],[],[],[],{"route":146,"uuid":148,"title":149,"excerpt":26,"content":26,"view_count":16,"published_at":26,"modified_at":26,"like":70,"category":70,"hero_image":70,"seo_metadata":70,"schema_org_metadata":70,"open_graph_metadata":70,"sections":150,"tags":151,"breadcrumbs":152,"images":153},{"language":6,"path":147},"/kb/how-to/vps/perenos-sajta-c-virtualnogo-hostinga-na-vps-c-pomoshhyu-ispmanager","df7823aa-4351-8143-3f92-010ea6090154","Перенос сайта c виртуального хостинга на VPS c помощью ispmanager",[],[],[],[],{"route":155,"uuid":157,"title":158,"excerpt":26,"content":26,"view_count":16,"published_at":26,"modified_at":26,"like":70,"category":70,"hero_image":70,"seo_metadata":70,"schema_org_metadata":70,"open_graph_metadata":70,"sections":159,"tags":160,"breadcrumbs":161,"images":162},{"language":6,"path":156},"/kb/how-to/vps/podklyuchenie-ipv6-k-sajtu-i-api-c-dostupom-ipv4","b2967b11-a4a7-8cd8-045c-e6af7a7137bd","Публикуем сайт и API по IPv6: dual stack без риска для текущих пользователей",[],[],[],[],"popularity-desc"]