Rootless Podman можно запускать при включённом SELinux в режиме enforcing. Для этого нужны корректные user namespaces, записи subuid и subgid, Unix-права на файлы и подходящий SELinux-контекст для bind mount. Отключение защиты через setenforce 0 не исправляет конфигурацию, а только скрывает причину отказа.
Базовый порядок такой: проверить окружение, запустить минимальный контейнер, отдельно проверить UID/GID mapping, подключить каталог с :z или :Z, затем диагностировать AVC-события через ausearch и audit2why. Локальную политику SELinux создают только после проверки контекста, пути и обычных разрешений.
Команды ниже рассчитаны на Linux с установленными Podman и SELinux. Команды без sudo выполняются от обычного пользователя, под которым будет работать rootless-контейнер. Административные проверки запускаются с правами root.
Как запустить rootless Podman с SELinux в режиме enforcing
Проверка окружения и режима SELinux
Сначала зафиксируйте версии и состояние системы. Версии Podman, libpod, SELinux policy и ядра влияют на сетевой стек, работу user namespaces и обработку volume.
podman version
podman info
id
getenforce
sestatus
cat /etc/subuid | grep "^$(id -un):"
cat /etc/subgid | grep "^$(id -un):"
loginctl user-status "$(id -u)"
Ожидаемый результат команды getenforce - Enforcing. Для rootless-пользователя должны существовать диапазоны в /etc/subuid и /etc/subgid, например:
devops:100000:65536
Первое число задаёт начало диапазона, второе - его размер. Конкретные значения зависят от дистрибутива и политики организации. Если записей нет, Podman может завершить запуск с ошибкой создания user namespace или не сможет корректно отображать пользователей контейнера.
Проверьте аудит SELinux:
sudo systemctl is-active auditd
sudo ausearch -m AVC -ts today
Пустой результат ausearch означает отсутствие найденных AVC за выбранный период, но не доказывает, что будущая операция не вызовет отказ. Для диагностики сначала зафиксируйте время, затем повторите проблемное действие.
Если контейнеры должны запускаться после выхода пользователя из системы, проверьте возможность пользовательского systemd-сеанса и настройте linger только по требованиям эксплуатации:
loginctl show-user "$(id -un)" -p Linger
sudo loginctl enable-linger "$(id -un)"
Для тестового VDS можно использовать облачную инфраструктуру Timeweb Cloud, если требуется изолировать проверку от рабочего сервера. Перед экспериментом зафиксируйте образ, версии пакетов и параметры ядра.
Минимальный rootless-контейнер для проверки
Начните с контейнера без bind mount и публикации портов. Такой тест отделяет проблемы базового запуска от ошибок доступа к файлам и сети.
podman pull docker.io/library/alpine:3.20
podman run --name selinux-rootless-test --rm docker.io/library/alpine:3.20 \
sh -c 'id; cat /proc/self/uid_map; echo container-started'
В выводе появится пользователь контейнера и таблица uid_map. Затем запустите контейнер в фоне:
podman run -d --name selinux-http-test -p 8080:8080 \
docker.io/library/python:3.12-alpine \
python -m http.server 8080 --bind 0.0.0.0
podman ps
podman inspect selinux-http-test
podman port selinux-http-test
curl http://127.0.0.1:8080
Команда podman ps должна показывать работающий контейнер, podman port - соответствие порта хоста и порта контейнера, а curl - ответ HTTP-сервера. После теста удалите контейнер:
podman rm -f selinux-http-test
Этот тест подтверждает базовый rootless-запуск. Для проверки SELinux-контекста нужны операции с файлами хоста.
User namespaces Podman: UID/GID, subuid и subgid
Как проверить отображение идентификаторов
Rootless Podman запускает контейнер в user namespace. Пользователь с UID 0 внутри контейнера не получает UID 0 на хосте. Его действия сопоставляются с диапазоном, выделенным в /etc/subuid и /etc/subgid.
podman run --rm docker.io/library/alpine:3.20 sh -c 'id; cat /proc/self/uid_map; cat /proc/self/gid_map'
podman unshare id
podman unshare cat /proc/self/uid_map
Для работающего контейнера можно проверить фактический процесс:
podman top selinux-http-test user huser group hgroup pid
На хосте сравните владельца каталога и его содержимое:
stat -c '%A %u:%g %n' /srv/containers/app-data /srv/containers/app-data/*
ls -ln /srv/containers/app-data
Для записи в bind mount нужны одновременно доступ к каждому элементу пути, подходящие Unix-разрешения, корректное сопоставление UID/GID и разрешение SELinux. Ошибка в любом из четырёх уровней может выглядеть как Permission denied.
Почему chmod не всегда решает проблему
Диагностируйте отказ по трём независимым направлениям:
- Путь и Unix-права:
namei -l /srv/containers/app-data,ls -ld,stat. - UID/GID mapping:
podman unshare,/proc/self/uid_map, владелец процесса и файлов. - SELinux:
getenforce,ls -Zd,ls -Z, журнал AVC.
chmod 777 не устраняет неправильный SELinux-контекст и не меняет UID/GID mapping. При этом он разрешает чтение, изменение и удаление данных большему числу локальных пользователей. Сначала определите конкретный уровень отказа, затем меняйте только нужную настройку.
Подробное сравнение UID/GID, user namespaces и bind mounts есть в статье о правах доступа контейнеров. Эти принципы применимы и к rootless Podman.
Bind mounts в rootless Podman: выбор между :z и :Z
Общий контекст :z для нескольких контейнеров
Параметр :z просит Podman подготовить каталог для совместного использования контейнерами. Он применяет общий SELinux-контекст, совместимый с доступом контейнеров, обычно с типом container_file_t.
mkdir -p "$HOME/containers/shared-data"
echo "rootless podman" > "$HOME/containers/shared-data/health.txt"
podman run --rm \
-v "$HOME/containers/shared-data:/data:z" \
docker.io/library/alpine:3.20 \
sh -c 'cat /data/health.txt; echo checked > /data/container.txt'
ls -Zd "$HOME/containers/shared-data"
ls -Z "$HOME/containers/shared-data"
Для двух контейнеров, которым нужен общий каталог, используйте одинаковый вариант:
podman run -d --name app-a \
-v "$HOME/containers/shared-data:/data:z" \
docker.io/library/alpine:3.20 sleep 3600
podman run --rm --name app-b \
-v "$HOME/containers/shared-data:/data:z" \
docker.io/library/alpine:3.20 \
cat /data/container.txt
Используйте :z, когда несколько контейнеров действительно должны обращаться к одним данным. Совместный label расширяет круг контейнеров, которым политика разрешает доступ к объекту, поэтому для изолированного хранилища он не нужен.
Приватный контекст :Z для одного контейнера
Параметр :Z назначает приватный SELinux-контекст для каталога, который подключает конкретный контейнер. Это подходит для данных одного приложения.
mkdir -p "$HOME/containers/private-data"
echo "private" > "$HOME/containers/private-data/input.txt"
podman run --rm \
-v "$HOME/containers/private-data:/data:Z" \
docker.io/library/alpine:3.20 \
sh -c 'cat /data/input.txt; echo processed > /data/output.txt'
ls -Zd "$HOME/containers/private-data"
Повторное подключение этого каталога с :Z из другого контейнера может привести к конфликту доступа, потому что приватная метка предназначена для изоляции. Перед relabel проверьте, какие контейнеры, службы и пользователи работают с каталогом:
podman ps -a
findmnt -T "$HOME/containers/private-data"
sudo lsof +D "$HOME/containers/private-data"
Не применяйте :Z к общему каталогу, который используется несколькими контейнерами, без проверки последствий для всех потребителей. Relabel меняет extended attributes файловой системы, а не только параметры запуска Podman.
Когда нужны semanage fcontext и restorecon
Параметры :z и :Z удобны для bind mount, но постоянную настройку пути лучше оформлять через правило file context. Сначала проверьте текущую метку:
ls -Zd /srv/containers/app-data
ls -Z /srv/containers/app-data
Если каталог должен постоянно использоваться контейнерами, задайте правило для конкретного пути и примените его:
sudo semanage fcontext -a -t container_file_t '/srv/containers/app-data(/.*)?'
sudo restorecon -Rv /srv/containers/app-data
ls -Zd /srv/containers/app-data
Пакет с командой semanage может называться policycoreutils-python-utils или аналогично, в зависимости от дистрибутива. После restorecon ожидайте тип container_file_t. Если правило уже существует, используйте -m вместо -a.
Перед изменением системного каталога сделайте резервную копию данных и сохраните исходную метку. Для нестандартных файловых систем проверьте поддержку extended attributes и сохранение SELinux labels при монтировании.
Проброс портов в rootless-контейнере
Проверка публикации и доступности сервиса
Порт приложения внутри контейнера и порт хоста - разные значения. В примере ниже приложение слушает 8080 в контейнере, а Podman публикует его на 8080 хоста:
podman run -d --name web-test -p 8080:8080 \
docker.io/library/python:3.12-alpine \
python -m http.server 8080 --bind 0.0.0.0
podman ps
podman port web-test
podman exec web-test ss -lnt
ss -lntp | grep ':8080'
curl -v http://127.0.0.1:8080
Проверяйте цепочку по порядку:
- Контейнер работает и не перезапускается с ошибкой.
- Приложение слушает нужный порт внутри контейнера.
- Приложение привязано к
0.0.0.0, если доступ нужен через сетевой интерфейс. podman portпоказывает ожидаемое сопоставление.- Firewall хоста разрешает внешний порт.
Для анализа сетевого периметра пригодится практическая методика аудита портов и правил firewall.
Что делать с портами ниже 1024
Rootless-процесс обычно не может напрямую открыть привилегированный порт ниже 1024. Самый простой вариант - опубликовать непривилегированный порт, например 8080, и разрешить доступ к нему через firewall.
podman run -d --name web-test -p 8080:80 \
docker.io/library/nginx:stable-alpine
Для внешнего порта 80 или 443 используйте reverse proxy, работающий с нужными правами и отдельной политикой доступа, а rootless-контейнер оставьте на непривилегированном порту. Системное перенаправление порта допустимо после оценки риска, документирования правила и проверки его поведения после перезагрузки.
Отключение SELinux не решает ограничение привилегированных портов. Это разные механизмы контроля.
Диагностика AVC-отказов: ausearch, audit2why и контексты
Проверка журнала SELinux через ausearch
Сообщение Permission denied само по себе не доказывает отказ SELinux. Сначала зафиксируйте время, повторите операцию и извлеките события AVC:
date
podman exec web-test sh -c 'cat /data/input.txt'
sudo ausearch -m AVC -ts recent -i
Для более узкого поиска используйте время начала диагностики:
sudo ausearch -m AVC -ts 14:30:00 -i
В записи ищите поля scontext, tcontext, tclass, comm, exe, name и действие в блоке { read write getattr open }. Они показывают субъект, объект, класс объекта, процесс, путь и запрещённую операцию.
Сопоставляйте AVC с конкретным контейнером по времени, процессу и пути. Один сервер может одновременно генерировать отказы для нескольких служб.
Интерпретация audit2why
Передайте релевантные события в audit2why:
sudo ausearch -m AVC -ts recent | audit2why
Утилита объясняет возможную причину отказа и показывает, какое правило политики сработало. Ее вывод не означает, что нужно немедленно создать разрешение. Сначала проверьте:
- правильно ли выбран путь в volume;
- используется ли
:zили:Zпо назначению; - имеет ли каталог тип
container_file_t; - проходит ли контейнерный пользователь проверку Unix-разрешений;
- не изменился ли процесс после обновления образа.
Если отказ исчезает после корректного relabel, локальная политика не нужна.
Разделение проблем SELinux, прав и user namespaces
| Симптом | Первичная проверка | Безопасное действие |
|---|---|---|
Permission denied без AVC | namei -l, ls -ln, stat | Исправить владельца, группу или режим доступа с учётом UID/GID mapping. |
| Отказ сопровождается AVC | ausearch -m AVC, ls -Z | Проверить :z, :Z, container_file_t и путь. |
| Файл принадлежит неожиданному UID | cat /proc/self/uid_map, podman unshare | Сопоставить UID контейнера с диапазоном subuid. |
| Путь отсутствует в контейнере | podman inspect, podman exec ... ls | Исправить source и destination в bind mount. |
| Сервис недоступен по сети | podman port, ss -lntp, firewall | Проверить binding приложения, публикацию порта и правила firewall. |
Минимальная локальная SELinux-политика: когда она оправдана
Почему audit2allow не следует применять вслепую
audit2allow преобразует выбранные AVC-события в правила, но не определяет, была ли исходная конфигурация безопасной. Неправильный label, ошибочный путь, неожиданный процесс или лишняя операция записи могут породить формально рабочее, но чрезмерное разрешение.
Не создавайте правило, пока не проверили стандартный контейнерный контекст. Для обычного bind mount сначала исправьте конфигурацию:
podman inspect CONTAINER_NAME
ls -Zd /path/to/data
sudo ausearch -m AVC -ts recent -i
Порядок создания и проверки локального модуля
Если нестандартное приложение действительно требует отдельного разрешения, работайте в тестовой среде и собирайте только AVC, относящиеся к одной проверяемой операции.
sudo ausearch -m AVC -ts recent > /tmp/podman-avc.log
sudo audit2allow -a -w
sudo audit2allow -a -M podman_local_test
sudo semodule -i podman_local_test.pp
sudo semodule -l | grep podman_local_test
Перед установкой изучите сгенерированные .te и .fc файлы. Удалите правила, которые не связаны с целевым контейнером или разрешают широкие действия. После установки повторите тест, проверьте журнал и убедитесь, что приложение получило только необходимый доступ.
Локальный модуль нужно описать в конфигурации или журнале изменений: назначение, дата, контейнер, образ, исходное AVC, разрешённые действия и ответственный за пересмотр.
Как удалить локальное правило
Сначала получите список модулей и удалите только модуль, созданный для этой задачи:
sudo semodule -l | grep podman_local_test
sudo semodule -r podman_local_test
sudo semodule -l | grep podman_local_test
Если менялся постоянный контекст каталога, удалите или скорректируйте правило fcontext, затем восстановите label:
sudo semanage fcontext -l | grep '/srv/containers/app-data'
sudo semanage fcontext -d '/srv/containers/app-data(/.*)?'
sudo restorecon -Rv /srv/containers/app-data
Команду удаления выполняйте только для правила, которое вы точно создавали. Системные правила дистрибутива удалять нельзя.
План проверки и безопасного отката
Чек-лист перед изменениями
Сохраните исходное состояние в отдельный файл с ограниченными правами:
umask 077
mkdir -p "$HOME/podman-selinux-before"
getenforce > "$HOME/podman-selinux-before/getenforce.txt"
podman info > "$HOME/podman-selinux-before/podman-info.txt"
podman ps -a > "$HOME/podman-selinux-before/containers.txt"
ls -l /srv/containers/app-data > "$HOME/podman-selinux-before/ls.txt"
ls -Z /srv/containers/app-data > "$HOME/podman-selinux-before/ls-z.txt"
cat /etc/subuid > "$HOME/podman-selinux-before/subuid.txt"
cat /etc/subgid > "$HOME/podman-selinux-before/subgid.txt"
sudo ausearch -m AVC -ts today > "$HOME/podman-selinux-before/avc.txt"
Для production-каталогов дополнительно зафиксируйте резервную копию данных, список потребителей и текущие правила fcontext. Не меняйте label каталога, если не знаете, какие процессы используют его одновременно с контейнером.
Проверка после исправления
- SELinux остаётся в режиме
Enforcing. - Контейнер запускается после удаления и повторного создания.
- Приложение читает и изменяет только разрешённые пути.
- Фактический UID/GID процесса соответствует ожидаемому mapping.
- Команда
ls -Zпоказывает подходящий контекст. podman portи сетевой тест подтверждают публикацию порта.- После повторения операции не появляются новые релевантные AVC.
- После перезапуска контейнера данные остаются доступными.
podman restart CONTAINER_NAME
podman inspect CONTAINER_NAME
getenforce
sudo ausearch -m AVC -ts recent -i
Откат :z, :Z, fcontext и локальной политики
Перед изменением volume остановите контейнер, чтобы исключить запись во время relabel:
podman stop CONTAINER_NAME
podman inspect CONTAINER_NAME > /tmp/container-inspect-after-stop.json
Для отката параметров :z или :Z удалите контейнер и создайте его с исходной строкой volume. Если вы добавляли правило fcontext, удалите только это правило и примените исходный контекст через restorecon. Если создавали локальный модуль, удалите его через semodule -r.
После отката проверьте владельцев, extended attributes, доступ приложения и отсутствие новых AVC. setenforce 0 не используйте как постоянное исправление. Временно изменить режим можно только по согласованной процедуре диагностики, с фиксацией времени, причины и обязательным возвратом:
sudo setenforce 1
getenforce
Шпаргалка: типовые ошибки SELinux и rootless Podman
Быстрый алгоритм за пять проверок
- Режим SELinux: выполните
getenforce. ПриEnforcingпереходите к проверке контекста, при другом результате сначала восстановите штатный режим согласно политике организации. - Параметры volume: проверьте
podman inspect. Для общего каталога используйте:z, для приватного каталога одного контейнера -:Z. - Контекст файлов: выполните
ls -Zd PATHиls -Z PATH. Для постоянного пути проверьте правилоsemanage fcontextи применитеrestorecon. - Unix-права и mapping: используйте
namei -l,stat,podman unshare,subuidиsubgid. Не заменяйте анализ командойchmod 777. - AVC и сеть: выполните
ausearch -m AVC -ts recent, затем проверьтеaudit2why,podman port, binding приложения и firewall.
| Ошибка | Вероятная причина | Проверка | Исправление |
|---|---|---|---|
| Контейнер не читает bind mount | Неверный label или Unix-права | ls -Z, namei -l, AVC | Исправить :z/:Z, права и mapping. |
После :Z другой контейнер потерял доступ | Приватный контекст конфликтует с общим использованием | podman ps -a, ls -Zd | Согласовать потребителей и выбрать :z либо разделить каталоги. |
Нет записи subuid или subgid | Не настроен диапазон rootless-пользователя | /etc/subuid, /etc/subgid | Добавить диапазоны по правилам дистрибутива и повторить запуск. |
| Порт опубликован, но сервис недоступен | Приложение слушает localhost или закрыт firewall | podman port, ss -lnt, firewall | Исправить binding приложения и сетевое правило. |
| AVC появился после обновления образа | Изменился процесс, путь или тип операции | ausearch, audit2why, inspect | Сравнить версии и контексты, не создавать широкое правило автоматически. |
Хочется выполнить setenforce 0 | Причина отказа не определена | getenforce, ausearch, ls -Z | Разделить проблему прав, mapping, label и сети. |
Для комплексной проверки журналов контейнерной среды используйте материал об аудите Docker и контейнеров. Подход с фиксацией событий, времени и параметров запуска подходит и для rootless Podman.
Рабочая конфигурация rootless Podman с SELinux строится вокруг правильного контекста и ограниченных прав. Сначала проверьте getenforce, mapping и volume, затем повторите операцию и изучите AVC. Локальная политика остаётся последним средством для подтверждённого нестандартного сценария, а каждое изменение должно иметь проверку и понятный откат.