Как указать пользователя при запуске контейнера Docker?

Ответ

Запуск контейнеров от непривилегированного пользователя — критически важная практика безопасности. Я использую несколько подходов в зависимости от контекста.

1. Через Dockerfile (рекомендуемый способ) Создаю пользователя на этапе сборки образа:

FROM alpine:3.18

# Создаем непривилегированного пользователя
RUN addgroup -g 1000 -S appgroup && 
    adduser -u 1000 -S appuser -G appgroup

# Копируем приложение с правильными правами
COPY --chown=appuser:appgroup app /app

# Переключаемся на непривилегированного пользователя
USER appuser

CMD ["/app/start.sh"]

2. При запуске контейнера (для отладки или переопределения)

# По UID:GID
docker run --user 1000:1000 my-app:latest

# По имени пользователя (должен существовать в контейнере)
docker run --user appuser my-app:latest

# С сохранением пользователя хоста для volume
docker run --user $(id -u):$(id -g) -v $(pwd)/data:/data my-app:latest

3. В Docker Compose

version: '3.8'
services:
  app:
    image: my-app:latest
    user: "1000:1000"
    # Или
    user: "appuser:appgroup"

4. Для Kubernetes в манифесте

apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
  containers:
  - name: app
    image: my-app:latest
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]

Ключевые практики безопасности:

  • Никогда не запускаю production-контейнеры от root
  • Использую не-root базовые образы (например, node:18-alpine вместо node:18)
  • Настраиваю правильные права на volume через fsGroup в Kubernetes
  • Проверяю запущенного пользователя:
    docker exec <container-id> whoami
    docker exec <container-id> id
  • Для legacy-приложений, требующих root, использую USER root только на этапе установки, затем переключаюсь на непривилегированного пользователя

Ответ 18+ 🔞

А, ну это же про безопасность в докере! Тема, блядь, архиважная, а то некоторые до сих пор как будто на дворе 2002-й год — всё от рута запускают, а потом удивляются, почему их кластер взломали и майнер крипты поставили. Ёпта, доверия ебать ноль к таким подходам.

Слушай, вот тебе моя наработка, как я это делаю, чтобы не было потом мучительно больно за свой пиздопроебибнальный код.

1. Через Dockerfile (самый правильный путь, ёб твою мать) Тут всё просто — создаёшь юзера прямо при сборке образа, чтобы он там жил на постоянке. Смотри, как красота:

FROM alpine:3.18

# Создаем непривилегированного пользователя
RUN addgroup -g 1000 -S appgroup && 
    adduser -u 1000 -S appuser -G appgroup

# Копируем приложение с правильными правами
COPY --chown=appuser:appgroup app /app

# Переключаемся на непривилегированного пользователя
USER appuser

CMD ["/app/start.sh"]

Вот видишь? Сначала создал группу и юзера, потом скопировал приложение с нужными правами, и в самом конце — бац! — USER appuser. Теперь контейнер стартует сразу от него, а не от рута. Красота, ядрёна вошь!

2. При запуске контейнера (когда нужно быстро поправить или потестить) Иногда надо снаружи указать, от кого запускать. Например, когда отлаживаешь или файлы на volume пишешь, чтобы права не ебали мозг.

# По UID:GID — просто и понятно
docker run --user 1000:1000 my-app:latest

# По имени пользователя (если он уже есть внутри контейнера)
docker run --user appuser my-app:latest

# Вот это вообще магия: берём ID текущего юзера на хосте
docker run --user $(id -u):$(id -g) -v $(pwd)/data:/data my-app:latest

Последний вариант — просто песня, когда нужно, чтобы файлы в volume писались от твоего же пользователя, а не от какого-то левого рута. Иначе потом сидишь и думаешь, какого хуя ты не можешь удалить файлы, которые твой же контейнер нагенерил.

3. В Docker Compose (для местных развертываний) Тут тоже всё прозрачно, в YAML прописываешь и спишь спокойно.

version: '3.8'
services:
  app:
    image: my-app:latest
    user: "1000:1000"
    # Или по имени, если хочешь
    user: "appuser:appgroup"

4. Для Kubernetes (тут уже серьёзно, блядь) В кубере без securityContext вообще делать нехуй. Это как выйти на улицу без штанов — холодно и стыдно.

apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000  # Это чтобы volume нормально монтировались
  containers:
  - name: app
    image: my-app:latest
    securityContext:
      allowPrivilegeEscalation: false  # Важно! Чтобы не вылезти в руты
      capabilities:
        drop: ["ALL"]  # Выкидываем все лишние права нахуй

А теперь главные правила, которые у меня в голове выжжены огнём:

  • Никогда не запускай продакшен-контейнеры от рута. Это просто крик души. Сам от себя охуею, когда вижу такое.
  • Используй не-root базовые образы. Взял node:18-alpine вместо обычного node:18 — и уже спокойнее на душе. Терпения ноль ебать объяснять, почему это важно.
  • Правильно настраивай права на volume. Особенно в кубере через fsGroup, иначе потом будешь танцевать с бубном вокруг пермишенов.
  • Всегда проверяй, кто внутри бегает. Простая команда — и спишь спокойно:
    docker exec <container-id> whoami
    docker exec <container-id> id
  • Для старого говнокода, который орет, что ему нужен root, делай так: используй USER root ТОЛЬКО на этапе установки пакетов или настройки, а потом, блядь, обязательно переключайся обратно на нормального юзера. Нельзя оставлять это на постоянку — это же просто хитрая жопа для взломщика.

Вот и вся наука. Кажется, мелочь, а на деле разница между «всё работает» и «нас взломали через уязвимость в контейнере» — именно в таких простых вещах. Не будь распиздяем, настрой безопасность с самого начала.