Представьте, что ваш контейнерный движок работает от 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

Проблемы файловой системы:

  • Права доступа: кто владеет файлами в образах?

  • Драйверы хранилищаvfsoverlayfuse-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. Бенчмарки: Цена безопасности

Методология тестирования:

  • CPUsysbench cpu run

  • Памятьmbwsysbench memory

  • Сетьiperf3netperf

  • Дискfiobonnie++

Результаты:

  • 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

Комментарии (2)


  1. gotch
    14.11.2025 08:44

    Проблема Docker: демон требует root → единая точка отказа

    Rootless mode | Docker Docs


  1. GritsanY
    14.11.2025 08:44

    Больше похоже на список буллетов для презентации и последующего разъяснения, чем на статью. Либо для тех, кто уже и так глубоко в теме, но зачем им статья, оправдывающая существование rootless-контейнеризации?