Ответ
Запуск контейнеров от непривилегированного пользователя — критически важная практика безопасности. Я использую несколько подходов в зависимости от контекста.
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ТОЛЬКО на этапе установки пакетов или настройки, а потом, блядь, обязательно переключайся обратно на нормального юзера. Нельзя оставлять это на постоянку — это же просто хитрая жопа для взломщика.
Вот и вся наука. Кажется, мелочь, а на деле разница между «всё работает» и «нас взломали через уязвимость в контейнере» — именно в таких простых вещах. Не будь распиздяем, настрой безопасность с самого начала.