Ви вмикаєте IPv6 у Docker, бо продукт потребує «реальних інтернет‑адрес», або тому що вичерпання IPv4 стало вашою проблемою.
Контейнери запускаються, більшість речей працює… і потім ви помічаєте, що вихідний трафік обходить ваш ретельно налаштований IPv4 NAT і контролі еґрезу.
Або ще гірше: сервіс доступний по IPv6 з місць, де він точно не мав би бути доступним.
IPv6 у Docker працює, але це не «вимкнути перемикач — і все добре». Потрібно вирішити: маршрутизований IPv6, NAT66 (так, він існує),
або «жодного глобального еґрезу, якщо це явно не дозволено». А потім — застосувати це через фаєрвол, який ви реально запускаєте, а не той, про який мрієте.
Що йде не так з Docker IPv6 (і чому це дивує досвідчених)
За замислом Docker «контейнери живуть за IPv4 NAT на мосту». Ви публікуєте порти — і вхід працює. Вихід теж працює.
Ви ставите кілька правил iptables і відчуваєте контроль.
IPv6 змінює угоду. Якщо хост має глобальний IPv6 і ви даєте глобальний (або глобально маршрутизований) IPv6 контейнерам, вони вже
не знаходяться автоматично за NAT. Це означає:
- Контроль еґрезу може мовчки провалитися. Ваші контролі лише для IPv4 не застосовуються до IPv6. Контейнер із задоволенням спілкується зі світом по v6.
- Вхідна експозиція може вас здивувати. Якщо випадково дозволено пересилання (або Docker вставляє занадто сприятливі правила), контейнери можуть бути доступні.
- Зникають видимість і логи. Flow‑логи, проксі‑логи і панелі «якою IP ми користувались» часто розраховані на IPv4.
- DNS може обійти політику. AAAA‑відповіді + Happy Eyeballs можуть обрати IPv6, навіть якщо ви «планували» IPv4.
Ще один нюанс: у Docker є кілька місць, де IPv6 можна «увімкнути», і вони не завжди означають одне й те саме.
Є рівень демона, IPv6 на кожну мережу, пул адрес, а також те, що фактично робить ядро хоста й фаєрвол.
Коли люди кажуть «IPv6 увімкнено», запитайте: «Увімкнено де і яка політика еґрезу?»
Факти та історичний контекст (щоб відрегулювати інстинкти)
Декілька коротких, конкретних фактів допомагають уникнути великих дорогих помилок:
- IPv6 стандартизовано наприкінці 1990‑х (ера RFC 2460), переважно тому, що вичерпання IPv4 було помітною проблемою.
- NAT ніколи не був безпековою функцією, якою його вважали. Він приховував хости, але не замінював фаєрвол; IPv6 забирає цю випадкову «ковдру безпеки».
- Великий простір адрес IPv6 змінив підхід до розподілу. Мета — відновити end‑to‑end адресацію і зменшити потребу в stateful трансляції адрес.
- Happy Eyeballs існує через нерівномірний rollout IPv6. Клієнти змагаються IPv6 і IPv4, щоб уникнути помітних затримок, якщо один шлях не працює.
- Docker спочатку сильно покладався на iptables. Коли системи перейшли на бекенд nftables і різні дистрибутиви, «магія» стала крихкою.
- Linux має стабільну підтримку IPv6 десятиліттями, але поведінка sysctl та RA відрізняється між дистрибутивами і cloud‑образами.
- NAT66 існує, але суперечливий. Він застосовний для певних перехідних і політичних потреб, але не є «щасливим шляхом» для IPv6.
- Багато корпоративних мереж все ще блокують вхідний IPv6 за політикою, дозволяючи лише вихідний IPv6 — саме так і відбуваються «неочікувані витоки».
- Cloud IPv6 зазвичай маршрутизований, а не мостовий. Часто ви отримуєте /64 (або більше), який маршрутизований до інтерфейсу; ваше завдання — коректно маршрутизувати префікси до контейнерних мереж.
Здорова ментальна модель: L2‑міст, маршрутизований L3 і «витік», якого ви не хотіли
За замислом, дефолтний мостовий інтерфейс Docker — це L2‑подібна конфігурація на хості: контейнери отримують інтерфейс у Linux‑місті, а хост виконує L3 маршрутизацію/форвардинг.
З IPv4 Docker зазвичай SNAT‑ить вихідний трафік до IP хоста (MASQUERADE). Світ бачить хост, а не контейнер.
З IPv6 у вас опції. Якщо ви призначаєте глобально маршрутизовані IPv6 контейнерам і правильно маршрутизуєте префікс, світ може бачити адреси контейнерів.
Це нормально — навіть добре — якщо ви цього хотіли і налаштували фаєрвол відповідно.
Проблема виникає, коли політики в організації реалізовані як:
- «Дозволено вихід лише через наш IPv4‑проксі.»
- «Вхід дозволено тільки на опубліковані порти.»
- «Наша команда безпеки дивиться логи NAT‑шлюзу.»
Витік простий: IPv6 стає другим, менш контрольованим інтернет‑шляхом. Ваш контейнер може вирішити AAAA і підключитися напряму.
Ніякого проксі. Ніякого NAT‑шлюзу. Нема логів там, де ви їх очікували.
Один афоризм варто тримати на стіні, не тому що він поетичний, а тому що він операційно правдивий:
Парафразована ідея: «Надія — це не стратегія.»
— приписують різним інженерам і лідерам у операційних колах.
Варіанти проєктування: маршрутизований IPv6 vs NAT66 vs відсутність глобального за замовчуванням
Вирішіть, чого хочете. Якщо не вирішите, середовище вирішить за вас — зазвичай під час інцидентної наради.
Варіант A: Маршрутизований IPv6 (рекомендовано, якщо можна зробити акуратно)
Контейнери отримують адреси з префіксу, яким ви керуєте. Ви маршрутизуєте цей префікс від upstream‑роутера (або мережі хмари) до Docker‑хоста,
потім хост маршрутизує до мостів контейнерів. Ніякого NAT. Чистий end‑to‑end. Легше мислити, коли ви погодилися, що фаєрвол обов’язковий.
Переваги: справжній IPv6, немає стану трансляції, прозорість, правильно працює вхід з фаєрволом + опубліковані сервіси.
Недоліки: потрібна делегація префіксу або маршрутизований підмереж; треба навмисно імплементувати фаєрвол для IPv6.
Варіант B: NAT66 (прийнятно як політичний хак, але не як спосіб життя)
Ви можете зберегти «контейнери приховані за хостом», виконуючи NAT для IPv6 еґрезу.
Це не «чистий IPv6», але може бути прагматичною точкою контролю, коли upstream‑маршрутизація складна або політика вимагає єдиної ідентичності еґрезу.
Переваги: стабільна ідентичність еґрезу, зменшує вхідну експозицію за замовчуванням.
Недоліки: вводить стан трансляції, руйнує end‑to‑end припущення і ускладнює налагодження.
Варіант C: Ніякого глобального IPv6 для контейнерів (тільки ULA + контрольований еґрез)
Дайте контейнерам лише ULA (fd00::/8) всередині і примусьте еґрез через проксі або шлюз, яким ви керуєте.
Це узгоджується з підходом «контейнери не повинні спілкуватися з інтернетом, якщо це явно не дозволено».
Переваги: найсильніший захист за замовчуванням, найменше несподіваних експозицій.
Недоліки: все одно потрібно запускати шлюз; деякі додатки очікують прямого IPv6.
Виберіть один варіант і запишіть його. Зробіть це інваріантом платформи. Інакше кожна команда вигадає свій «тільки цього разу» підхід до IPv6.
Правильне увімкнення IPv6: демон, мережі та план адресації
Існують два великі шляхи, як люди наражаються на проблеми:
- Увімкнули IPv6 у Docker, але не запланували префікс, тому вибір адрес стає випадковим і непослідовним між хостами.
- Запланували префікс, але забули про фаєрвол, тож «маршрутизований IPv6» перетворюється на «публічний зоопарк контейнерів».
План адресації, що не переслідуватиме вас
Потрібні стабільні, невзаємоперекриваючі префікси на кожен Docker‑хост або сегмент кластера. У маршрутизованому дизайні типовий підхід такий:
- Виділіть більший префікс для середовища (наприклад, /56 або /48 від провайдера).
- Призначайте /64 на кожну Docker‑містову мережу (бо SLAAC і багато стеків припускають /64).
- Маршрутизуйте ці /64 до хоста, потім дозвольте Docker призначати адреси в їх межах.
Якщо не можете отримати маршрутизованого простору, ULA працюватиме всередині, але чесно: ULA не дає автоматично доступу до інтернету.
Вам все одно знадобиться NAT66 або проксі/шлюз.
Речі в daemon Docker, які мають значення
Увімкнення IPv6 у Docker зазвичай починається в /etc/docker/daemon.json. Конкретні опції залежать від ваших цілей:
"ipv6": trueвмикає підтримку IPv6 для дефолтного мосту."fixed-cidr-v6": "2001:db8:.../64"(приклад префіксу) призначає фіксовану IPv6 підмережу дефолтному мосту."ip6tables": trueвказує Docker керувати правил IPv6 iptables. Це може допомогти, але не віддавайте політику демону.
Якщо ви запускаєте кілька користувацьких мостових мереж, ймовірно, ви використовуватимете docker network create --ipv6 з явними підмережами замість покладання тільки на дефолтний міст.
Жарт №1: IPv6 — це як переїзд у будинок з набагато більшим числом кімнат — класно, поки не зрозумієш, що забув встановити двері.
Фаєрвол для Docker IPv6: припиніть покладатися на інтуїцію
Ваша політика має існувати в фаєрволі хоста, а не в електронній таблиці. З IPv6 хост — це маршрутизатор. Ставайте з ним так само.
Це означає:
- Явний default‑drop на пересланому трафіку, а потім дозволяйте те, що маєте на увазі.
- Явна політика еґрезу (особливо якщо ви використовуєте проксі, DLP‑контроль або аудиторські шлюзи).
- Логування в правильних місцях, щоб «чому не підключається» не було двогодинним археологічним проєктом.
iptables vs nftables — реальність
Багато дистрибутивів зараз використовують nftables під капотом, навіть коли ви вводите iptables. Docker історично управляв правилами iptables напряму.
Це може призвести до «працювало торік, тепер — ні» після оновлень.
Ваше завдання: знати, який бекенд фаєрвола активний, і тестувати правила реальним трафіком.
Якщо ви явно використовуєте nftables — пишіть правила для nftables. Не покладайтеся на Docker, щоб він вас захистив.
Запобігання несподіваному IPv6 еґрезу
Найпоширеніший «витік» такий: IPv4 еґрез йде через ваш NAT/проксі; IPv6 еґрез йде напряму.
Виправлення однакове просте і однаково ігнороване: реалізуйте контроль вихідного IPv6 на шляху пересилання хоста.
Якщо ваша політика «контейнери мають використовувати проксі», то фаєрвол має блокувати прямий IPv6 еґрез, окрім доступу до проксі.
Якщо політика «контейнери можуть виходити назовні», то дозвольте це, але забезпечте спостереження, обмеження швидкості/сегментацію за потреби.
DNS та Happy Eyeballs: звідки береться «на моєму ноутбуці працює»
Як тільки IPv6 доступний, DNS повертає AAAA записи. Багато клієнтів віддаватимуть перевагу IPv6 або запускають гонку IPv6/IPv4.
Це означає, що «ми заблокували IPv4 до цього призначення» не еквівалентно «ми заблокували це призначення».
Перевірте свої резолвери. Перевірте базові образи контейнерів. Деякі образи постачаються з налаштуваннями resolv.conf або різною поведінкою libc.
Якщо ви запускаєте внутрішній DNS, який синтезує AAAA (DNS64), ви отримаєте IPv6 еґрез навіть коли сервіс upstream лише по IPv4.
Операційний висновок: діагностуйте з’єднання обома шляхами — A і AAAA — і не вважайте «ping працює» доказом досяжності додатку.
Практичні завдання: команди, виводи та рішення (12+)
Це завдання, які я реально виконую, коли вмикаю IPv6 у Docker або налагоджую його о 2‑й ночі.
Кожне містить: команду, приклад виводу, що це означає і яке рішення прийняти.
Завдання 1: Підтвердити, що хост фактично має IPv6 і маршрут за замовчуванням
cr0x@server:~$ ip -6 addr show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet6 2001:db8:10:20::15/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe4e:66a1/64 scope link
valid_lft forever preferred_lft forever
Значення: У вас є глобальний IPv6 і link‑local. Добрий початок.
Рішення: Якщо немає глобального IPv6 — зупиніться. Ви не зможете зробити маршрутизований IPv6 без підтримки upstream. Розгляньте ULA + шлюз або спочатку виправте IPv6 на хості.
cr0x@server:~$ ip -6 route show default
default via 2001:db8:10:20::1 dev eth0 metric 100
Значення: Хост має дефолтний маршрут IPv6.
Рішення: Якщо дефолтний маршрут відсутній — контейнери вилітатимуть назовні навіть якщо адреси є. Виправте upstream маршрутизацію/RA/статичні маршрути перед тим, як звинувачувати Docker.
Завдання 2: Перевірити форвардинг ядра та sysctl для IPv6
cr0x@server:~$ sysctl net.ipv6.conf.all.forwarding net.ipv6.conf.default.forwarding
net.ipv6.conf.all.forwarding = 0
net.ipv6.conf.default.forwarding = 0
Значення: Хост не пересилає IPv6‑пакети. Контейнери можуть мати IPv6, але не маршрутуватимуть назовні через хост.
Рішення: Для маршрутизованого IPv6 встановіть forwarding=1 (і забезпечте політику фаєрволу). Якщо ви маєте на увазі «без пересилання», то це правильно — реалізуйте еґрез іншим способом.
Завдання 3: Оглянути конфіг демона Docker по IPv6
cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:dead:beef::/64",
"ip6tables": true
}
Значення: IPv6 для Docker увімкнено на дефолтному мосту з фіксованим /64, і Docker керуватиме IPv6 iptables‑правилами.
Рішення: Переконайтеся, що цей /64 маршрутизовано до хоста (або принаймні доступний). Якщо він випадковий або перекривається з іншими, виправте план перед перезапуском Docker.
Завдання 4: Перезапустити Docker і підтвердити застосування конфігу
cr0x@server:~$ sudo systemctl restart docker
cr0x@server:~$ systemctl status docker --no-pager -l
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled)
Active: active (running) since Tue 2026-01-02 09:18:31 UTC; 6s ago
Docs: https://docs.docker.com
Main PID: 1462 (dockerd)
Tasks: 19
Memory: 76.3M
CGroup: /system.slice/docker.service
└─1462 /usr/bin/dockerd -H fd://
Значення: Docker перезапустився коректно.
Рішення: Якщо він не стартує — перевірте журнали за помилками синтаксису JSON або невідомими опціями для вашої версії Docker.
Завдання 5: Перевірити, чи дефолтний міст має IPv6 підмережу
cr0x@server:~$ docker network inspect bridge --format '{{json .IPAM.Config}}'
[{"Subnet":"172.17.0.0/16","Gateway":"172.17.0.1"},{"Subnet":"2001:db8:dead:beef::/64","Gateway":"2001:db8:dead:beef::1"}]
Значення: Дефолтний міст тепер має IPv6 /64 і адресу шлюзу.
Рішення: Якщо IPv6 підмережа відсутня — ваш конфіг демона не застосовано або IPv6 вимкнено. Виправте перед продовженням.
Завдання 6: Створити користувацьку мережу з явним IPv6
cr0x@server:~$ docker network create --driver bridge --ipv6 --subnet 2001:db8:dead:cafe::/64 appv6
a1d8d3f6a2e7b2c1c0c9b5c0d74a2c1c4f6a8a1c2e1f0b9e8d7c6b5a4f3e2d1
Значення: У вас є IPv6‑вмикнена мостова мережа з явним /64. Зазвичай це простіше керувати, ніж дефолтний міст.
Рішення: Якщо ваша організація хоче сегментацію для додатків — створюйте мережу для кожного стеку і застосовуйте політику фаєрвола по інтерфейсу мосту.
Завдання 7: Запустити контейнер і підтвердити, що він отримує IPv6
cr0x@server:~$ docker run --rm --network appv6 alpine sh -c "ip -6 addr show dev eth0"
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet6 2001:db8:dead:cafe::2/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2/64 scope link
valid_lft forever preferred_lft forever
Значення: Контейнер має глобальну‑подібну IPv6 з вашої підмережі і link‑local адресу.
Рішення: Якщо є тільки link‑local — IPv6 Docker або не увімкнено для цієї мережі, або підмережа не застосувалася.
Завдання 8: Перевірити вихідну IPv6‑з’єднуваність з контейнера
cr0x@server:~$ docker run --rm --network appv6 alpine sh -c "apk add --no-cache curl >/dev/null; curl -6 -sS -m 3 https://ifconfig.co/ip"
2001:db8:10:20::15
Значення: Вихідний IPv6 працює, і еґрез виглядає як адреса хоста (у цьому прикладі). Залежно від маршрутизації/NAT, це може показувати IP контейнера або хоста.
Рішення: Якщо політика вимагає еґрез через проксі — такий «прямий IPv6» є витоком. Блокуйте його або направляйте через проксі явно.
Завдання 9: Подивитися, чи Docker встановив IPv6‑правила фаєрвола (і чи вони адекватні)
cr0x@server:~$ sudo ip6tables -S DOCKER-USER
-N DOCKER-USER
-A DOCKER-USER -j RETURN
Значення: DOCKER‑USER існує, але порожній (RETURN). Docker за замовчуванням не застосовує вашу політику.
Рішення: Помістіть вашу політику еґрезу/входу в DOCKER‑USER (або еквівалент nftables), щоб вона переживала динамічні правила Docker.
Завдання 10: Перевірити, чи форвардинг дозволено фаєрволом (nftables‑шлях)
cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif "lo" accept
ct state established,related accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Значення: Default‑drop на forward. Добра базова лінія: нічого не проходить, якщо ви не дозволите. Docker може вставляти додаткові правила залежно від налаштувань.
Рішення: Додайте явні дозволи для інтерфейсів Docker bridge до потрібних призначень. Якщо ваша ланцюжок forward — accept‑за‑замовчуванням, очікуйте «неочікуваної експозиції».
Завдання 11: Підтвердити, які інтерфейси створив Docker і зв’язати їх з мережами
cr0x@server:~$ ip link show type bridge
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
8: br-7b3b9c2f7a12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
Значення: docker0 (дефолтний міст) і користувацький міст (br‑…).
Рішення: Використовуйте правила фаєрвола на рівні моста. Якщо ви зведете все в одну allow‑list — втрачається сегментація і зручність налагодження.
Завдання 12: Перевірити, що маршрути до підмереж контейнерів існують на хості
cr0x@server:~$ ip -6 route show | grep -E 'dead:beef|dead:cafe'
2001:db8:dead:beef::/64 dev docker0 proto kernel metric 256
2001:db8:dead:cafe::/64 dev br-7b3b9c2f7a12 proto kernel metric 256
Значення: Хост має підключені маршрути до /64 контейнерів через Docker‑мости.
Рішення: Якщо цих маршрутів немає — мережевий конфіг Docker неправильний або міст не піднятий. Виправте це перед тим, як чіпати upstream‑маршрутизацію.
Завдання 13: Перевірити доступність підмережі контейнера ззовні (маршрутизація не опціональна)
cr0x@server:~$ ping6 -c 2 2001:db8:dead:cafe::2
PING 2001:db8:dead:cafe::2(2001:db8:dead:cafe::2) 56 data bytes
64 bytes from 2001:db8:dead:cafe::2: icmp_seq=1 ttl=64 time=0.120 ms
64 bytes from 2001:db8:dead:cafe::2: icmp_seq=2 ttl=64 time=0.118 ms
--- 2001:db8:dead:cafe::2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1010ms
Значення: Від хоста до контейнера все добре (локально). Це не доводить, що зовнішній світ може дістатися до підмережі контейнера.
Рішення: Якщо вам потрібен вхід до контейнерів — упевніться, що upstream‑роутер має маршрути для цих /64, що вказують на хост.
Завдання 14: Виявити «IPv6 обходить проксі» за одну хвилину
cr0x@server:~$ docker run --rm --network appv6 alpine sh -c "apk add --no-cache bind-tools curl >/dev/null; nslookup example.com | sed -n '1,12p'"
Server: 127.0.0.11
Address: 127.0.0.11:53
Non-authoritative answer:
Name: example.com
Address: 93.184.216.34
Name: example.com
Address: 2606:2800:220:1:248:1893:25c8:1946
Значення: Контейнер бачить і A, і AAAA. Багато клієнтів використає IPv6, якщо він працює.
Рішення: Якщо ваша безпекова позиція передбачає логування через проксі/NAT — реалізуйте IPv6‑контроль еґрезу негайно, а не після того, як аудит запитає, куди пішли дані.
Жарт №2: Якщо ви не фаєрволите IPv6, ви фактично запускаєте прод у режимі «екран‑двері — демо».
Швидкий план діагностики
Коли IPv6 у контейнерах «ламається» (або надто «працює»), ви хочете короткий шлях до істини.
Ось порядок, що швидко знаходить вузьке місце.
Перше: здоров’я IPv6 на рівні хоста (не починайте зсередини контейнера)
- Чи має хост глобальний IPv6 на uplink‑інтерфейсі?
- Чи є дефолтний маршрут IPv6?
- Чи увімкнено IPv6‑форвардинг, якщо ви маршрутизуєте трафік контейнерів?
Якщо IPv6 на хості нестабільний — IPv6 контейнерів лише театр.
Друге: мережеве підключення Docker
- Чи має Docker‑мережа IPv6‑підмережу і шлюз?
- Чи отримує контейнер глобальну/ULA IPv6 на eth0?
- Чи має хост підключений маршрут до тієї підмережі через міст?
Третє: фаєрвол і політика (зазвичай винуватець)
- Чи дозволено пересилання IPv6 пакетів від мосту контейнерів до uplink?
- Чи некоректно блокується ICMPv6 (ламає PMTU, neighbor discovery і загальну робочість)?
- Чи реалізована політика еґрезу для IPv6, або тільки для IPv4?
Четверте: DNS і поведінка додатку
- Чи повертає DNS AAAA? Чи додаток неочікувано використовує IPv6?
- Чи є DNS64 або внутрішній резолвер, що переписує відповіді?
- Чи бачите різницю між
curl -4таcurl -6?
П’яте: upstream‑маршрутизація (тільки якщо потрібен вхід)
- Чи є маршрути для контейнерних /64 в upstream‑роутері/таблиці маршрутів хмари?
- Чи фільтрація зворотного шляху або анти‑спуфінг не відкидає трафік?
Поширені помилки: симптом → причина → виправлення
Це не «теоретично». Це те, що з’являється в таймлайнах інцидентів.
1) Контейнери мають IPv6‑адреси, але нічого не можуть досягти по IPv6
Симптом: curl -6 таймаутить; DNS показує AAAA; IPv4 працює.
Причина: Форвардинг IPv6 на хості вимкнено або ланцюжок forward фаєрвола відкидає IPv6.
Виправлення: Увімкніть net.ipv6.conf.all.forwarding=1 для маршрутизованого дизайну і дозволіть форвардинг для Docker‑міст; тримайте default‑drop та додайте конкретні дозволи.
2) IPv6 еґрез обходить корпоративний проксі/NAT‑логування
Симптом: Команда безпеки бачить трафік до зовнішніх IPv6‑призначень, якого немає в логах NAT‑шлюзу IPv4.
Причина: Немає політики еґрезу для IPv6; клієнти віддають перевагу IPv6 через AAAA + Happy Eyeballs.
Виправлення: Заблокуйте прямий IPv6 еґрез з мостів контейнерів, окрім схвалених шлюзів/проксі, або змусьте користуватись проксі; перевірте curl -6 тестами.
3) Контейнер доступний з інтернету по IPv6 без публікації портів
Симптом: Зовнішній скан виявив сервіс, що слухає на IPv6 контейнера; команда каже, що не публікувала порт.
Причина: Маршрутизований IPv6 + сприятливі правила форвардингу/input дозволяють вхід; Docker більше не «приховує» сервіси, як це робив NAT в IPv4.
Виправлення: Default‑drop для вхідного/форварденого IPv6; явно дозволяйте лише потрібні порти для конкретних контейнерів; розгляньте окремі мережі для публічних робочих навантажень.
4) Все працює на одному хості, але не на іншому
Симптом: Той самий compose файл — різна поведінка на різних нодах.
Причина: Різний бекенд фаєрвола (iptables vs nft), різні sysctl або різне забезпечення IPv6 від upstream (деякі ноди мають v6‑маршрути, деякі — ні).
Виправлення: Стандартизувати базовий образ хоста: sysctl, інструменти фаєрвола, daemon.json і валідацію IPv6 у CI для образів нод.
5) Випадкові затримки і дивні проблеми з MTU по IPv6
Симптом: Великі передачі зависають; малі запити проходять; логи показують ретрансмісії.
Причина: ICMPv6 блокується (PMTU discovery ламається) або невідповідність MTU на оверлеї/андерлеї.
Виправлення: Дозвольте необхідні типи ICMPv6; перевірте path MTU; уникайте blanket‑блокування ICMP, що залишилося з блогів 2004 року.
6) «Ми увімкнули IPv6», але є тільки link‑local адреси
Симптом: Контейнер показує тільки fe80:: адреси.
Причина: Мережу не створено з --ipv6 або демо‑рівень IPv6 не увімкнено; відсутній fixed-cidr-v6 для дефолтного мосту.
Виправлення: Увімкніть IPv6 у демонові і/або на рівні мережі; відтворіть мережі за потреби (обережно, у вікні технічного обслуговування).
Три корпоративні історії з IPv6‑тренчів
Міні‑історія 1: Інцидент через неправильне припущення
Середня SaaS‑компанія перевела завантажений інгестійний сервіс у контейнери на флоті Linux‑хостів. Вихідний IPv4 був суворо контрольований:
весь вихід від робочих навантажень йшов через моніторований NAT‑шлюз і шар проксі. Аудит задоволений. Фінанси задоволені. Усі спали.
Платформна команда увімкнула IPv6 на хостах, бо новий партнерський API був «IPv6‑перший», і вони хотіли уникнути додаткового шару трансляції.
Вони включили IPv6 у Docker, перевірили, що контейнери дотягуються до партнера по IPv6, і перейшли далі.
Через два тижні безпеки помітили незвичні вихідні з’єднання до зовнішніх призначень, яких не було в логах NAT‑шлюзу.
Це не був злам. Це була нормальна телеметрія додатка, що йшла напряму до вендорських кінцевих точок, обираючи IPv6 через наявність AAAA.
Ніхто не вирішував, чи дозволено телеметрії обходити проксі. Вона просто обійшла.
Інцидентна відповідь була незручною, бо нічого не «зламали». Система робила саме те, для чого була налаштована.
Неправильне припущення було культурним: думали «NAT означає контроль еґрезу». IPv6 зняв NAT і тим самим ілюзію контролю.
Виправлення було простим, але політично делікатним: вони ввели явні правила IPv6 еґрезу на Docker‑хостах і примусили вихідний HTTP(S)
через контрольований проксі для IPv4 і IPv6. Урок: мережеву політику треба явно визначати по кожному протоколу.
Міні‑історія 2: Оптимізація, що обернулась проблемою
Великий ентерпрайз мав платформу контейнерів з частковою підтримкою IPv6: ULA всередині, NAT66 на межі.
Мережна команда хотіла зменшити стан і спростити налагодження, тому запропонувала «маршрутизований IPv6 скрізь».
Звучить чисто. Так воно й є, коли робити з наміром.
Вони розгорнули маршрутизовані /64 на хост і широко дозволили форвардинг, бо не хотіли ламати робочі навантаження.
План був «потім затягнути фаєрвол», але це «потім» ніколи не було заплановане. Це було просто настрій.
Через місяць скан вразливостей знайшов кілька внутрішніх адмін‑інтерфейсів, доступних по IPv6 з суміжних мереж.
Вони не були «публічно інтернет‑доступні», але були доступні ширше, ніж передбачалося, що спричинило комплаенс‑паніку.
Ці сервіси ніколи не були доступні по IPv4 через NAT і default‑deny, тому власники не вважали їх ризиковими.
Проблема не була у маршрутизованому IPv6. Маршрутизований IPv6 був правильним архітектурним вибором. Проблема була в «тимчасово лояльному фаєрволі».
Оптимізація без запобіжних заходів — це просто швидкий шлях до посмертного аналізу.
Вони зробили те, що мали зробити спочатку: default‑drop форвардингу, явні дозволи по мережах і портах, і формальний розподіл «публічне vs приватне».
Маршрутизований IPv6 лишився. Звичка «потім затягнемо» зникла.
Міні‑історія 3: Нудна, але правильна практика, що врятувала день
Компанія, що працює поруч з платіжними сервісами, запускала контейнери з жорстким контролем змін. Не весело, але ефективно.
Коли вони почали додавати IPv6, написали односторінковий стандарт: план адресації, виділення префіксу на хост і інваріанти фаєрвола.
Кожен образ хоста валідував ті самі sysctl і правила фаєрвола. Кожна Docker‑мережа створювалась явно з відомим /64.
За місяць оновлення cloud‑образу змінило поведінку фаєрвола: дистрибутив перейшов на nftables з іншою політикою за замовчуванням.
На менш дисциплінованій платформі це місце, де IPv6 або тихо перестав би працювати, або тихо відкрився б.
Тут тести валідації хоста впали під час побудови: контейнери втратили вихідний IPv6, бо відсутні були правила форвардингу.
Виправлення було нудним: оновити шаблон набір правил nftables, перезапустити валідацію, перебрати образи.
Жодного інциденту. Жодної екстренної зміни. Жодної «чому тільки IPv6 ламається?»
Практика, що їх врятувала, була найменш глянсовою: ставити IPv6 в перший клас у базових тестах конфігурації.
Якщо ви тестуєте лише IPv4 у вашому golden image pipeline — ви свідомо обираєте бути здивованими пізніше.
Чек‑листи / покроковий план
Ось план, який я б використав для розгортання Docker IPv6 у продакшені, де «ой» дорого коштує.
Спочатку виберіть дизайн, потім виконуйте.
Чек‑лист A: Передпольотна перевірка (хост і upstream)
- Підтвердіть, що хост має стабільний глобальний IPv6 і дефолтний маршрут.
- Вирішіть: маршрутизований IPv6, NAT66 чи тільки ULA.
- Отримайте або виділіть план префіксів (на хост або мережу). Уникайте перекриттів між середовищами.
- Встановіть sysctl: увімкніть IPv6‑форвардинг для маршрутизованих контейнерів; переконайтесь, що поведінка RA відповідає вашому середовищу.
- Визначте базовий фаєрвол: default‑drop для форвардингу; дозвольте потрібні ICMPv6; вирішіть політику еґрезу.
Чек‑лист B: Конфігурація Docker
- Налаштуйте
/etc/docker/daemon.jsonз"ipv6": trueі"fixed-cidr-v6"якщо використовуєте дефолтний міст. - Віддавайте перевагу користувацьким мережам з явними IPv6‑підмережами для додатків.
- Перезапускайте Docker у вікні технічного обслуговування, якщо це впливає на робочі навантаження.
- Перевірте через docker network inspect, що є IPv6 підмережа/шлюз.
Чек‑лист C: Застосування політики (уникати витоків)
- Реалізуйте політику еґрезу для IPv6: дозволяйте лише те, що потрібно (проксі, конкретні призначення або повністю відкритий вихід).
- Реалізуйте політику входу для IPv6: default‑drop, дозволяйте лише опубліковані сервіси і переконайтесь, що підмережі контейнерів не широко доступні.
- Логуйте відкидання у rate‑limited режимі, щоб дебаг не перетворився на пожежу логів.
- Тестуйте поведінку, керовану AAAA: порівнюйте
curl -4таcurl -6з контейнерів.
Чек‑лист D: Валідаційні ворота перед розгортанням
- Підключення: контейнер до інтернету по IPv6 (якщо дозволено) і до внутрішніх залежностей.
- Тест на витік: підтвердіть, що прямий IPv6 еґрез заблоковано, якщо політика вимагає проксі.
- Тест експозиції: підтвердіть, що контейнери не доступні по IPv6, якщо це не передбачено.
- Спостережуваність: переконайтесь, що логи/метрики містять IPv6 (парсинг клієнтських IP, панелі, алерти).
- Тест оновлень: упевніться, що зміни інструментів фаєрвола (iptables/nftables) ловляться у валідації образу.
FAQ
1) Чи увімкнути Docker IPv6 глобально чи на мережу?
Краще на рівні мережі з явними підмережами. Увімкніть на рівні демона, щоб дозволити функціонал, а потім створюйте користувацькі мережі з --ipv6.
Згодом ви будете вдячні за сегментацію і передбачувану адресацію.
2) Чи потрібен /64 для кожної Docker‑мережі?
Практично — так, якщо ви хочете уникнути проблем. Багато IPv6 інструментів і припущень базуються на /64. Менші префікси можуть працювати в деяких маршрутизованих дизайнах,
але ви витратите час на крайові випадки замість того, щоб запускати сервіси.
3) Чи «неправильний» NAT66?
Це не моральна вада. Це компроміс. Якщо upstream‑маршрутизація обмежена або вам потрібна єдина ідентичність еґрезу, NAT66 може бути прагматичним.
Просто документуйте це і будьте готові до складніших розслідувань.
4) Чому мої контролі еґрезу перестали працювати після увімкнення IPv6?
Тому що ваші контролі були специфічні для IPv4 (логи NAT‑шлюзу, правила фаєрвола для IPv4, проксі лише для IPv4). IPv6 створив другий шлях.
Виправте це, навівши політику для IPv6 явно — не сподівайтесь, що клієнти продовжать використовувати IPv4.
5) Чи можуть контейнери бути доступні по IPv6 без публікації портів?
Так, у маршрутизованому IPv6 дизайні, якщо фаєрвол дозволяє і маршрути існують. IPv4 NAT часто маскував цю реальність.
З IPv6 вважайте, що сервіс, що слухає, доступний, якщо ви його не заблокуєте.
6) Чи потрібно дозволяти ICMPv6? Хіба можна блокувати, як ICMP?
Потрібні необхідні типи ICMPv6 для neighbor discovery і PMTU discovery. Надмірне блокування ICMPv6 призводить до дивних, періодичних збоїв, що забирають вихідні вихідні вихідні вихідні вихідні вихідні вихідні вихідні вихідні вихідні вихідні. (Примітка: не перекладайте технічну частину по‑другому.)
Дозвольте конкретні типи; не робіть blanket‑drop.
7) Чому curl іноді використовує IPv6, навіть коли IPv4 працює?
DNS повертає AAAA записи, і бібліотеки клієнта часто віддають перевагу IPv6 або змагаються IPv6/IPv4 (Happy Eyeballs).
Якщо IPv6 доступний і не блокується — його використовують. Сприймайте це як очікувану поведінку, а не зраду.
8) Як запобігти IPv6 витокам, не зламавши все?
Почніть з блокування вихідного IPv6 з інтерфейсів мосту контейнерів, окрім відомих шлюзів (проксі, mirror‑репозиторії оновлень за потреби).
Потім додавайте винятки навмисно. Валідуйте через curl -6 і перевірки AAAA DNS.
9) Чи підтримує Docker Compose IPv6‑мережі?
Так, через визначення мереж з увімкненим IPv6 і явними підмережами. Ключове — переконатися, що демон і хост підтримують IPv6 і що політика фаєрвола послідовна.
10) Який найшвидший доказ, що контейнер «втікає» через IPv6?
Розв’язати відоме двостекове доменне ім’я і примусити IPv6: nslookup побачити AAAA, потім curl -6 до публічної кінцевої точки.
Якщо це вдається, у той час як ваші IPv4‑контролі мали б його блокувати — у вас прогалина в політиці еґрезу.
Висновок: наступні кроки, які не поставлять вас у незручне становище через два квартали
Увімкнення IPv6 у Docker не складне. Увімкнення його безпечно — це дисципліна.
Основна робота — не «Docker». Це маршрутизація і політика: план адресації, форвардинг, фаєрвол, поведінка DNS і спостережуваність.
Практичні наступні кроки:
- Запишіть свій вибір дизайну (маршрутизований IPv6, NAT66 або тільки ULA) і пов’язану з ним безпекову позицію.
- Реалізуйте базові перевірки хоста: IPv6‑адреса, дефолтний маршрут, sysctl для форвардингу, і консистентність бекенду фаєрвола.
- Створюйте явні IPv6 Docker‑мережі з відомими /64; припиніть покладатися на «дефолти» як на архітектуру.
- Застосуйте політику IPv6 еґрезу на шляху форвардингу, щоб IPv6 не обходив ваші контролі.
- Додайте валідацію в процес створення образів: запускайте
curl -6тести, перевірки AAAA DNS і мінімальне сканування експозиції у staging.
Зробіть це — і IPv6 стане просто ще одним транспортом, який ваша платформа підтримує: нудним, передбачуваним і не‑таємним каналом в інтернет.