Podman: контейнеры без демона и root-доступа

Зачем менять Docker на Podman

Компания «СекьюрДев» — разработка ПО для банков. Аудит безопасности выявил проблему: Docker daemon работает от root. Любой пользователь в группе docker имеет фактически root-доступ к хост-системе.

# Демонстрация проблемы Docker
# Пользователь в группе docker может стать root:
docker run -v /:/host -it alpine chroot /host
# Теперь мы root на хосте. Читаем /etc/shadow, добавляем SSH-ключи...

Podman — drop-in замена Docker без центрального демона, с поддержкой rootless-контейнеров из коробки.

КритерийDockerPodman
АрхитектураКлиент → daemon (root)Без демона (fork/exec)
RootlessЭкспериментальноПо умолчанию
CLI совместимостьalias docker=podman
Composedocker composepodman-compose / podman compose
Pod-ыНетДа (как в Kubernetes)
Systemd интеграцияСлабаяQuadlet (нативная)
ЛицензияApache 2.0Apache 2.0

Установка и базовое использование

# Debian/Ubuntu
apt install podman

# RHEL/Rocky/Alma
dnf install podman

# Проверка
podman --version
# podman version 5.0.0

# 100% совместимость с Docker CLI
podman pull nginx:alpine
podman run -d --name web -p 8080:80 nginx:alpine
podman ps
podman logs web
podman stop web
podman rm web

# Rootless — контейнеры от обычного пользователя
# Не нужен sudo, не нужна группа docker
podman run -d --name myapp -p 8080:80 nginx:alpine
# Работает от uid=1000, без root-привилегий

Для существующих скриптов и CI/CD:

# Создаём alias — все docker-команды будут работать
echo 'alias docker=podman' >> ~/.bashrc

# Или символическую ссылку для скриптов
ln -s /usr/bin/podman /usr/local/bin/docker

# Docker Compose файлы работают без изменений
apt install podman-compose
podman-compose -f docker-compose.yml up -d
Совет: Podman хранит образы в ~/.local/share/containers/ для rootless и /var/lib/containers/ для root. Если переходите с Docker — образы нужно перетянуть: podman pull docker-daemon:myimage:latest.

Quadlet: контейнеры как systemd-сервисы

Quadlet — механизм интеграции Podman с systemd. Вместо podman run с флагами пишете .container-файл, и systemd управляет жизненным циклом контейнера.

# /etc/containers/systemd/webapp.container
[Unit]
Description=Web Application
After=network-online.target

[Container]
Image=docker.io/myapp:latest
PublishPort=8080:3000
Environment=NODE_ENV=production
Environment=DB_HOST=10.0.1.5
Volume=/opt/webapp/data:/app/data:Z
HealthCmd=curl -f http://localhost:3000/health
HealthInterval=30s
AutoUpdate=registry

[Service]
Restart=always
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target
# Активировать
systemctl daemon-reload
systemctl start webapp
systemctl enable webapp

# Логи через journald
journalctl -u webapp -f

# Статус
systemctl status webapp
# ● webapp.service - Web Application
#   Active: active (running) since Mon 2026-04-14 09:00:00 MSK
#   Main PID: 1234 (conmon)

Quadlet для pod (несколько контейнеров в общей сети):

# /etc/containers/systemd/app.pod
[Pod]
PodName=myapp
PublishPort=8080:80
PublishPort=5432:5432

# /etc/containers/systemd/app-nginx.container
[Container]
Pod=myapp.pod
Image=nginx:alpine
Volume=/opt/app/nginx.conf:/etc/nginx/nginx.conf:ro,Z

# /etc/containers/systemd/app-db.container
[Container]
Pod=myapp.pod
Image=postgres:17-alpine
Environment=POSTGRES_PASSWORD=secret
Volume=app-pgdata.volume:/var/lib/postgresql/data

Автообновление образов и безопасность

Podman Auto-Update — автоматическое обновление контейнеров при появлении нового образа в registry:

