- Контейнеры используют ядро совместно с хостом, поэтому их изоляция зависит от пространств имен, cgroups и мер по усилению безопасности системы.
- Podman обеспечивает дополнительную безопасность по сравнению с классическим Docker, избегая центрального демона с правами root и обеспечивая выполнение без прав root.
- В Podman мостовые, хостовые и бессерверные сети (slirp4netns, paste) позволяют регулировать баланс между связностью и изоляцией.
- Сочетание KVM, контейнеров и передовых методов (без прав root, MAC, seccomp, сканирование образов) обеспечивает надежную платформу для производственной среды.

При создании серьезной контейнерной среды на Linux сразу же возникает тот же вопрос: насколько безопасно полагаться исключительно на контейнеры, и когда целесообразно переходить на KVM или микро-виртуальные машины? В корпоративных средах, где используются Docker, Podman, LXC, виртуальные машины KVM и различные гипервизоры, четкое понимание модели изоляции является ключом к предотвращению ошибок.
Кроме того, нередки и довольно сложные сценарии, например, попытка использовать Docker Desktop или Podman внутри виртуальной машины в VirtualBox или KVM , где задействовано несколько уровней виртуализации (вложенных или нет), и легко столкнуться с ошибками типа «KVM не включен на хосте». Давайте организуем все эти компоненты — контейнеры, виртуальные машины, Podman, KVM, сеть и безопасность — и посмотрим, как их объединить для достижения действительно безопасной и практичной виртуализации.
Классическая виртуализация с использованием KVM и виртуальных машин.
В основе всей этой системы по-прежнему лежит полная виртуализация операционных систем с использованием гипервизоров, таких как KVM, Xen или ESXi . В этом случае на физическом оборудовании работает гипервизор, отвечающий за «разделение» машины на несколько независимых виртуальных машин.
В этой модели каждая виртуальная машина загружает собственное ядро и гостевую операционную систему , полностью отделенные от хоста. Гипервизор (например, KVM, интегрированный в ядро Linux) выступает в роли привратника между оборудованием и виртуальными машинами и отвечает за строгую изоляцию между ними.
Обычно говорят о двух типах гипервизоров: гипервизоры типа 1, или гипервизоры, работающие непосредственно на оборудовании, и гипервизоры типа 2, размещаемые поверх существующей операционной системы. VirtualBox или VMware Workstation относятся к типу 2 , в то время как KVM, управляемый libvirt на сервере Linux, ближе к типу 1, даже несмотря на то, что он использует то же пространство, что и сам хост.
Главное преимущество такого подхода заключается в том, что каждая виртуальная машина имеет свой собственный полный стек: ядро, пользовательское пространство, службы, сеть и хранилище . Это обеспечивает большую гибкость и возможность запуска различных операционных систем и версий ядра на одной и той же физической машине.
Для управления этими виртуализированными платформами в GNU/Linux почти всегда используется libvirt в качестве API оркестрации , поверх которого поддерживаются графические инструменты и инструменты командной строки, или более крупные проекты, такие как OpenStack, которые создают полноценные облака, объединяя виртуальные машины, физические серверы и контейнеры.
Требования к оборудованию и распространенные проблемы с KVM
Перед запуском KVM или использованием решений, зависящих от него (например, некоторых конфигураций Docker Desktop или микро-VM), необходимо убедиться, что процессор поддерживает аппаратную виртуализацию (VT-x на Intel или SVM/AMD-V на AMD) и что она включена в BIOS/UEFI.
В Linux есть быстрая проверка: если команда `grep -E 'svm|vmx' /proc/cpuinfo` ничего не возвращает или эта опция по-прежнему отключена в прошивке, KVM работать не будет. Кроме того, в сценариях, когда мы будем запускать виртуальные машины внутри другой виртуальной машины (вложенная виртуализация), гипервизор верхнего уровня должен явно предоставлять гостевой системе доступ к этим расширениям.
Когда что-то идёт не так, появляются типичные сообщения, например, печально известное всплывающее окно Docker Desktop в RHEL 9 внутри VirtualBox: «KVM не включен на хосте ». Проблема здесь не в Docker или RHEL напрямую, а в том, что виртуальная машина VirtualBox не получает необходимые расширения виртуализации для запуска KVM внутри неё.
Команды типа `modprobe kvm` или `modprobe kvm_amd / kvm_intel` должны загружать модули ядра без сообщений об ошибках. Если `dmesg` не показывает ничего существенного, или `lsmod | grep kvm` показывает только пустые `kvm` и `irqbypass`, а модули, специфичные для процессора, отсутствуют, весьма вероятно, что аппаратная виртуализация не предоставляется гостевой системе или отключена в прошивке.
В таких сценариях, даже если в гостевой системе включены все возможные функции, если VirtualBox не поддерживает вложенную виртуализацию или настроен неправильно, KVM просто не будет работать . Решение заключается в проверке конфигурации хост-гипервизора (VirtualBox, VMware и т. д.) и включении вложенной виртуализации, если она доступна.
Контейнеры: облегченная виртуализация на уровне операционной системы.
В отличие от традиционных виртуальных машин, контейнеры предлагают иной подход: они не эмулируют всё оборудование, а повторно используют ядро хоста и изолируют процессы и ресурсы с помощью функций ядра Linux.
Контейнер по сути представляет собой набор процессов, инкапсулированных с помощью пространств имен и cgroups . Внутри этой «среды» приложение считает, что работает на собственной машине со своей файловой системой, сетевыми интерфейсами и PID, но на самом деле оно использует ядро совместно с хостом.
Контейнеры обеспечивают производительность, близкую к нативной, поскольку не используется второе ядро и полная эмуляция оборудования . Накладные расходы минимальны: их достаточно лишь для монтирования пространств имен, ограничения ресурсов с помощью cgroups и обеспечения соблюдения политик безопасности (seccomp, AppArmor, SELinux и т. д.).
Однако такая конструкция имеет существенное последствие: все рабочие нагрузки используют одно и то же физическое ядро . Серьезная уязвимость в этом ядре может внезапно стать потенциальным вектором выхода для всех контейнеров, работающих на этом узле.
Вот почему часто говорят, что изоляция контейнеров не является «абсолютной», как изоляция виртуальных машин . Если кому-то удастся использовать критическую ошибку ядра, пространства имен перестанут быть эффективным барьером, и границы между контейнерами и хостами могут быть нарушены.
Пространства имен и cgroups: основа изоляции контейнеров.
В Linux изоляция контейнеров строится на двух основных столпах: пространствах имен для разделения системных представлений и cgroups для ограничения и учета ресурсов.
Пространства имен изолируют такие элементы, как PID, сеть, точки монтирования, межпроцессное взаимодействие (IPC), имя хоста (UTS) и пользователей . Таким образом, процесс «видит» только процессы, сетевые интерфейсы, файловую систему и имя хоста своего собственного пространства имен, создавая у него ощущение работы в другой системе.
В свою очередь, cgroups позволяют указать, сколько ресурсов ЦП, памяти, дискового ввода-вывода, количества процессов и т. д. может потреблять контейнер . Это предотвращает неконтролируемую нагрузку, которая может перегрузить весь хост, что крайне важно при совместном использовании узлов командами или проектами.
Однако крайне важно понимать, что одних только пространств имен и cgroup недостаточно для создания полноценной системы безопасности . Они не предотвращают использование уязвимостей ядра, не фильтруют системные вызовы и не управляют детальными политиками доступа. Они являются основой, но их необходимо укреплять.
Разумный способ повысить безопасность среды — это сочетание системных фильтров вызовов с seccomp, политик MAC (AppArmor или SELinux) и резкого ограничения возможностей процессов, в идеале — с использованием пространств имен пользователей и режима без прав root, чтобы минимизировать ущерб в случае, если кому-то удастся выйти из контейнера.
Угрозы безопасности на контейнерных платформах
В реальной контейнерной платформе поверхность атаки распределена по нескольким уровням: ядро и среда выполнения (runc, containerd, CRI-O), цепочка поставок образов, конфигурация контейнера/оркестратора и базовая инфраструктура (облако, хранилище, IAM, сеть).
К распространенным векторам атак относятся уязвимости программного обеспечения в ядре или среде выполнения контейнеров, использование небезопасных или не поддерживаемых образов, ошибки конфигурации в Kubernetes или Docker (привилегированные контейнеры, опасные точки монтирования хоста, чрезмерно открытые сетевые режимы), а также уязвимости безопасности в реестрах или конвейерах CI/CD.
Во многих реальных ситуациях нет какой-то одной «волшебной ошибки», а скорее сочетание известных уязвимостей и небрежной конфигурации : контейнеры, работающие от имени root, использование флага --privileged, монтирование файловой системы хоста внутри контейнера и отсутствие seccomp или MAC.
Ко всему этому добавляется проблема цепочки поставок: общедоступные базовые образы часто содержат уязвимости CVE, устаревшие пакеты и плохо контролируемые зависимости . Вредоносному ПО относительно легко проникнуть внутрь, или скомпрометированная библиотека может оставаться в спящем режиме до тех пор, пока не возникнут подходящие условия для ее выполнения.
Поэтому в производственной среде это не является необязательным: необходимо сканировать образы на наличие уязвимостей с помощью таких инструментов, как Trivy, Clair или Grype , контролировать используемые реестры (Harbor, Quay, GHCR с политиками) и подписывать/проверять образы перед их развертыванием в кластере.
Docker против Podman: влияние на модель угроз
В мире контейнеров приложений Docker и Podman выполняют схожие функции, но их архитектура значительно различается, что меняет модель безопасности, с которой мы работаем на хосте.
В классическом режиме работы с правами root Docker использует центральный демон (dockerd), который запускается от имени root . Интерфейс командной строки взаимодействует с этим демоном через /var/run/docker.sock или по TCP, и именно этот привилегированный процесс создает и уничтожает контейнеры, управляет образами, сетями и томами, а также взаимодействует с реестрами.
Это означает, что любой, кто имеет доступ к сокету Docker, фактически получает root-доступ к хосту , поскольку он может запускать привилегированные контейнеры, монтировать произвольные файловые системы и изменять критически важные параметры конфигурации. Демон фактически становится единой точкой отказа.
С другой стороны, Podman был создан с целью стать бессерверным контейнерным движком, максимально «нативным» для Linux . В нем нет постоянного централизованного процесса; контейнеры управляются пользователем или systemd, а интеграция с последним позволяет управлять контейнерами через сервисные модули.
Этот подход лучше соответствует модели Unix, где «каждый процесс принадлежит пользователю, который его запускает», и позволяет избежать использования демона с высокими привилегиями . Кроме того, Podman обеспечивает совместимость с командами Docker, упрощая миграцию (вы даже можете создать псевдоним `docker=podman` в средах, где не хотите устанавливать Docker CE).
Режим без прав root и пространства имен пользователей
Одним из главных практических достижений в снижении последствий потенциального выхода контейнера за пределы допустимых ресурсов является режим без прав root в сочетании с пространствами имен пользователей . Цель этого подхода — ответить на вопрос: «Что произойдет, если контейнер выйдет за пределы допустимых ресурсов?»
Пространства имен пользователей позволяют переназначить UID 0 контейнера на непривилегированный UID на хосте . Другими словами, внутри контейнера приложение считает себя root-пользователем, но с точки зрения хоста этот процесс на самом деле является обычным пользователем с назначенным диапазоном subuid/subgid.
Таким образом, даже если злоумышленник получит права root внутри контейнера и сумеет реализовать эксплойт ядра или среды выполнения, при возвращении на хост он сделает это как непривилегированный пользователь . Он не сможет перезаписывать исполняемые файлы root, монтировать конфиденциальные файловые системы или манипулировать устройствами, для которых у него нет необходимых привилегий.
Podman с самого начала разрабатывался с учетом этой модели: контейнеризация без прав root по умолчанию, когда это возможно , использование SELinux/AppArmor и cgroups даже без root-прав, а также сильный акцент на минимизацию структурных привилегий.
Docker теперь также предлагает настоящий режим без прав root , в котором dockerd и контейнеры находятся в пространстве имен пользователя, в отличие от более старой функции userns-remap, где демон оставался root. С точки зрения безопасности это предотвращает использование уязвимости в dockerd для автоматического предоставления root-доступа хосту.
Контейнерные сети с Podman: интерфейсы bridge, host, macvlan и rootless.
Еще одним важным элементом для безопасности и практической работы является сетевой уровень контейнеров . Podman предлагает различные механизмы обеспечения связи, уделяя особое внимание сценариям без прав root.
В Podman 4 был представлен netavark, сетевой драйвер, который следует модели CNI и предоставляет контейнерам IP-адреса в мостовых сетях, macvlan и т. д. С помощью netavark корневые контейнеры обычно по умолчанию подключаются к сети под названием «podman», связанной с Linux-мостом (podman0) на хосте, с адресацией 10.88.0.0/16.
Эта стандартная мостовая сеть обеспечивает базовое подключение к интернету с помощью правил SNAT и позволяет открывать порты контейнеров для хоста с помощью `-po --publish`, что генерирует правила DNAT в iptables/nftables. Для совместимости с Docker в этой сети не работает внутренний DNS-сервер.
Мы также можем создавать пользовательские мостовые сети , которые изолируют группы контейнеров друг от друга и обеспечивают внутреннюю DNS-запись. Это позволяет контейнерам разрешать имена внутри своей частной сети, упрощает разделение служб, которые не должны видеть друг друга, и дает больше контроля над MTU, брандмауэрами и другими функциями.
В более продвинутых средах можно использовать сети macvlan или ipvlan для прямого подключения контейнеров к физической сети хоста. Macvlan позволяет контейнерам взаимодействовать друг с другом, в то время как ipvlan, как правило, обеспечивает большую изоляцию; эти варианты полезны в развертываниях, где каждый контейнер должен восприниматься остальной частью сети как реальный хост.
Сети без корневых узлов: slirp4netns и pasta
Когда пользователь не является root-пользователем, ситуация усложняется: пользователь без привилегий не может свободно изменять глобальное сетевое пространство имен или создавать произвольные интерфейсы, поэтому Podman приходится прибегать к решениям в пользовательском пространстве для обеспечения подключения.
Здесь вступает в дело slirp4netns — классический механизм, используемый Podman rootless . Этот проект создает изолированную сетевую среду внутри контейнера и использует модуль slirp ядра для трансляции адресов (NAT), позволяя контейнеру получать доступ к интернету через сеть хоста.
На практике slirp4netns создает TAP-интерфейс в сетевом пространстве имен контейнера (например, tap0 с IP-адресом 10.0.2.100 и шлюзом 10.0.2.2) и маршрутизирует трафик через стек TCP/IP, реализованный в пользовательском пространстве. Это функционально, но добавляет некоторые накладные расходы и определенные ограничения.
Одно из таких ограничений заключается в том, что непривилегированные пользователи не могут использовать порты ниже 1024. Параметр sysctl net.ipv4.ip_unprivileged_port_start указывает, к какому порту они относятся как "непривилегированные" (по умолчанию, 1024), хотя это можно тщательно настроить, если позволяет модель безопасности.
В Podman 5 slirp4netns заменен на pasta в качестве механизма по умолчанию для работы без прав root . Pasta также работает полностью в пользовательском пространстве, но благодаря интерфейсу tap и методам нулевого копирования он обеспечивает производительность сети, близкую к нативной, снижая влияние на задержку и пропускную способность по сравнению с slirp4netns.
Сетевые мосты и хосты в контейнерах без прав root
Даже при работе в режиме без прав root мы можем подключать контейнеры без прав root к сети моста по умолчанию "podman", используя параметр `--network=podman`. Это позволяет использовать возможности Netavark и сопоставлять порты с хостом с параметром `-p`, как и в режиме root.
Также возможно создание пользовательских мостовых сетей в средах без прав root . В этом случае мосты и связанные с ними устройства создаются не в глобальном сетевом пространстве имен хоста, а в пространстве имен пользователя, поэтому эти интерфейсы не будут видны с хоста при выполнении простой команды `sudo ip a`.
Для проверки этих мостов без прав root Podman предлагает команду `podman unshare --rootless-netns` , которая открывает оболочку в сетевом пространстве имен пользователя. Оттуда можно наблюдать за внутренними мостами и проверять прямое подключение к контейнерам.
Эта изоляция оказывает любопытный эффект: Возможно, отсутствует прямое соединение между хостом и IP-адресами этих контейнеров, работающих без прав root.Однако это работает и с портами, сопоставленными с собственным IP-адресом хоста, которые выступают в качестве точки входа в сервис.
Для обмена данными между контейнерами встроенный в пользовательскую мостовую сеть DNS-сервер позволяет им общаться по имени, что значительно упрощает настройку распределенных приложений.
На другом конце находится хост-сеть (--network=host) , где контейнер использует тот же сетевой стек, что и хост, но без собственного IP-адреса. В этом режиме, если контейнер без прав root запускает сервер на порту 8080/tcp, сервис будет напрямую доступен на порту 8080 хоста, что позволяет избежать сопоставления портов, но снижает изоляцию.
Podman Desktop, инструменты для создания образов дисков и локальная оркестрация
Для тех, кто предпочитает более наглядный интерфейс, Podman Desktop предоставляет графическую среду для управления контейнерами, подами, образами и сетевыми конфигурациями на машинах разработчиков, выступая в качестве прямой альтернативы Docker Desktop, но используя движок без демонов.
Образы получаются с помощью команды `podman pull`, которая загружает их из реестров, определенных в файле `/etc/containers/registries.conf` . Если образ указан без имени реестра, Podman пытается найти его в заданном порядке реестров, используя по умолчанию тег `latest`.
В дополнение к интерфейсу командной строки Podman, мы можем использовать skopeo в качестве специализированного инструмента для управления, проверки и копирования образов между реестрами и локальными репозиториями. Такие команды, как copy, delete и sync, позволяют автоматизировать синхронизацию между репозиториями, помечать образы для сборки мусора и перемещать артефакты между средами.
После загрузки образа команда `podman run` позволяет запускать контейнеры, указывая транспорт и путь (по умолчанию используется транспорт Docker, а поиск выполняется в настроенном реестре). Стандартные параметры (-d, -p, --name, --pod и т. д.) подходят как для лабораторных сценариев, так и для более серьезных развертываний.
Ежедневное управление осуществляется с помощью команд `podman ps` для отображения списка контейнеров, `podman stop/start` для остановки или запуска существующих экземпляров и `podman rm` для удаления остановленных контейнеров . Если контейнер был изменен и мы хотим преобразовать его в новый образ, команда `podman commit` сохранит эти изменения под новым именем.
Поды и продвинутые модели выполнения с помощью Podman
Вдохновленный Kubernetes, Podman позволяет группировать контейнеры в поды, которые используют один и тот же сетевой стек и определенные ресурсы . Это идеальный шаблон для таких вещей, как база данных и ее клиент, или несколько микросервисов, которым необходимо взаимодействовать с низкой задержкой.
Команда `podman pod create` создает новый под и возвращает его идентификатор, но по умолчанию под остается остановленным до тех пор, пока не будет запущен связанный с ним контейнер или пока он не будет явно запущен с помощью команды `podman pod start`.
Чтобы узнать, какие поды находятся в системе, используйте команду `podman pod ls` , которая также выведет список INFRA-контейнеров, связанных с каждым из них. Этот специальный контейнер обеспечивает работу сети и других общих ресурсов пода, поэтому количество контейнеров никогда не равно нулю.
Podman предлагает специальные команды для запуска, остановки или перезапуска целых подов (podman pod start/stop/restart) , но мы также можем действовать только над конкретным контейнером внутри пода, не изменяя остальные, поддерживая изоляцию процессов на уровне приложения.
Чтобы добавить больше контейнеров в существующий под, используйте команду `podman run --pod POD_NAME` , следуя тому же синтаксису, что и для отдельных контейнеров. Вы не можете удалить контейнер из пода, не уничтожив его, поскольку членство в поде привязано к его жизненному циклу.
Мониторинг осуществляется с помощью команды `podman ps –pod` или специальных команд для отслеживания процессов всех контейнеров во всех подах , что позволяет быстро определить, к какой группе принадлежит тот или иной контейнер и как распределены сервисы.
Контейнеры, LXC и виртуализация приложений
Хотя Docker и Podman доминируют в обсуждениях, это не единственные варианты контейнеризации в GNU/Linux . Такие решения, как LXC/LXD, systemd-nspawn и Kata Containers, решают эту проблему с несколько иных сторон.
LXC предоставляет очень легковесные системные контейнеры со своим собственным именем хоста, IP-адресом, файловой системой и полной системой инициализации . В сочетании с LXD, который выступает в качестве гипервизора для контейнеров и виртуальных машин, он обеспечивает возможности, очень близкие к работе на нескольких легковесных машинах с производительностью, сравнимой с производительностью физического оборудования.
В то время как Docker/Podman ориентированы на контейнеры приложений, совместимые с OCI , LXC проявляет себя наилучшим образом, когда необходимы постоянные среды с множеством сервисов и процессов, работая как виртуальная машина, но с меньшими накладными расходами и очень естественной интеграцией с ядром Linux.
С точки зрения производительности, LXC может запускать ресурсоемкие корпоративные приложения практически без потерь , что делает его мощным выбором для рабочих нагрузок, чувствительных к задержкам и не требующих строгой изоляции, характерной для традиционных виртуальных машин.
Наряду с этим, получили распространение технологии, ориентированные на микро-виртуальные машины и улучшенную изоляцию, такие как Kata Containers , которая запускает каждый под внутри небольшой виртуальной машины со своим собственным ядром, или gVisor, которая вставляет «песочницу ядра», написанную на Go, между приложением и Linux. Обе технологии уменьшают зависимость от общего ядра и приближают нас к модели безопасности виртуальных машин, сохраняя при этом удобство использования контейнеров.
Рекомендации по повышению безопасности Docker и Podman.
Учитывая все вышесказанное, разумный подход к созданию надежной контейнерной платформы заключается в многоуровневой работе: сегментация с помощью пространств имен/cgroups, политики фильтрации с помощью seccomp и MAC, а также структурное ограничение привилегий в режиме без прав root.
На практике рекомендуется придерживаться стандартной практики запуска контейнеров в режиме без прав root , особенно в многопользовательских средах или в облачных средах с общим доступом. Podman обеспечивает это по умолчанию, а Docker rootless использует тот же принцип, когда требуется совместимость.
Ядро хоста необходимо постоянно обновлять с помощью патчей безопасности . Уязвимости, такие как Dirty COW, Dirty Pipe и другие, устраняются с помощью обновлений, которые необходимо устанавливать достаточно быстро, чтобы не оставлять открытой возможность выхода из контейнеров.
Еще одна весьма эффективная мера — использование неизменяемых, ориентированных на контейнеры дистрибутивов, таких как openSUSE MicroOS или Flatcar Linux . Их доступ к корневому каталогу только для чтения и система атомарных обновлений значительно уменьшают поверхность атаки и упрощают откат изменений в случае возникновения проблем после установки патча.
Что касается конфигурации: в производственной среде не допускаются привилегированные контейнеры, за исключением случаев с крайне жестким контролем; приложения не запускаются от имени root внутри контейнера; по возможности используются файловые системы только для чтения; возможности ограничены минимальным набором функций. Практически обязательны профили SELinux/AppArmor и seccomp, адаптированные к реальным вызовам приложения.
Наконец, нельзя забывать об аспекте мониторинга в режиме реального времени . Такие инструменты, как Falco, Sysdig Secure или Aqua, помогают обнаруживать аномалии, попытки обхода защиты (например, необычный доступ к /proc или /sys), нетипичный сетевой трафик или подозрительные команды, предоставляя время для реагирования до того, как атака достигнет цели.
Сочетание классической виртуализации с KVM, контейнерами, управляемыми Podman (предпочтительно в режиме без прав root), хорошо сегментированными сетями и надежными методами защиты позволяет создать высокогибкую и достаточно безопасную платформу виртуализации. Понимание того, что общее ядро является наиболее важным элементом, использование виртуальных машин или микро-виртуальных машин, когда этого требуют риски или соответствие нормативным требованиям, и тщательное управление цепочкой поставок образов — вот что отличает лабораторную среду от готовой к производству инфраструктуры.