Архитектура Rootless Podman: Полное руководство по контейнерам
Представьте, что ваш контейнерный движок работает от root. Теперь представьте, что злоумышленник нашел уязвимость в демоне. Поздравляю! У него теперь root на всей вашей системе.
Безопасность стала необходимостью, архитектура Docker с его привилегированным демоном похож на пережиток прошлого. Podman предлагает иной подход: контейнеры без root, где каждый процесс запускается от имени обычного пользователя.
Как это работает? Как обычный пользователь может изолировать процессы, создавать сетевые пространства и управлять хранилищем без единой привилегии? Давайте разберемся, что скрывается за rootless Podman :)
Зачем нужны rootless-контейнеры?
Проблема Docker: демон требует root → единая точка отказа
Принцип минимальных привилегий: зачем давать root для изоляции?
Реальные инциденты: кейсы побега из контейнеров через уязвимости в демоне
⠀⠀
1. User Namespace. Механизм трансляции UID/GID для изоляции контейнеров
# Демонстрация: что видит процесс внутри vs снаружи
# Внутри контейнера:
$ id
uid=0(root) gid=0(root)
# Снаружи на хосте:
$ ps aux | grep nginx
user123 12345 ... nginx
Технические детали:
Системный вызов
unshare(CLONE_NEWUSER)
- создание пользовательского namespaceФайлы
/etc/subuid
и/etc/subgid
:
# Формат файла /etc/subuid:
# username:start_uid:uid_count
user123:100000:65536
# │ │ └─ количество UID (65536)
# │ └───────── начальный UID (100000)
# └─────────────────── имя пользователя (user123)
Маппинг в реальном времени: как uid 0 → 100000, uid 1 → 100001
⠀⠀
2. Глубокая настройка subuid/subgid
Проблемы и решения:
Автоматическая настройка через
podman system migrate
Ручная настройка для системных пользователей
Конфликты диапазонов между пользователями
Проверка конфигурации:
$ podman unshare cat /proc/self/uid_map
0 1000 1
1 100000 65536
⠀⠀⠀
3. Сетевые вызовы: slirp4netns vs pasta
Slirp4netns - TUN/TAP в userspace:
Архитектура: процесс-посредник, эмулирующий сетевой стек
Ограничения: производительность NAT, задержки
Диагностика:
podman run --network=slirp4netns
Pasta - прямое пробрасывание сокетов:
Принцип работы: проброс портов без NAT
Производительность: почти нативная скорость
Настройка:
podman run --network=pasta
Сравнительная таблица:
Аспект | slirp4netns | pasta |
|---|---|---|
Производительность | ~80% от нативной | ~95% от нативной |
Совместимость | высокая | требуется современное ядро |
Порты <1024 | через sudo | напрямую |
⠀⠀
4. Хранилище: OverlayFS без root
Проблемы файловой системы:
Права доступа: кто владеет файлами в образах?
Драйверы хранилища:
vfs
,overlay
,fuse-overlayfs
Настройка в
/etc/containers/storage.conf
:
[storage]
driver = "overlay"
graphroot = "/home/user/.local/share/containers/storage"
FUSE-overlayfs - решение для rootless:
Архитектура: FUSE-драйвер для OverlayFS
Производительность: сравнение с привилегированным режимом
Проблемы с hard links: ограничения и обходы
⠀⠀⠀
5. Ограничения и обходные пути
Что не работает в rootless:
Прямой доступ к сетевым устройствам
Изменение системных параметров через
/proc/sys
Монтирование специфичных файловых систем (cgroup2, proc)
Работа с аппаратными устройствами
Обходные решения:
podman run --device
с правильными правамиsysctl через
--security-opt systempaths=unconfined
Портирование приложений под rootless
⠀⠀⠀
6. Бенчмарки: Цена безопасности
Методология тестирования:
CPU:
sysbench cpu run
Память:
mbw
,sysbench memory
Сеть:
iperf3
,netperf
Диск:
fio
,bonnie++
Результаты:
CPU: разница < 2%
Память: дополнительные ~15MB на процесс изоляции
Сеть: slirp4netns -20% throughput, pasta -3%
Диск: fuse-overlayfs -8% IOPS
⠀⠀⠀
7. Реальные кейсы миграции
Миграция с Docker:
# Экспорт из Docker
$ docker save app > app.tar
# Импорт в Podman
$ podman load < app.tar
$ podman run --user 1000:1000 app
Проблемы и решения:
Образы с fixed UID: пересборка или
podman unshare chown
Сетевые порты: перенос на порты >1024
Volumes с правами: настройка правильного владельца
⠀
⠀⠀
Техническая глубина: Анализ системных вызовов
// Пример последовательности вызовов
unshare(CLONE_NEWUSER); // Создание user namespace
unshare(CLONE_NEWNS); // Mount namespace
unshare(CLONE_NEWNET); // Network namespace
setgid_map(...); // Настройка маппинга GID
setuid_map(...); // Настройка маппинга UID
execve("/app"); // Запуск приложения
Отладка и мониторинг:
strace -f podman run alpine
nsenter для диагностики namespaces
cat /proc/self/status | grep Cap
- проверка capabilities
⠀⠀⠀
⠀⠀
Что дальше? Практическое внедрение
Попробуйте прямо сейчас:
# Установите Podman
sudo apt install podman
# Настройте rootless
podman system migrate
# Запустите ваш первый безопасный контейнер
podman run --rm -it -p 8080:8080 \
--name my-app \
--user 1001:1001 \
docker.io/nginx