1. Архитектура LXC, LXD и Incus: эволюция от низкоуровневых инструментов к демонам-гипервизорам
Архитектура LXC, LXD и Incus: эволюция от низкоуровневых инструментов к демонам-гипервизорам
Когда ИБ-специалисты слышат слово «контейнер», первая ассоциация почти всегда связана с Docker или Kubernetes — эфемерными средами для запуска одного приложения (application containers). Однако в мире Linux существует параллельная вселенная системных контейнеров (system containers). В них запускается полноценный процесс инициализации (например, systemd), работают фоновные службы, демоны журналирования и планировщики задач. С точки зрения операционной системы и администратора, системный контейнер ведет себя как полноценная виртуальная машина, но без накладных расходов на эмуляцию оборудования и запуск отдельного ядра.
Понимание того, как именно конструируются эти среды, критически важно для проектирования защищенной инфраструктуры. В отличие от аппаратной виртуализации (KVM/QEMU), где границей безопасности выступает гипервизор, в контейнерах граница безопасности — это само ядро Linux хостовой системы. Эволюция инструментов управления этой границей прошла путь от набора разрозненных утилит до мощных централизованных демонов, что радикально изменило архитектуру и, как следствие, поверхность атаки.
LXC: Фундамент изоляции без центрального управления
Проект LXC (Linux Containers) появился задолго до Docker и стал первой попыткой объединить разрозненные механизмы ядра Linux в единый пользовательский интерфейс.
На фундаментальном уровне контейнеров в Linux не существует. Это абстракция, создаваемая комбинацией трех основных механизмов ядра:
Архитектура классического LXC предельно минималистична и децентрализована. В ее основе лежит библиотека liblxc, написанная на C.
!Сравнение архитектур LXC и Incus
Когда администратор выполняет команду lxc-start -n mycontainer, происходит следующее:
Утилита командной строки напрямую обращается к liblxc. Библиотека читает конфигурационный файл контейнера (обычно из /var/lib/lxc/mycontainer/config), парсит его и выполняет серию системных вызовов (преимущественно clone() с флагами CLONE_NEW* для создания новых пространств имен). После настройки cgroups и применения политик AppArmor/SELinux, процесс выполняет системный вызов execve() для запуска /sbin/init внутри изолированной среды.
С точки зрения безопасности архитектура LXC имеет следующие особенности:
LXC предоставил отличный низкоуровневый API (liblxc), но для корпоративных инфраструктур требовался инструмент оркестрации, который взял бы на себя управление сетью, хранилищами и жизненным циклом сотен инстансов.
LXD: Появление демона-гипервизора
В 2015 году компания Canonical (разработчик Ubuntu) представила LXD. Несмотря на схожесть названий, LXD не заменял LXC, а надстраивался над ним. LXD — это демон, написанный на языке Go, который предоставляет REST API для управления системными контейнерами, используя liblxc под капотом.
Переход от набора утилит к постоянно работающему демону — это тектонический сдвиг в архитектуре. LXD позиционировался как «гипервизор для контейнеров».
В этой модели администратор больше не взаимодействует с конфигурационными файлами напрямую. Клиентская утилита lxc (не путать с утилитами проекта LXC, такими как lxc-start) отправляет HTTPS-запросы к демону LXD. Демон может слушать как локальный Unix-сокет (/var/lib/lxd/unix.socket), так и сетевой TCP-порт.
Архитектурные изменения и их влияние на ИБ:
lxd, может отправлять команды демону. Поскольку демон работает от имени пользователя root и имеет полный доступ к ядру, доступ к сокету эквивалентен прямому доступу к root на хосте. (Этот критический вектор угроз будет детально разобран в следующей главе).LXD сделал системные контейнеры удобными для облачных провайдеров и энтерпрайза. Однако со временем политика Canonical в отношении проекта начала вызывать вопросы у сообщества.
Incus: Форк и современный выбор сообщества
В середине 2023 года произошли события, изменившие ландшафт системных контейнеров. Компания Canonical приняла решение вывести LXD из-под крыла независимой организации LinuxContainers (которая исторически развивала LXC, LXCFS и LXD) и перевести разработку полностью внутрь компании. Это сопровождалось требованием подписания Contributor License Agreement (CLA), что оттолкнуло многих независимых разработчиков.
В ответ на это сообщество LinuxContainers во главе со Стефаном Грабером (Stéphane Graber), создателем и бывшим ведущим разработчиком LXD, объявило о создании форка под названием Incus.
Incus — это не просто переименованный LXD. За первые месяцы независимой разработки из кодовой базы были удалены сотни тысяч строк устаревшего или специфичного для инфраструктуры Canonical кода.
Ключевые архитектурные отличия Incus от последних версий LXD:
.deb и .rpm пакетов, работая напрямую в хостовой системе, что делает аудит его взаимодействия с ядром прозрачным.Сегодня Incus является основным выбором для новых проектов, требующих безопасной контейнеризации. Он сохранил всю мощь клиент-серверной архитектуры LXD, но избавился от вендор-лока и избыточного кода. Выпуск Incus 6.0 LTS закрепил его статус как enterprise-ready решения.
Анатомия запуска: от API к изолированному процессу
Чтобы эффективно защищать инфраструктуру на базе Incus, необходимо понимать пошаговый процесс создания границы безопасности. Что именно происходит в системе, когда администратор отправляет команду на запуск?
!Процесс запуска контейнера Incus
Архитектурно этот процесс пересекает несколько слоев абстракции: от сетевого запроса до низкоуровневых структур ядра.
/1.0/instances/mycontainer/state с указанием действия start. Демон incusd (работающий с правами root) принимает запрос, проверяет TLS-сертификат клиента или токен OIDC, и сверяет права доступа (RBAC).incusd использует Go-биндинги (go-lxc) для передачи конфигурации библиотеке liblxc. На этом этапе управление переходит от кода на Go к коду на C.liblxc вызывает системный вызов ядра clone() с флагами изоляции. Например:CLONE_NEWPID создает изолированное дерево процессов. Процесс внутри контейнера получит PID 1.
- CLONE_NEWNET создает пустой сетевой стек. liblxc переместит второй конец veth-интерфейса в это пространство.
- CLONE_NEWNS создает новое пространство точек монтирования.
pivot_root. В отличие от старого chroot, который просто меняет видимый корневой каталог (и из которого можно сбежать), pivot_root физически отмонтирует корневую файловую систему хоста для данного пространства имен и заменяет ее на смонтированный ZFS-клон контейнера. Хостовая файловая система становится абсолютно недоступной.liblxc применяет профиль Seccomp, блокируя опасные системные вызовы (например, kexec_load, open_by_handle_at). Затем процесс сбрасывает часть Linux Capabilities (например, CAP_SYS_BOOT, CAP_MAC_ADMIN), чтобы даже root внутри контейнера не мог модифицировать ядро. Наконец, процесс переводится в профиль AppArmor, сгенерированный демоном Incus.execve() для запуска /sbin/init (обычно systemd) внутри контейнера.liblxc на хосте завершается, но оставляет легковесный фоновый процесс lxc-monitored, который следит за состоянием PID 1 внутри контейнера и сообщает демону incusd о его остановке или падении.Понимание этой цепочки критично для расследования инцидентов. Если злоумышленник находит уязвимость нулевого дня в ядре, позволяющую обойти pivot_root или Seccomp, он прорывает границу на шагах 5-6. Если же злоумышленник получает доступ к REST API с правами администратора, он легитимно просит демона (шаг 1) создать контейнер без изоляции (отключив Seccomp и AppArmor через конфигурацию), что делает взлом ядра ненужным.
Эволюция от LXC к Incus — это переход от ручного конструирования изоляции к автоматизированной фабрике контейнеров. Демон-гипервизор берет на себя рутину по генерации строгих профилей безопасности и управлению ресурсами, минимизируя риск человеческой ошибки. Однако платой за это удобство становится появление мощного привилегированного процесса, компрометация которого означает полный контроль над инфраструктурой.