Контейнери бачать один одного: де ламається ізоляція Docker networks
За замовчуванням контейнери потрапляють в одну мережу, де кожен учасник розпізнає інших за іменем сервісу й може звернутися до будь-якого відкритого порту. Поки все працює штатно, це зручно: застосунок знаходить базу та кеш без ручного налаштування адрес.
Але ізоляція Docker networks при цьому не налаштована взагалі — пласка мережа контейнерів перетворює будь-який скомпрометований сервіс на точку входу до всієї решти інфраструктури.
Що реально доступно зсередини: база даних на своєму порту, кеш і черга завдань, службові адреси метрик і перевірок працездатності, адміністративні панелі. Частина з них відповідає без автентифікації або зі спільним паролем, який лежить поруч — у тому самому файлі опису сервісів.
Ізоляція контейнерів на мережевому рівні відсутня: доступ між контейнерами не обмежений нічим, крім знання імені.
Правила на рівні застосунку цю діру не закривають. Процес усередині контейнера вже тримає валідні токени й облікові дані свого сервісу, а звертається він не через користувацький інтерфейс, а напряму до мережевого сокета.
Ролі та перевірки прав описують, що сервісу дозволено робити з даними, але не заважають йому підключитися до чужого порту й читати те, що там відкрито.
Сегментація контейнерної мережі будується на розподілі сервісів за зонами: зовнішній веб-шар, прикладна логіка, дані. Порты назовні публікує лише вхідна точка, а сховища живуть у мережі без маршруту за межі хоста й доступні лише тим сервісам, яким справді потрібні. Нижче розбираємо, як це налаштовується і де найчастіше помиляються.
Що дає сегментація: менше точок входу та передбачуваний трафік
Сегментація мережі контейнерів — це не косметика в конфігурації, а явно задані межі довіри сервісів. Поки всі контейнери стоять в одній мережі, кожен бачить решту за іменем і може відкрити з'єднання з будь-яким: поверхня атаки дорівнює всьому, що запущено на вузлі.
У Docker мережа за замовчуванням об'єднує весь стек, тому перший крок — розвести ролі за призначенням. Що перевіряємо після розділення:
- Поверхня атаки. З контейнера, куди потрапив чужий код, видно лише сусідів по мережі: замість десятків імен і портів залишається два-три адреси. Назовні порти публікує єдиний вхід.
- Передбачуваний трафік. Зв'язки описані явно: сервіс звертається до бази за фіксованим іменем, а не «куди вийшло». Схему спілкування читаєш з конфігурації, а не відновлюєш за логами.
- Розділення середовищ. Стенд розробки та промисловий контур не перетинаються: тестовий контейнер не побачить робочу базу навіть при помилці в змінних середовища.
- Межі довіри сервісів. Компоненти з різними вимогами до доступів більше не лежать в одній площині — адміністративний інтерфейс недосяжний із публічного контуру.
- Розбір інцидентів. Аномалія локалізується по мережі: видно, яка пара сервісів почала спілкуватися нештатно, і пошук звужується до одного-двох компонентів.
Правила перевіряємо тестами зв'язності: дозволені пари відповідають, заборонені мовчать. Інакше сегментація залишається в схемі, але не в працюючій системі.
Порівняння режимів: стандартна, користувацька, host і overlay мережа
Режим мережі вибирають за завданням, а не за звичкою: та сама конфігурація на стенді розробника й у кластері поводиться по-різному. Нижче — зіставлення чотирьох варіантів, які дає Docker, за умовами застосовності та обмеженнями.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Стандартна | одиночні контейнери, ручні прогони | немає розпізнавання імен між контейнерами, слабка сегментація |
| Користувацька мережа | група сервісів застосунку, звернення за іменем | склад мереж описується заздалегідь, доступи налаштовуються вручну |
| host режим | сервіси, чутливі до затримки, агенти моніторингу вузла | порти вузла зайняті, ізоляція мережі втрачається, один екземпляр на порт |
| overlay мережа | сервіси на кількох вузлах кластера | інкапсуляція трафіку та керівний шар кластера додають складності |
Коли режим шкодить
host на вузлі з кількома застосунками призводить до конфлікту портів і знімає сегментацію. overlay для одного вузла дає лише накладні витрати на інкапсуляцію без виграшу в ізоляції.
Кілька мереж у сервісу
Один сервіс може перебувати одразу в кількох мережах: зовнішня приймає трафік від зворотного проксі, внутрішня — лише обмін із базою. Проксі при цьому не бачить базу, а застосунок не публікує порт назовні.
Порядок робіт: від інвентаризації сервісів до передачі схем
Порядок робіт вибудовуємо так, щоб мережа не перетворилася на клубок: спочатку фіксуємо реальні зв'язки між сервісами, потім проєктуємо сегменти й лише після цього змінюємо конфігурацію на живих контурах.
-
Інвентаризація сервісів і зв'язків. Збираємо перелік контейнерів, опублікованих назовні портів і звернень «хто до кого ходить». Джерела — файли запуску, журнали, карта залежностей від розробників. На виході — таблиця зв'язків із зазначенням порту й напрямку.
-
Проєктування схеми мережі. Розбиваємо сервіси на сегменти за роллю: зовнішній шар, прикладний, сховище даних, службові завдання. Для кожної пари сегментів позначаємо, які зв'язки допустимі, а які мають бути закриті за замовчуванням.
-
Налаштування мереж контейнерів. Піднімаємо окрему мережу під кожен сегмент, сервіс підключаємо лише до тих мереж, які йому справді потрібні. Публікація портів — точкова, через зворотний проксі, а не назовні з усіх контейнерів підряд.
-
Правила доступу між сегментами. Міжмережеві зв'язки відкриваємо поіменно й там, де вони обґрунтовані завданням. Усе інше закрито — так нова вразливість в одному сервісі не дає доступу до сусідів.
-
Перевірка ізоляції на стенді. Прогоняємо сценарії: пробуємо достукатися до сховища із зовнішнього контейнера, дивимося доступні порти, перевіряємо поведінку при перезапуску. Кожен заборонений зв'язок має давати явну відмову, а не «випадково працювати».
-
Викат у бойовий контур і контроль. Переносимо схему покроково, з перевіркою після кожного кроку, щоб не зловити відмову сервісу на середині. Відключені мережі та зайві підключення видаляємо.
-
Передача документації та регламентів. Віддаємо схему мереж, карту зв'язків, порядок підключення нового сервісу й команди діагностики при збоях.
Зафіксована схема живе довше, ніж пам'ять команди: новий сервіс стає за регламентом, а не методом проб, і після кожної зміни склад сегментів залишається передбачуваним.
Що залишається в команди: схеми мереж, політики, регламенти
Працюючий стенд — це ще не результат: за пів року команда змінює сервіс, і ніхто не пам'ятає, чому фронтенд бачить базу напряму, а не через внутрішню мережу.
Тому разом із середовищем ми передаємо артефакти, за якими мережеву конфігурацію можна читати, відтворювати й розвивати без нас — включно з порядком, у якому такі зміни взагалі вносяться.
- Схема мереж і зв'язків — які мережі створені, які сервіси до них входять, що опубліковано назовні й що навмисно закрито. Схема звірена з фактичним станом, а не з початковим задумом.
- Файли конфігурації мереж — декларативний опис мереж, аліасів і підключень, з якого середовище піднімається однією командою на чистій машині.
- Політики доступу — таблиця виду «сервіс → сервіс → дозволено або заборонено», прив'язана до імен мереж: правило змінюється в одному місці й не розповзається по машинах.
- Опис інфраструктури — вузли, точки входу, зовнішні адреси, зворотні проксі та те, що критично для відмовостійкості кожного контуру.
- Регламент мереж — хто погоджує зміну, у який момент вона перевіряється на стенді, яким чином відкочується при невдачі.
- Порядок додавання нового сервісу — покроковий перелік: до якої мережі підключити, які імена задати, які перевірки пройти перед випуском.
- Пам'ятка з діагностики — типові збої на кшталт нерозпізнаного імені або зайнятого порту, що дивитися першим і як відрізнити помилку конфігурації від збою сервісу.
Кожен документ оновлюється разом зі зміною, інакше втрачає сенс: домовмося, хто у вашій команді тримає їх в актуальному вигляді.
Ізоляція сервісів у проді: кейс із метриками до та після
Клієнт прийшов із середовищем із півтора десятка сервісів в одній мережі за замовчуванням: фронтенд, API, фонові обробники, кеш і база бачили один одного напряму.
Причини інцидентів у проді шукали за логами навпомацки — визначити, який контейнер звертається до бази в обхід API, було неможливо.
Розвели трафік за окремими користувацькими мережами: одна — лише для проксі та фронтенду, друга — для API й обробників, третя — під базу та кеш, куди підключений лише шар застосунку.
Замість адрес — мережеві аліаси, замість спільної мережі за замовчуванням — явний список підключень на кожен сервіс.
Що показали заміри до та після
| Показник | До | Після |
|---|---|---|
| Частка інцидентів через зайву мережеву зв'язність | 4 з 10 | 1 з 10 |
| Час локалізації джерела збою | 3–4 години | 40–60 хвилин |
| Час відновлення сервісу після відкату | 25–30 хвилин | 8–10 хвилин |
| Сервісів в одній мережі | 14 | 3 |
Обмеження виринули відразу: застарілий сервіс із жорстко прописаною локальною адресою не переносився в ізольовану мережу й залишився окремою групою, позначеною на схемі.
Зміна аліаса вимагає перезапуску контейнера, а перевірки доступності тепер ідуть через мережу застосунку — їх переписали під нову топологію.
Що робити, якщо контейнери вже працюють в одній пласкій мережі?
Пласку мережу не рвуть за одну технічну перерву. Робочий порядок — накласти сегментацію поверх поточної адресації, перевести один сервіс, переконатися, що він відповідає на новій адресі, і лише потім братися за наступний.
Робоче середовище при цьому продовжує обслуговувати запити: старі імена розв'язуються доти, доки новий контур не перевірено на живому зверненні.
Перший крок — інвентаризація зв'язків: хто з ким реально обмінюється даними, а не хто що має за документацією. Дивимося активні з'єднання та журнали звернень за характерний період, включно з рідкісними завданнями за розкладом.
Сервіси, між якими немає спільних даних, розводимо за різними мережами першими — це найбезпечніші кроки міграції.
Порядок переведення — від периферії до ядра: спочатку зовнішній проксі, збір метрик, допоміжні завдання, потім сервіси з єдиною залежністю, в кінці — сховища та черги, де обрив зв'язку дорожчий за все.
На кожному кроці контейнер якийсь час підключений до обох мереж: сусіди встигають переїхати на нові імена, а потік запитів не переривається.
Зміна адресації сервісів — головне джерело відмов. Якщо адреса сусіда зашита в конфігурацію, після переїзду звернення йде в нікуди, і падає не той сервіс, який переносили.
Тому явні адреси замінюємо на мережеві псевдоніми заздалегідь, до самого перемикання, а правила доступу між сегментами описуємо явно, а не успадковуємо із спільного простору.
До ризиків міграції мережі належать втрата розв'язування імен, забуті правила між контурами та сервіси, які давно не розгорталися й живуть на старих налаштуваннях.
Тому зворотний шлях тримаємо відкритим до кінця спостереження: повернути контейнер у попередню мережу — одна операція, а не аварійний розбір.
Часті технічні сумніви: імена сервісів, порти, затримки
Усередині користувацької мережі контейнери знаходять один одного за іменем сервісу: вбудований резолвер імен віддає актуальну адресу, і звертатися за IP не потрібно — при перестворенні контейнера ім'я залишається, а адреса змінюється.
Назовні публікуються лише точки входу, решта сервісів спілкуються за внутрішніми портами й зовні недоступні. Розділення на підмережі помітної затримки не дає: трафік іде через міст на хості, приріст — долі мілісекунди проти часу відповіді бази.
Нижче — те, що найчастіше уточнюють до старту: від відповідей залежить конфігурація мереж і правила доступу.
Як сервіси знайдуть один одного, якщо адреси змінюються при перезапуску?
У користувацькій мережі працює вбудований резолвер: ім'я сервісу або мережевий псевдонім завжди вказує на живий контейнер. Прописувати IP у конфігурацію не можна — після перестворення контейнера застосунок почне чекати відповіді до помилки.
Що публікувати назовні, а що залишати всередині?
Назовні виводимо лише точки входу — зворотний проксі та, на час відладки, службові інтерфейси. База, кеш і черги живуть у внутрішній мережі без публікації портів: доступ до них є лише у сервісів, підключених до тієї самої мережі.
Ізоляція сповільнить обмін між сервісами?
Ні. Ізоляція — це правила маршрутизації на хості, а не зайва мережева ділянка. Затримка зростає на долі мілісекунди й губиться на тлі запиту до бази; різниця видна лише в синтетичних замірах.
Як перевірити, що ізоляція справді діє?
Зворотним тестом: піднімаємо тимчасовий контейнер в одній мережі й пробуємо достукатися до сервісу з іншої — з'єднання не повинно встановлюватися. Звіряємо docker network inspect і список мереж у контейнера з проєктною схемою.
Обговоримо вашу схему мережі: що надіслати для оцінки та плану
Оцінка налаштування мережі впирається не в кількість контейнерів, а в те, які зв'язки між ними справді потрібні. Чим повніші вихідні дані на вході, тим менше уточнювальних кіл і тим швидше ми повернемося зі складом робіт і планом впровадження. Щоб обговорити завдання предметно, достатньо надіслати кілька речей.
- Перелік сервісів та їхні ролі: що працює постійно, що піднімається під завдання, а що залишилося від попередніх версій.
- Поточні зв'язки: хто з ким спілкується, за якими портами, які сервіси відкриті назовні, а які вже закриті.
- Цільова ізоляція: які групи не повинні бачити одна одну й що зобов'язане ходити строго всередині контуру.
- Вимоги до середовищ: що має бути розділено між тестовим і робочим контуром, де допустима спільна мережа, а де ні.
- Обмеження щодо вікна робіт: скільки часу сервіси можуть простоювати, у які години зміни допустимі.
- Очікуваний результат: що вважаємо готовим — наприклад, доступ до баз даних зовні закритий, зворотний потік до них — передбачуваний.
- Організаційні рамки: хто погоджує зміни та які правила безпеки діють на вашій стороні.
З цього набору ми зберемо карту зв'язків і запропонуємо схему сегментації: де окрема bridge-мережа з явним переліком учасників, де внутрішня мережа без виходу назовні, де потрібен власний контур під службовий трафік.
Після обговорення завдання повернемося з оцінкою налаштування мережі — етапами, точками відкату й вікном робіт, у яке вкладаємося.