# Включаем auto-update для контейнера
# В Quadlet: AutoUpdate=registry
# Или при запуске: --label io.containers.autoupdate=registry

# Проверка обновлений (dry-run)
podman auto-update --dry-run

# Применить обновления
podman auto-update

# Cron для регулярных проверок (или systemd timer)
systemctl enable --now podman-auto-update.timer
# По умолчанию — ежедневно в 00:00

Безопасность rootless-контейнеров:

  • User namespaces: root внутри контейнера = uid 100000+ на хосте — никаких привилегий
  • Нет демона: нет сокета, через который можно получить root-доступ
  • SELinux/AppArmor: полная интеграция из коробки
  • Seccomp: профили по умолчанию блокируют опасные syscall-ы
# Проверка user mapping (rootless)
podman unshare cat /proc/self/uid_map
#          0       1000          1   ← root в контейнере = uid 1000 на хосте
#          1     100000      65536   ← остальные uid-ы замаплены в безопасный диапазон

# Сканирование образа на уязвимости
podman image inspect --format '{{.Digest}}' myapp:latest
# + интеграция с Trivy:
trivy image myapp:latest

Миграция «СекьюрДев» с Docker на Podman

Миграция 15 серверов с Docker на Podman за 1 неделю:

  1. День 1-2: установка Podman, конвертация docker-compose.yml → Quadlet-файлы
  2. День 3-4: тестирование на staging, проверка CI/CD (GitLab Runner с Podman executor)
  3. День 5: production-миграция (остановка Docker, запуск Podman)
# Конвертация docker-compose в Quadlet (полуавтоматическая)
podman compose -f docker-compose.yml up -d
# Podman создаст контейнеры
# Затем генерируем Quadlet-файлы:
podman generate systemd --new --files --name myapp
МетрикаDockerPodman
Root-доступ для контейнеровОбязателен (daemon)Не нужен
Поверхность атаки (CVE за год)12 CVE в dockerd3 CVE в Podman
Потребление RAM (idle)~150 MB (daemon)~0 (нет демона)
Прошли аудит безопасностиНет (группа docker = root)Да
Время старта контейнера~1.2 сек~1.1 сек
Ограничения Podman: Docker Swarm не поддерживается (но есть Kubernetes). Некоторые docker-compose фичи (depends_on с условиями) работают не идеально. BuildKit не доступен — используйте Buildah (входит в экосистему Podman).

Часто задаваемые вопросы

На уровне CLI — 99% совместим. alias docker=podman работает для подавляющего большинства команд. Основные различия: Podman не поддерживает Docker Swarm, Docker socket (/var/run/docker.sock) нужно эмулировать через podman.socket, и BuildKit заменяется на Buildah.

Два варианта: 1) podman-compose (Python-пакет, pip install podman-compose) — базовая совместимость. 2) podman compose (встроенная команда с Podman 5.0+) — использует docker-compose бинарник через Podman socket. Оба варианта работают с большинством docker-compose.yml файлов без изменений.

По умолчанию нет — непривилегированные пользователи не могут bind port <1024. Решения: 1) sysctl net.ipv4.ip_unprivileged_port_start=80. 2) Использовать порт 8080 и редирект через firewall. 3) Запускать Podman как root только для контейнеров, которым нужны привилегированные порты.

Да, Podman поддерживает YAML-манифесты Kubernetes: podman play kube deployment.yaml. Можно генерировать Kubernetes YAML из запущенных контейнеров: podman generate kube myapp. Это упрощает переход от локальной разработки к Kubernetes.

Сетевой I/O в rootless на ~5-10% медленнее из-за slirp4netns (user-space сеть). С pasta (новый сетевой backend) разница <2%. Дисковый I/O — без потерь при использовании native overlay. CPU overhead — нулевой.

Нужна помощь с внедрением?

Настроим, оптимизируем и возьмём на поддержку вашу инфраструктуру. 15+ лет опыта, 8 серверов Dell Xeon в дата-центре МТС.

📞 Связаться с нами

Комментарии 0

Оставить комментарий

4 + 8 =