Безпека контейнерів
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Що таке контейнер насправді
Практичний спосіб визначити контейнер такий: контейнер — це звичайне дерево процесів Linux, яке запущено під певною конфігурацією в стилі OCI так, щоб воно бачило контрольовану файлову систему, контрольований набір ресурсів ядра та обмежену модель привілеїв. Процес може вважати себе PID 1, може вважати, що має власний network stack, може вважати, що володіє своїм власним hostname та IPC ресурсами, і навіть може виконуватись як root всередині власного user namespace. Але під капотом це все ще хостовий процес, який планувальник ядра обробляє як будь-який інший.
Саме тому безпека контейнерів — це насправді вивчення того, як ця ілюзія створюється і як вона дає збої. Якщо mount namespace слабкий, процес може бачити файлову систему хоста. Якщо user namespace відсутній або вимкнений, root всередині контейнера може відображатися занадто близько до root на хості. Якщо seccomp не обмежений і capability set занадто широкий, процес може дістатися до syscalls і привілейованих можливостей ядра, які мали б залишитися поза досяжністю. Якщо runtime socket змонтовано всередині контейнера, контейнер може навіть не потребувати kernel breakout, бо він може просто попросити runtime запустити більш потужний sibling container або змонтувати корінь файлової системи хоста безпосередньо.
Чим контейнери відрізняються від віртуальних машин
VM зазвичай має власний kernel та апаратний рівень абстракції. Це означає, що guest kernel може впасти, запанікувати або бути експлойтнутим без автоматичного набуття прямого контролю над host kernel. У контейнерах робоче навантаження не отримує окремого kernel. Натомість воно отримує ретельно відфільтрований і namespaced вигляд того самого ядра, яке використовує хост. В результаті контейнери зазвичай легші, швидше стартують, їх простіше щільно розміщувати на машині і вони краще підходять для короткоживучих додатків. Ціна — те, що межа ізоляції значно більше залежить від правильної конфігурації хоста і runtime.
Це не означає, що контейнери «небезпечні», а VMs — «безпечні». Це означає, що модель безпеки інша. Добре налаштований стек контейнерів з rootless виконанням, user namespaces, типовим seccomp, суворим capability set, без спільного доступу до host namespaces і з жорстким застосуванням SELinux або AppArmor може бути дуже стійким. Навпаки, контейнер, запущений з --privileged, з host PID/network sharing, з Docker socket змонтованим всередині і з записуваним bind mount для /, функціонально набагато ближчий до доступу root хоста, ніж до безпечно ізольованого sandbox для додатку. Різниця походить від шарів, які були увімкнені або вимкнені.
Існує також проміжна позиція, яку читачам слід розуміти, оскільки вона з’являється дедалі частіше в реальних середовищах. Sandboxed container runtimes, такі як gVisor і Kata Containers, свідомо жорсткіше закривають межу порівняно з класичним runc контейнером. gVisor розміщує userspace kernel шар між робочим навантаженням і багатьма інтерфейсами host kernel, тоді як Kata запускає робоче навантаження всередині легковагової віртуальної машини. Вони все ще використовуються через контейнерні екосистеми та оркестраційні workflow, але їхні властивості безпеки відрізняються від простих OCI runtimes і не повинні ментально групуватись з «звичайними Docker containers», ніби все поводиться однаково.
Стек контейнера: кілька шарів, а не один
Коли хтось каже «цей контейнер небезпечний», корисне наступне питання: який шар зробив його небезпечним? Контейнерне робоче навантаження зазвичай — це результат роботи кількох компонентів разом.
На верхньому рівні часто є image build layer, такий як BuildKit, Buildah або Kaniko, який створює OCI image і метадані. Вище низькорівневого runtime може бути engine або manager, такий як Docker Engine, Podman, containerd, CRI-O, Incus або systemd-nspawn. У кластерних середовищах також може бути orchestrator, наприклад Kubernetes, який визначає бажаний security posture через конфігурацію робочого навантаження. Нарешті, kernel — це те, що фактично забезпечує namespaces, cgroups, seccomp і MAC policy.
Ця багатошарова модель важлива для розуміння значень за замовчуванням. Обмеження може бути запрошене Kubernetes, трансльоване через CRI контейнерd або CRI-O, перетворене у OCI spec обгорткою runtime і лише потім застосоване runc, crun, runsc або іншим runtime щодо ядра. Коли значення за замовчуванням відрізняються між середовищами, часто це тому, що один із цих шарів змінив кінцеву конфігурацію. Тому ж механізм може з’являтися в Docker або Podman як CLI прапорець, у Kubernetes як Pod або securityContext поле, і в нижчих runtime-стеках як OCI конфігурація, згенерована для робочого навантаження. З цієї причини CLI-приклади в цьому розділі слід читати як runtime-specific синтаксис для загальної контейнерної концепції, а не як універсальні прапорці, що підтримуються кожним інструментом.
Справжня межа безпеки контейнера
На практиці безпека контейнерів походить від перекритих контролів, а не від одного досконалого контролю. Namespaces ізолюють видимість. cgroups керують і обмежують використання ресурсів. Capabilities зменшують те, що процес, що виглядає привілейованим, насправді може зробити. seccomp блокує небезпечні syscalls до того, як вони потраплять у kernel. AppArmor і SELinux додають Mandatory Access Control поверх звичайних DAC перевірок. no_new_privs, masked procfs paths і read-only system paths ускладнюють звичні ланцюжки зловживань привілеями та proc/sys. Сам runtime також має значення, оскільки він вирішує, як створюються mounts, sockets, labels і namespace joins.
Ось чому багато документації з безпеки контейнерів здається повторюваною. Той самий ланцюжок ескейпу часто залежить від кількох механізмів одночасно. Наприклад, записуваний host bind mount — це погано, але стає набагато гірше, якщо контейнер також виконується як реальний root на хості, має CAP_SYS_ADMIN, не обмежений seccomp і не обмежений SELinux або AppArmor. Аналогічно, host PID sharing — серйозна вразливість, але вона стає значно кориснішою для атакуючого, коли поєднана з CAP_SYS_PTRACE, слабким захистом procfs або інструментами входу в namespace, такими як nsenter. Правильний спосіб документувати цю тему — не повторювати ту саму атаку на кожній сторінці, а пояснювати, що кожен шар додає до кінцевої межі.
Як читати цей розділ
Розділ організовано від найзагальніших концепцій до найконкретніших.
Почніть з огляду runtime і екосистеми:
Потім перегляньте control planes і supply-chain поверхні, які часто вирішують, чи взагалі атакуючому потрібен kernel escape:
Runtime API And Daemon Exposure
Потім перейдіть до моделі захисту:
Сторінки про namespaces пояснюють примітиви ізоляції ядра поодинці:
Сторінки про cgroups, capabilities, seccomp, AppArmor, SELinux, no_new_privs, masked paths і read-only system paths пояснюють механізми, які зазвичай накладаються поверх namespaces:
Хороший початковий підхід для перерахування
При оцінці контейнерної цілі набагато корисніше поставити невелику кількість точних технічних питань, ніж одразу переходити до відомих PoC ескейпів. Спочатку визначте стек: Docker, Podman, containerd, CRI-O, Incus/LXC, systemd-nspawn, Apptainer або щось більш спеціалізоване. Потім визначте runtime: runc, crun, runsc, kata-runtime або іншу OCI-сумісну реалізацію. Після цього перевірте, чи середовище rootful чи rootless, чи активні user namespaces, чи спільні якісь host namespaces, які capabilities залишилися, чи увімкнено seccomp, чи дійсно застосовується MAC policy, чи присутні небезпечні mounts або sockets, і чи процес може взаємодіяти з container runtime API.
Ці відповіді розкажуть вам значно більше про реальну безпеку, ніж назва базового образу. У багатьох оцінках ви можете передбачити ймовірну сім’ю breakout ще до того, як прочитаєте жоден файл додатку, просто зрозумівши кінцеву конфігурацію контейнера.
Покриття
Цей розділ охоплює старий Docker-орієнтований матеріал у контейнерно-орієнтованій організації: runtime і daemon exposure, authorization plugins, image trust і build secrets, sensitive host mounts, distroless workloads, privileged containers і kernel захисти, які зазвичай накладаються навколо виконання контейнера.
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.


