Как запустить контейнер не от пользователя root?
Запуск контейнера не от пользователя root – это практика создания и использования непривилегированного пользователя (с UID отличным от 0) для выполнения процесса внутри контейнера, что предотвращает получение атакующим root-прав на хосте при побеге из контейнера.
Инструкция USER в Dockerfile
Основной способ – создать непривилегированного пользователя и переключиться на него:
Пример Dockerfile с непривилегированным пользователем
FROM eclipse-temurin:21-jre-alpine
# Создаём группу и пользователя
RUN addgroup -S javaapp && adduser -S javaapp -G javaapp
# Создаём директории и назначаем владельца
RUN mkdir -p /app/logs /app/config && \
chown -R javaapp:javaapp /app
WORKDIR /app
# Копируем артефакт
COPY --chown=javaapp:javaapp target/banking-service.jar /app/app.jar
# Переключаемся на непривилегированного пользователя
USER javaapp
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Директива runAsNonRoot в Kubernetes
Kubernetes может принудительно запретить запуск контейнеров от root:
Пример Deployment с runAsNonRoot
apiVersion: apps/v1
kind: Deployment
metadata:
name: banking-service
spec:
replicas: 3
selector:
matchLabels:
app: banking-service
template:
metadata:
labels:
app: banking-service
spec:
securityContext:
runAsNonRoot: true
fsGroup: 1000
containers:
- name: app
image: registry.bank.local/banking-service:1.0.0
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
Если в образе USER указан root, а в Kubernetes выставлен runAsNonRoot: true, кластер откажется запустить под и выдаст ошибку CreateContainerConfigError.
User namespace remapping в Docker daemon
Этот механизм сопоставляет UID 0 внутри контейнера с непривилегированным UID на хосте:
Пример
// /etc/docker/daemon.json
{
"userns-remap": "default"
}
Даже если процесс внутри контейнера работает как root (UID 0), на хосте он будет обычным пользователем (например, UID 100000). Это обеспечивает дополнительный уровень защиты при побеге из контейнера.
Типичные проблемы при запуске не от root
| Проблема | Решение |
|---|---|
Нет прав на запись в /app/logs |
RUN mkdir /app/logs && chown appuser:appgroup /app/logs |
| Порт ниже 1024 (например, 80) | Использовать порт 8080+ или добавить NET_BIND_SERVICE capability |
| Java не может создать временные файлы | Примонтировать tmpfs или emptyDir в /tmp |
| Не удаётся подключиться к БД через unix-socket | Настроить права на сокет или использовать TCP |
Проверка текущего пользователя
Пример
docker exec my-container whoami
docker exec my-container id
# uid=1000(javaapp) gid=1000(javaapp) groups=1000(javaapp)
Вывод
Запуск от непривилегированного пользователя – первая и простейшая мера защиты контейнера. Комбинация инструкции USER в Dockerfile и runAsNonRoot: true в Kubernetes гарантирует, что процесс никогда не запустится от root, даже если кто-то случайно соберёт образ без USER.
На собеседовании: достаточно показать знание трёх уровней защиты:
USERв Dockerfile,runAsNonRootв Kubernetes, и user namespace remapping как дополнительный барьер. Также упомяните типичные проблемы – это демонстрирует практический опыт.