Хостова мережа — це мережевий еквівалент зняття перил безпеки, бо ви „обережний водій“. Іноді це вірне рішення — висока пропускна здатність пакетів, мала затримка, менше компонентів. Іншим разом це шлях до того, що ви ненавмисно випускаєте контейнер, який зв’язується на 0.0.0.0:22 і проводить вихідні дні у спілкуванні з ботами з усього світу.
Якщо ви колись казали „це лише внутрішня служба“ і потім виявляли, що вона слухає у Інтернеті — ви вже розумієте, чому цю тему варто розглянути детальніше ніж у вигляді одноредкового попередження.
Що насправді робить хостова мережа (і що вона мовчки прибирає)
У звичайній Docker-мережі контейнери живуть у власних мережевих неймспейсах. Вони отримують віртуальні інтерфейси, приватні IP та зазвичай виходять назовні через NAT. Опубліковані порти обробляються комбінацією iptables/nftables правил і іноді userland-проксі. Контейнер — це орендар. Хост — це будівля.
З --network host ви переміщуєте контейнер у мережевий неймспейс хоста. Немає veth-пари. Немає окремого IP контейнера. Немає Docker-бриджу для цього контейнера. Контейнер бачить інтерфейси хоста, таблицю маршрутизації хоста і ті порти, що слухає хост. Процес всередині контейнера, який прив’язується до 0.0.0.0:8080, фактично прив’язується на хості.
Це не „просто швидша мережа“. Це компроміс ізоляції. Ви обходите значну частину межі контейнера, а отже:
- Немає підстраховки при публікації портів. Docker не може промедіювати експозицію через
-p. Процес вирішує, що слухати і де. - Конфлікти портів стають миттєвими збоями. Два контейнери не можуть одночасно слухати один і той же порт хоста без координації.
- Очікування щодо брандмауера змінюються. Ви більше не маєте справи з трансляцією контейнера у хост через DOCKER-ланцюги; трафік просто… хостовий трафік.
- Наблююваність стає дивною. «Який контейнер володіє тим сокетом?» перетворюється на судово-експертне питання замість простого CLI-запиту.
- Межі безпеки тоншають. Ізоляція мережевого неймспейсу зникає; інші захисти все ще діють (cgroups, mount namespaces, seccomp, capabilities), але ви прибрали важливий шар.
На Linux хостова мережа проста, бо мережеві неймспейси — це можливість ядра Linux. На Docker Desktop (Mac/Windows) «host network» — не те саме; воно працює всередині VM і поводиться по-іншому. У продакшні більшість болю (і швидкості) — на Linux.
Цитата, яку варто запам’ятати
Werner Vogels (парафраз): «Все ламається, постійно». Якщо ви ставитесь до хостової мережі як до «безпечної, бо швидка», ви закладаєте простій у бюджет.
Короткий жарт #1: Хостова мережа — як дати контейнеру головний ключ від будівлі: класно, поки він не почне «переставляти» двері.
Коли варто використовувати хостову мережу
Я не тут, щоб моралізувати. Хостова мережа — інструмент. Іноді це правильний інструмент.
1) Високопродуктивні шляхи пакетів, де затримка/NAT-бридж має значення
Якщо ви обробляєте високу кількість пакетів в секунду або зменшуєте затримку, усунення veth/bridge/NAT шляху може допомогти. Думайте про збирачі телеметрії, L4 балансувальники навантаження або спеціалізовані мережеві пристрої в контейнерах. Хостова мережа також уникає тиску conntrack від NAT у деяких налаштуваннях.
Але: якщо вам „потрібна хостова мережа для продуктивності“, доведіть це. Заміряйте до і після. У більшості веб-навантажень вузьке місце не в Docker-бриджі; це TLS, системні виклики, GC, звернення до бази даних або проста неефективність додатку.
2) Потрібно брати участь у маршрутизації на рівні хоста або запускати демони маршрутизації
Деякі демони очікують прив’язуватися до відомих портів на конкретних інтерфейсах або взаємодіяти з маршрутизацією хоста так, як це незручно через брідж. Приклади: BGP-спікери, поведінка, схожа на VRRP, або ПЗ, що змінює маршрути і очікує видимості хоста.
3) Прості лабораторні середовища і «одна служба на сервері» розгортання
Якщо сервер виділений під одну контейнеризовану службу і ви ставитесь до нього як до pet-сервера (я знаю), хостова мережа може зменшити складність конфігурації. Менше рухомих частин: немає мапінгу портів, менше крайових випадків Docker-мережі, менше дебагу „чому я не можу дістатися з VLAN X?“.
4) Агентські моніторинги, які повинні бачити мережу хоста
Деякі інструменти мережевого моніторингу, захоплення пакетів, IDS або експорту потоку повинні підключатися до реальних інтерфейсів хоста. Хостова мережа — один зі способів. Інший варіант — --cap-add NET_ADMIN плюс доступ до конкретних пристроїв; але часто бачать використання хостової мережі для таких агентів.
Коли це не варто
- Мульти-орендні хости. Якщо ви запускаєте багато сервісів на одному хості, хостова мережа підвищує шанси випадкової експозиції та конфліктів портів.
- Все з недовіреним вхідним трафіком. Публічні HTTP-інтерфейси, парсери, шлюзи протоколів. Вам потрібні шари, а не менше шарів.
- Що-небудь, що ви хочете швидко перемістити. Хостова мережа часто закладає залежності від інтерфейсів і портів хоста, що уповільнює міграцію.
- Коли „це раз колись вирішило проблему“. Використання хостової мережі як забобону — шлях до крихкої платформи.
Реальні ризики: ізоляція, радіус ураження та «ой, це хост»
Ризик №1: Ви обходите модель публікації портів Docker
З мостовою мережею -p 127.0.0.1:8080:8080 — це чітка, явна вказівка. З хостовою мережею немає „публікації“. Процес прив’язується; хост слухає. Якщо додаток прив’язується до 0.0.0.0, він доступний на кожному інтерфейсі хоста — production VLAN, management VLAN, VPN тощо.
Саме так внутрішні адміністративні порти перетворюються на інцидент. Не через злий намір. Через дефолти.
Ризик №2: Політика брандмауера на рівні хоста стає єдиною реальною загородою
У типовому налаштуванні Docker-бриджу ви побачите ланцюги DOCKER і DOCKER-USER, що посередничають трафік. З хостовою мережею трафік контейнера — це хостовий трафік. Це нормально, якщо брандмауер хоста жорсткий. Катастрофа, якщо брандмауер хоста — „ми додамо це потім“.
Ризик №3: Конфлікти портів і недетерміновані старти
На завантаженому хості два сервіси можуть „працювати в стенді“ через різний порядок запуску, але зламатися в продакшні, бо щось інше зайняло порт раніше. Це не теоретично. Це те, що трапляється о 2:00 ранку, коли вузол перезавантажився і systemd запустив усе в трохи іншому порядку.
Ризик №4: Наблююваність і атрибуція ускладнюються
З мостовою мережею ви кажете «контейнер X володіє 172.17.0.5:9000». З хостовою мережею ви кажете «щось на хості володіє :9000», і тепер доводиться копирсатися в деревах процесів, cgroups і метаданих контейнерів, щоб знайти власника. Це затримує реагування на інциденти.
Ризик №5: Деякі захисти все ще існують, але не обманюйте себе
Хостова мережа не дає автоматично права root на хост. Неймспейси для монтувань, PID, користувачів і cgroups все ще важливі. Seccomp теж важливий. Capabilities теж. Але мережева межа — місце, де часто застосовують принцип найменших привілеїв. Ви її видалили.
Ризик №6: «Localhost» більше не локальний для контейнера
Всередині контейнера в хостовій мережі 127.0.0.1 — це лупбек хоста. Це означає:
- Контейнер може дістатися до сервісів хоста, прив’язаних до localhost (якщо не обмежено інакше).
- Якщо контейнер прив’язується до localhost, він також прив’язується до хостового лупбеку і може конфліктувати з іншими локальними сервісами.
Короткий жарт #2: Найшвидший спосіб „зменшити затримку“ — видалити брандмауер. Будь ласка, не бенчмаркуйте так.
Цікаві факти та коротка історія (для контексту, а не вікторини)
- Мережеві неймспейси Linux з’явилися в ядрі у 2008 році (приблизно у 2.6.24), дозволивши мати окремі мережеві стеки без повних ВМ.
- Docker спочатку сильно покладався на iptables для публікації портів, через що мережі Docker і правила брандмауера тісно переплетені з ранніх часів.
- Існував userland-проксі для обробки крайових випадків (наприклад, hairpin-з’єднання), і з часом багато розгортань перейшли до NAT у ядрі, щоб зменшити накладні витрати.
- Conntrack — це спільний ресурс: коли ви виконуєте багато NAT для трафіку контейнерів, ви ставите кластер під ризик через обмеження таблиці станів ядра.
- Хостова мережа з’явилася ще до Docker: рантайми контейнерів і jail’и часто пропонували режими „shared stack“ задовго до того, як це стало прапорцем у Docker.
- У Kubernetes
hostNetwork: trueмає схожі компроміси; ризики не специфічні для Docker — вони пов’язані з неймспейсами. - Деякі CNI уникають NAT повністю, маршрутизуючи IP подів, тому „host networking для продуктивності“ не завжди вірне порівняння.
- Сучасні фаєрволи перейшли з iptables на nftables, і змішані середовища можуть створювати набори правил, що виглядають коректно, але не виконують очікувань.
Три корпоративні міні-історії з практики
Міні-історія 1: Інцидент через хибне припущення
Середня SaaS-компанія запускала логувальний шлюз на кількох Linux-хостах. Він приймав логи від внутрішніх агентів і форвардив їх у керований бекенд. Хтось змінив контейнер на --network host, бо „NAT губив пакети“. Зміна пройшла після швидкого smoke-тесту. Логи пішли. Усі повернулись до релізів функцій.
Через два тижні непов’язана мережева зміна відкрила раніше невідповідний інтерфейс у ширший сегмент корпоративної мережі. Логувальний шлюз також мав адміністративний ендпоінт — призначений лише для localhost для відладження. У режимі bridge він був доступний тільки на 127.0.0.1. У режимі host сервіс за замовчуванням прив’язався до 0.0.0.0, бо конфіг був неакуратний і ніхто не помітив.
Першим сигналом не був алерт. Це був тікет: „Чому цей сервіс має адмінський UI?“ Далі пішли сканери. Потім питання від керівництва: „Чи ми скомпрометовані?“ Той день пройшов у доказах негативу — копирсанні в логах доступу, посиленні політики брандмауера та навчанні, що „внутрішній“ не є безпековою межею.
Корінна причина не була лише в хостовій мережі. Це було припущення, що рантайм контейнера утримає експозицію. Хостова мережа миттєво зробила це припущення хибним.
Міні-історія 2: Оптимізація, що обернулась проти
Фінтех-команда мала latency-sensitive API. Хтось виміряв p99 і вирішив, що Docker-бридж „додає джиттеру“. API-перемістили в хостову мережу. Бенчмарки трохи покращились на тихому хості. Слайд з перемогою. Роллаут.
Потім продакшн-трафік зустрів реальність: співмешкані сервіси, оновлення ядра і брудна правда епhemeral-портів. API використовував пул вихідних з’єднань до бази даних. З хостовою мережею вихідний трафік ділив діапазон епhemeral-портів хоста з усім іншим. Під навантаженням хост почав стикатись із вичерпанням епhemeral-портів під час сплесків, що викликало збої з’єднань, які виглядали як проблеми бази даних.
Команда гонялась за проблемами бази даних кілька днів — додавали репліки, налаштовували параметри, звинувачували мережу — поки хтось не подивився на ss -s і не побачив гору сокетів у TIME-WAIT. „Оптимізація продуктивності“ не лише пересунула вузьке місце; вона перемістила його у спільний ресурс хоста, що почав впливати на несуміжні сервіси.
Вони повернулись до bridge-мережі для API, виправили повторне використання з’єднань, обережно налаштували tcp_tw_reuse (не бездумно) і ввели правильне навантажувальне тестування у процес змін. Перевага по затримці не коштувала системного ризику.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Велике підприємство запускало кілька контейнерів з хостовою мережею на виділених вузлах: внутрішній DNS-форвардер і збирач метрик. Було правило: будь-який контейнер з хостовою мережею повинен мати (1) явний systemd-юніт з документованими портами, (2) allowlist у брандмауері хоста і (3) періодичну задачу аудиту, що знімає знімок слухаючих сокетів і порівнює їх.
Це не було модним. Це створювало квитки на кшталт „порт 8125/udp досі відкритий, очікувано“. Ніхто за це не отримував підвищення. Але це створило стабільний контракт: якщо хочеш хостову мережу — ти платиш за перила.
Якось рутинне оновлення образу підтягнуло нову версію агента метрик, що за замовчуванням увімкнула debug HTTP-сервер. Діф аудиту відзначив новий слухач на :6060. Брандмауер його заблокував. On-call отримав алерт, підтвердив, що це нове, і вимкнув його в конфігурації до того, як це стало знахідкою сканера.
Постмортем був коротким і майже нудним — найкращий варіант. Контролі спрацювали, радіус ураження залишився малим, і зміна не перетворилась на інцидент безпеки.
Практичні завдання: команди, виводи та як прийняти рішення
Це не „іграшкові“ команди. Це те, що ви запускаєте, коли вирішуєте, чи безпечно використовувати хостову мережу, і коли прибираєте наслідки її неправильного застосування.
Завдання 1: Виявити контейнери, що використовують хостову мережу (перша очевидна перевірка)
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Networks}}'
NAMES IMAGE NETWORKS
api-1 myco/api:2026.01 bridge
node-exporter prom/node-exporter:v1.8.0 host
dns-forwarder myco/dns:2.3 host
Що це означає: Усе, що показує host, ділиться мережевим неймспейсом хоста.
Рішення: Для кожного контейнера в host-режимі зафіксуйте, які порти він має слухати і хто ними володіє. Якщо ви не можете відповісти за 60 секунд — ви вже у зоні „потенційного інциденту“.
Завдання 2: Підтвердити, що контейнер дійсно в мережевому неймспейсі хоста
cr0x@server:~$ docker inspect -f '{{.Name}} {{.HostConfig.NetworkMode}}' node-exporter
/node-exporter host
Що це означає: Це остаточно: контейнер у host-режимі.
Рішення: Якщо цей контейнер не на виділеному вузлі, розглядайте його як сервіс підвищеного ризику і застосуйте заходи ізоляції нижче.
Завдання 3: Перелік слухаючих портів на хості (це обов’язково при host-режимі)
cr0x@server:~$ sudo ss -lntup
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 4096 0.0.0.0:9100 0.0.0.0:* users:(("node_exporter",pid=2314,fd=3))
tcp LISTEN 0 4096 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1189,fd=6))
tcp LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=4128,fd=112))
Що це означає: Ви бачите, що світ може потенційно дістати. У host-режимі сервіси контейнерів виглядають як звичайні процеси хоста.
Рішення: Усе, що слухає на 0.0.0.0 (або ::), потребує свідомої політики брандмауера. Якщо ви очікували, що сервіс буде лише внутрішнім — або прив’яжіть його до конкретного інтерфейсу, або заблокуйте його явно.
Завдання 4: Зв’язати слухаючий процес із контейнером (швидка атрибуція)
cr0x@server:~$ ps -p 2314 -o pid,cmd,cgroup
PID CMD CGROUP
2314 /bin/node_exporter --web.listen-address=:9100 0::/docker/1e7b9d0f6a3f8b2c9f3d6a1b4a6f0b2c1d9e0a7b...
Що це означає: Шлях cgroup містить ID контейнера.
Рішення: Якщо ви не можете швидко атрибувати порт власнику, створіть рукбук для цього. Інциденти — не час перевідкривати метадані процесів Linux.
Завдання 5: Перевірити, які інтерфейси є на хості (і які ви випадково експонуєте)
cr0x@server:~$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 10.20.4.18/24
eth1 UP 172.16.12.18/24
docker0 DOWN 172.17.0.1/16
Що це означає: Кілька NIC — кілька зон довіри. Хостова мережа експонує на всі, якщо ви не прив’язуєтесь обережно.
Рішення: Якщо сервіс має бути доступним лише на eth0, прив’яжіть його до 10.20.4.18 (або заблокуйте eth1) замість полагання на „ніхто не маршрутизує туди“.
Завдання 6: Перевірити таблицю маршрутів (неочікувані шляхи egress реальні)
cr0x@server:~$ ip route
default via 10.20.4.1 dev eth0
10.20.4.0/24 dev eth0 proto kernel scope link src 10.20.4.18
172.16.12.0/24 dev eth1 proto kernel scope link src 172.16.12.18
Що це означає: Контейнери в host-режимі успадковують цю маршрутизацію. Якщо у вас розділена маршрутизація або policy routing, контейнери також її наслідуватимуть.
Рішення: Якщо вам потрібен контроль egress на сервіс, хостова мережа проти вас. Віддавайте перевагу окремим неймспейсам або egress-проксі.
Завдання 7: Подивитися, який фреймворк брандмауера активний (iptables vs nftables має значення)
cr0x@server:~$ sudo iptables -S | head
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-USER
Що це означає: За замовчуванням ACCEPT на INPUT — червоний прапор у будь-якому середовищі з host-мережевими контейнерами.
Рішення: Якщо INPUT встановлено ACCEPT, виправте це першочергово. Хостова мережа плюс дозволений INPUT — як стати „інтернет-доступним“ випадково.
Завдання 8: Інспектувати ланцюг DOCKER-USER (де слід розміщувати allow/deny політику)
cr0x@server:~$ sudo iptables -S DOCKER-USER
-N DOCKER-USER
-A DOCKER-USER -j RETURN
Що це означає: Тут політики немає. Також: хостова мережа не завжди йде через Docker-ланцюги для вхідного трафіку.
Рішення: Для host-режиму застосовуйте політику в INPUT (і, можливо, OUTPUT). Для bridge-контейнерів DOCKER-USER — добрий контрольний пункт. Не плутайте їх.
Завдання 9: Підтвердити, які порти доступні з іншого хоста (перевірка реальності)
cr0x@server:~$ nc -vz 10.20.4.18 9100
Connection to 10.20.4.18 9100 port [tcp/*] succeeded!
Що це означає: Порт доступний на цьому інтерфейсі з місця, де ви запускали команду.
Рішення: Якщо доступність ширша, ніж задумано, не „запам’ятайте виправити пізніше“. Заблокуйте зараз, потім відкоригуйте адреси прив’язки.
Завдання 10: Перевірити навантаження conntrack (host-режим часто супроводжується великим трафіком)
cr0x@server:~$ sudo conntrack -S
cpu=0 found=120938 invalid=12 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0
Що це означає: invalid і drop-и можуть підказувати перевантаження або асиметричну маршрутизацію. NAT-важкі налаштування навантажують conntrack; host-режим може зменшити NAT, але не чарівно усуває тиск на стан у ядрі.
Рішення: Якщо під час інцидентів ви бачите зростання drop/early_drop, це вузьке місце стану ядра. Розгляньте зменшення NAT, налаштування лімітів conntrack або переробку потоків трафіку.
Завдання 11: Поглянути на зведення сокетів (вичерпання портів і TIME-WAIT шторми)
cr0x@server:~$ ss -s
Total: 31234 (kernel 0)
TCP: 19872 (estab 2412, closed 16011, orphaned 3, timewait 15890)
Transport Total IP IPv6
RAW 0 0 0
UDP 412 356 56
TCP 3861 3312 549
INET 4273 3668 605
FRAG 0 0 0
Що це означає: Велика кількість timewait під навантаженням може сигналізувати про часті з’єднання. Хостова мережа змушує ваш контейнер ділити діапазон епhemeral-портів хоста й життєвий цикл TCP з усім іншим.
Рішення: Якщо TIME-WAIT домінує, спочатку виправте повторне використання з’єднань і пулінг. Лише потім розглядайте налаштування ядра — і робіть це обережно, бо налаштування можуть маскувати баги до моменту їх вибуху.
Завдання 12: Підтвердити діапазон епhemeral-портів (спільний ресурс, який часто забувають)
cr0x@server:~$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
Що це означає: Це діапазон вихідних епhemeral-портів. Всі host-мережеві робочі навантаження ділять його.
Рішення: Якщо ви запускаєте багато клієнтів із високим churn на одному хості, розгляньте розширення діапазону і зменшення churn. Краще: ізолюйте навантаження або уникайте host-режиму для „балакучих“ клієнтів.
Завдання 13: Визначити, який контейнер володіє підозрілим відкритим портом за допомогою lsof
cr0x@server:~$ sudo lsof -iTCP:8080 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 4128 root 112u IPv6 65741 0t0 TCP *:http-alt (LISTEN)
Що це означає: Процес слухає на всіх інтерфейсах (*). Тепер потрібно зв’язати PID з контейнером через cgroup, як у Завданні 4.
Рішення: Якщо він не має бути публічним, або прив’яжіть його до конкретної адреси, або негайно блокуйте доступ на брандмауері хоста.
Завдання 14: Перевірити, чи Docker не експонував щось непомітно через опубліковані порти (порівняння cgroup)
cr0x@server:~$ docker port api-1
8080/tcp -> 127.0.0.1:18080
Що це означає: Контейнер у bridge-режимі явно прив’язаний до localhost на боці хоста. Це та підстраховка, яку ви втрачаєте в host-режимі.
Рішення: Якщо ви покладались на прив’язку до 127.0.0.1 як безпеку, host-режим несумісний з цим, якщо ви не відтворите ту саму гарантію через прив’язки додатку і брандмауер.
Завдання 15: Перевірити, чи контейнер має додаткові мережеві capabilities (host-режим + NET_ADMIN — гостро)
cr0x@server:~$ docker inspect -f '{{.HostConfig.CapAdd}}' dns-forwarder
[NET_ADMIN NET_RAW]
Що це означає: Контейнер може маніпулювати мережею і формувати пакети. У host-режимі це може впливати безпосередньо на хост.
Рішення: Розглядайте це як високий ризик. Якщо вам потрібен NET_ADMIN у host-режимі — ізолюйте його на виділеному вузлі і посиліть аудит.
Завдання 16: Перевірити шлях резолюції імен (host-режим успадковує /etc/resolv.conf хоста)
cr0x@server:~$ cat /etc/resolv.conf
nameserver 10.20.4.53
search corp.example
Що це означає: Контейнери в host-режимі часто ділять конфіг резолвера хоста, що може змінюватись під час DHCP оновлень або VPN-сесій.
Рішення: Якщо стабільність DNS важлива, зафіксуйте конфіг DNS явно (конфіг systemd-resolved, статичний resolv.conf або налаштування резолвера на рівні додатку).
Швидкий план діагностики: що перевірити першим/другим/третім
Це послідовність „не панікуй, тільки триаж“, коли є хост-мережевий контейнер і трафік падає або продуктивність різко знижується.
Перше: підтвердити, що саме слухає і де
- Запустіть
sudo ss -lntup. Визначте несподіваних слухачів і чи вони прив’язані до0.0.0.0, конкретного IP або127.0.0.1. - Зв’яжіть підозрілі PID з контейнерами за допомогою
ps -o cgroupабоcat /proc/<pid>/cgroup.
Ціль: Визначити, чи збій — це просто „неправильна адреса прив’язки“ або „конфлікт портів“. Це швидкі виправлення.
Друге: перевірити досяжність звідти, звідки має бути
- З сусіднього хоста в тій самій мережі тестуйте
nc -vz <ip> <port>. - Для UDP використовуйте
nc -vzuабо власні проби додатка і перевірку лічильників пакетів (ip -s link).
Ціль: З’ясувати, чи це локальна проблема процесу, чи проблема мережевого шляху/брандмауера.
Третє: перевірити брандмауер хоста і політику маршрутизації
- Огляньте правила INPUT (
sudo iptables -Sабо nft-правила, якщо застосовно). - Підтвердьте інтерфейси і маршрути (
ip -br addr,ip route). - Якщо використовуєте policy routing, перевірте
ip ruleі відповідні таблиці (ip route show table <n>).
Ціль: Переконатися, що ви випадково не експонуєте або не блокуєте сервіс, змінивши модель неймспейсу.
Четверте: пошук спільного виснаження ресурсів хоста
ss -sдля TIME-WAIT-шторм та лічильників встановлених з’єднань.sysctl net.ipv4.ip_local_port_rangeі загальні TCP-настройки, якщо підозрюєте вичерпання епhemeral-портів.conntrack -S, якщо в шляху є NAT/статусні таблиці.
Ціль: Визначити, чи хостова мережа перемістила вас у спільне вузьке місце (ephemeral-порти, conntrack, CPU softirq).
П’яте: перевірити softirq CPU і насичення NIC (шар „це ядро“)
mpstat -P ALL 1абоtop, щоб побачити завантаження CPU.cat /proc/softirqsіsar -n DEV 1(якщо доступно) для тиску мережевих переривань.
Ціль: Підтвердити, чи сприймана „накладність Docker“ була насправді пов’язана із CPU/перериваннями.
Поширені помилки: симптом → корінна причина → виправлення
1) «Сервіс доступний в Інтернеті»
Симптом: Сканер безпеки знайшов новий відкритий порт; у логах доступу фігурують випадкові IP.
Корінна причина: Контейнер у host-режимі прив’язався до 0.0.0.0; політика INPUT хоста це дозволяє.
Виправлення: Явно прив’язуйте до потрібного інтерфейсу/IP; застосуйте deny-by-default для INPUT і allowlist лише потрібних діапазонів/портів.
2) «Два контейнери більше не стартують на одному вузлі»
Симптом: Один екземпляр падає з помилкою „address already in use“.
Корінна причина: Хостова мережа прибирає мапінг портів; у вас реальний конфлікт портів.
Виправлення: Призначте унікальні порти для кожного екземпляра або перестаньте використовувати host-режим. В оркестрації застосуйте anti-affinity, щоб тільки один екземпляр потрапляв на вузол.
3) «Виклики на localhost потрапляють не туди, куди треба»
Симптом: Контейнер намагається звернутися до 127.0.0.1:xxxx, очікуючи сайдкар, але потрапляє на сервіс хоста (або ні на що).
Корінна причина: У host-режимі loopback — це хостовий loopback.
Виправлення: Використовуйте явне service discovery і виділені порти, або тримайте сайдкари в спільному неймспейсі мережі контейнера з bridge-режимом. Не думайте, що localhost означає „цей контейнер“.
4) «Після переходу в host-режим вихідні з’єднання періодично падають»
Симптом: Приривчасті помилки з’єднань; багато TIME-WAIT; помилки на кшталт „cannot assign requested address“.
Корінна причина: Вичерпання епhemeral-портів або churn з’єднань на хості, що ділять навантаження.
Виправлення: Зменшіть churn з’єднань (keep-alives, pooling); розгляньте розширення діапазону епhemeral-портів; розподіліть навантаження; перегляньте доцільність host-режиму для клієнтів з високим churn.
5) «Правила брандмауера, що працювали раніше, ніби не діють»
Симптом: Правила DOCKER-USER не блокують трафік до контейнера в host-режимі.
Корінна причина: Хостова мережа обходить Docker-бридж ланцюги; трафік потрапляє прямо в INPUT.
Виправлення: Реалізуйте політику в INPUT/OUTPUT (iptables/nft), а не тільки в Docker-ланцюгах.
6) «Моніторинг показує неправильні IP джерела»
Симптом: Логи показують IP хоста як джерело; атрибуція ламається; ACL не працюють.
Корінна причина: Хостова мережа стирає ідентичність контейнера на L3; source IP стає IP інтерфейсу хоста.
Виправлення: Використовуйте mTLS-ідентичності, явні заголовки з обережністю або відокремлені неймспейси мережі там, де потрібна ідентичність. Не будуйте ACL, що припускають пер-контейнерні IP, якщо ви використовуєте host-режим.
7) «Трафік обходить очікуваний проксі/контроль egress»
Симптом: Контейнер може дістатись мереж, куди він не мав би; логи egress неповні.
Корінна причина: Маршрутизація і конфіг резолвера хоста застосовуються; контролі на основі неймспейсів вже не діють.
Виправлення: Запровадьте egress-контроль на рівні брандмауера хоста або перемістіть навантаження з host-режиму в контрольований шлях egress.
Як обмежити шкоду, якщо потрібно використовувати хостову мережу
Якщо ви вибираєте хостову мережу, ви винні своїй майбутній копії деяку ізоляцію. Це не параноїдально. Це базова оперативна гігієна.
1) Розміщуйте хостово-мережеві робочі навантаження на виділених вузлах
Ізоляція через планування не замінює неймспейси, але ефективно обмежує радіус ураження. Якщо контейнер ділить мережу хоста, принаймні нехай він не ділить хост з усім іншим.
2) Deny-by-default для INPUT, потім allowlist
Хостова мережа перетворює кожного слухача на слухача хоста. Ваш брандмауер хоста має бути нудним і строгим:
- За замовчуванням DROP для INPUT.
- Дозволяти established/related.
- Дозволяти SSH лише з мереж управління.
- Дозволяти конкретні порти сервісів лише з певних діапазонів джерел.
Робіть це за допомогою стандартних інструментів брандмауера, а не одноразових команд на випадкових вузлах. Дрейф — шлях до втрати контролю.
3) Прив’язуйте до явних інтерфейсів
Краще прив’язувати до конкретного IP, ніж до 0.0.0.0. Якщо потрібне багатоджерельне експонування, вказуйте це явно в конфігу. Якщо додаток не підтримує адекватних опцій прив’язки — це проблема продукту, а не ops.
4) Агресивно знижуйте capabilities
У більшості випадків хостова мережа не вимагає NET_ADMIN. Якщо він не потрібен — не додавайте. Те ж для NET_RAW. Ви не будуєте пакет-крафтер; ви запускаєте сервіс.
5) Використовуйте read-only файлові системи та мінімальні маунти
Це не тільки про мережу, але важливо: якщо контейнер у host-netns і має записувальний доступ до критичних шляхів хоста — ви збільшуєте ризики. Використовуйте read-only rootfs і мінімізуйте bind mount’и.
6) Зробіть власність портів явною в коді та в операціях
Визначайте порти в одному місці. Документуйте їх. Налаштуйте алерт при зміні. Для контейнерів у host-режимі „дрейф портів“ — і ризик продуктивності, і ризик безпеки.
7) Віддавайте перевагу rootless-контейнерам, де можливо, але розумійте обмеження
Rootless Docker може зменшити вплив від втечі контейнера, але host-мережа і rootless не завжди сумісні залежно від середовища. Не думайте, що rootless робить host-мережу повністю „безпечнішою“. Це часткова міра у деяких вимірах, не у всіх.
8) Не використовуйте host-мережу як обхід для зламаного DNS/service discovery
Якщо причина, через яку ви тягнетесь до host-режиму — „контейнер не може дістатись X“, виправте маршрутизацію, брандмауер або service discovery. Host-режим — не терапевт для мережі.
Чеклісти / покроковий план
План A: Вирішіть, чи виправдана хостова мережа
- Запишіть мету. „Зменшити затримку“ — не мета. „Знизити p99 на 2ms при 10k rps“ — це мета.
- Зніміть базову метрику. Зафіксуйте затримку, CPU, softirq, падіння пакетів, статистику conntrack.
- Спробуйте безпечні альтернативи першими. Використайте bridge-мережу з tuned MTU, відповідний CNI або host-порти з жорсткими прив’язками; виправте churn на рівні додатку.
- Запустіть канарку на виділених вузлах. Спочатку не змішуйте з іншими навантаженнями.
- Визначте очікування щодо портів/прив’язок. Точні порти, протоколи, адреси прив’язки і дозволені діапазони джерел.
- Побудуйте план відкату. Якщо на відкат потрібно більше ніж один деплой — ви граєте на удачу.
План B: Якщо ви вже використовуєте хостову мережу, зменшіть ризик без перепису
- Проведіть інвентар всіх host-мережевих контейнерів за допомогою
docker psіdocker inspect. - Зніміть знімок слухачів за допомогою
ss -lntupі зафіксуйте очікуване vs фактичне. - Впровадьте deny-by-default для INPUT і явні allow-правила для потрібних портів.
- Зафіксуйте адреси прив’язки у конфігах додатків; уникайте
0.0.0.0, якщо це не необхідно. - Приберіть непотрібні capabilities і запускайте не від root, якщо можливо.
- Додайте виявлення: алерти на нові слухачі і дрейф політики брандмауера.
План C: Міграція від хостової мережі (план «погасити борг»)
- Визначте, чому було обрано host-режим. Продуктивність? Доступність? Біль зі мапінгом портів?
- Відтворіть початкову проблему у контрольованому середовищі.
- Перемістіть у bridge або routed networking з явною публікацією портів, або використайте CNI, що дає маршрутизовані IP контейнерів.
- Заново перевірте продуктивність і підтвердіть, що ви не погіршили ситуацію, виправляючи не ту річ.
- Приберіть припущення, прив’язані до хоста (імена інтерфейсів, хардкодні порти, залежності від localhost).
- Розгортайте поступово і тримайте брандмауер хоста жорстким під час міграції.
Питання та відповіді
1) Чи «небезпечна» хостова мережа Docker за замовчуванням?
Вона менш ізольована за задумом. Якщо брандмауер хоста жорсткий і сервіси прив’язані явно, її можна безпечно використовувати. Якщо брандмауер дозволений, а додатки за замовчуванням прив’язуються до 0.0.0.0, це фактично степ-лупка безпеки.
2) Чи робить --network host контейнери швидшими?
Іноді. Це може прибрати накладні витрати bridge/NAT і спростити шлях пакетів. У багатьох реальних системах вузьке місце в іншому. Заміряйте до і після.
3) Чи можу я все ще використовувати -p з host-мережею?
Ні. Немає чого публікувати. Контейнер прив’язується напряму на хост. Ваш „мапінг портів“ тепер — те, що робить процес.
4) Чому мої правила у DOCKER-USER не блокують host-мережеві контейнери?
Бо трафік host-мережевих контейнерів не проходить через Docker-ланцюги мосту так, як ви очікуєте. Ставтесь до нього як до будь-якого процесу хоста: реалізуйте політику в INPUT/OUTPUT (iptables/nftables) або на рівні зовнішніх фаєрволів.
5) А Kubernetes hostNetwork: true?
Ті ж самі компроміси: ви ділите неймспейс мережі вузла. Деякі випадки дають простоту і продуктивність, інші — втрата ізоляції. Міри пом’якшення (виділені вузли, жорсткий брандмауер, явні прив’язки) застосовні й тут.
6) Чи обходить host-мережа всі ізоляції контейнера?
Ні. Вона обходить мережевий неймспейс. Інші межі — простору монтувань, PID, користувачів, cgroups, seccomp — все ще можуть діяти. Але втрата netns — суттєве зниження defense-in-depth.
7) Як визначити, який контейнер відкрив порт у host-режимі?
Використайте ss -lntup щоб отримати PID, потім зв’яжіть PID з контейнером через cgroups (ps -o cgroup) або метадані Docker. Побудуйте рукбук, бо під тиском вам це знадобиться.
8) Яка найбезпечніша „стратегія за замовчуванням“ для продакшну?
Уникати host-мережі, якщо ви не можете чітко описати вигоду або функціональну вимогу. Якщо вона необхідна — запускати такі контейнери на виділених вузлах з deny-by-default брандмауером і явними адресами прив’язки.
9) Якщо я прив’язуюсь тільки до 127.0.0.1, чи я в безпеці?
Ви в безпечнішій позиції, але не повністю. Localhost-only зменшує мережеву експозицію, але треба врахувати, хто може дістатись до хоста (SSH-користувачі, інші локальні процеси, можливі шляхи ескалації привілеїв). Також у host-режимі контейнери ділять localhost з хостом.
10) Чи достатньо rootless Docker, щоб зробити host-мережу прийнятною?
Rootless зменшує певні ризики втечі, але не вирішує фундаментальної проблеми: слухачі знаходяться в мережі хоста, а політика брандмауера — реальна межа. Розглядайте це як часткове пом’якшення, а не як дозвіл.
Висновок: практичні наступні кроки
Якщо ви запам’ятаєте одне: хостова мережа — це не „просто режим мережі“. Це рішення зруйнувати межу. Це може бути розумно, але ніколи не має бути випадковим.
- Зробіть інвентар host-мережевих контейнерів сьогодні і запишіть очікувані слухачі для кожного.
- Запустіть
ss -lntupна хостах і зіставте „очікуване“ з „фактичним“. Виправте сюрпризи. - Заблокуйте політику INPUT хоста, щоб випадкові слухачі не ставали зовнішніми інцидентами.
- Ізолюйте хостово-мережеві робочі навантаження на виділені вузли, де це можливо.
- Заміряйте претензії щодо продуктивності перед використанням host-режиму як універсального оптимізатора.
- Автоматизуйте виявлення: алерти на нові слухачі і дрейф брандмауера, бо люди забувають і ПЗ відправляє дефолти.
Хостова мережа може бути правильним кроком. Просто робіть це як інженерне рішення, а не за відчуттям.