Докер без рут привилегий
В этой статье мы обсудим, как запустить Docker без привилегий root, чтобы лучше управлять безопасностью в ваших контейнерах.
Docker с root правами
Docker запускает свои контейнеры как root. Но нужен ли вашему рабочему контейнеру root права? Ответ: редко. Тем не менее, ваши контейнеры по умолчанию продолжают работать от имени пользователя root. Это может иметь серьезные проблемы с безопасностью. Процесс, который выполняется внутри контейнера от имени пользователя root, фактически является процессом, выполняющимся от имени пользователя root на самом хосте. Это дает возможность злонамеренной попытке получить неограниченный доступ к самому хосту.
Вы можете проверить это самостоятельно, просто используйте следующую команду для любого контейнера, который вы обычно используете:
$ kubectl run -i --tty hello-world --image=hello-world --restart=Never -- sh
/ # ps aux
PID USER TIME COMMAND
1 root 0:10 sh
Очевидно, что в качестве лучшей практики нам следует избегать запуска контейнеров с правами root. Итак, как мы можем это исправить, давайте посмотрим, как мы можем запустить контейнер от пользователя без полномочий root.
Добавление пользователя без прав root в Dockerfile
Создайте пользователя с таким количеством разрешений, которое требуется рабочей нагрузкой внутри контейнера. Вы можете создать пользователя с помощью команды RUN в Dockerfile самого изображения контейнера.
RUN groupadd --gid 5000 newuser \
&& useradd --home-dir /home/newuser --create-home --uid 5000 \
--gid 5000 --shell /bin/sh --skel /dev/null newuser
Выше строка кода создает пользователя, newuser
, вместе с домашним каталогом. Теперь просто добавьте пользователя в свой Dockerfile, как в следующем примере:
FROM ubuntu:18.04
COPY . /myapp
RUN make /myapp
...
USER newuser
CMD python /myapp/hello.py
Начиная со строки 5, каждая команда запускается от имени пользователя newuser
, а не от имени пользователя root. Просто, не правда ли?
Но мы не всегда используем только наши собственные изображения; мы также используем много сторонних изображений и не сможем внедрить в них непривилегированного пользователя, как описано выше.
Эти сторонние образы Docker по умолчанию будут работать от имени пользователя root, если мы не будем с ними что-то делать. Если вы используете изображение из не очень популярного источника, то оно может даже быть встроено с помощью вредоносной команды, которая может поставить под угрозу безопасность вашего кластера.
К нам приходят на помощь контекст безопасности Kubernetes и политики безопасности pod.
Используйте Pod Security Context
Вы можете использовать контекст безопасности модуля, чтобы ограничить выполнение модуля конкретным пользователем без полномочий root. Чтобы указать эти параметры безопасности для модуля, добавьте поле securityContext
в спецификации модуля.
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
securityContext:
runAsUser: 5000
runAsGroup: 5000
volumes:
- name: my-vol
emptyDir: {}
containers:
- name: my-container
image: hello-world
command: ["sh", "-c", "sleep 10 m"]
volumeMounts:
- name: my-vol
mountPath: /data/hello
securityContext:
allowPrivilegeEscalation: false
В приведенной выше спецификации runAsUser
указывает, что любой контейнер внутри модуля будет работать только с идентификатором пользователя 5000
. Это пользователь, которого мы создали специально как непривилегированного. runAsGroup
определяет идентификатор группы всех процессов. Если мы не упомянем об этом, то идентификатор группы будет root (0).
Теперь вы можете создать этот модуль и проверить процессы, запущенные внутри контейнера:
$ kubectl apply -f my-pod.yaml
$ kubectl exec -it my-pod – sh
ps
PID USER TIME COMMAND
1 5000 0:00 sleep 10 m
6 5000 0:00 sh
Как вы можете видеть выше, PID 1 запускается как userID 5000, а не как root.
Использовать политику безопасности Kubernetes Pod
Kubernetes Pod Политика безопасности определяет условия, с которыми модуль должен работать; в противном случае он не будет подготовлен в кластере. Другими словами, если эти условия не будут выполнены, Kubernetes будет препятствовать запуску pod.
Пример PodSecurityPolicy приведен ниже:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: my-psp
spec:
privileged: false
#Required to prevent escalations to root.
allowPrivilegeEscalation: false
allowedCapabilities:
- '*'
volumes:
- 'nfs'
hostNetwork: true
hostPorts:
- min: 8000
max: 8000
hostIPC: true
hostPID: true
runAsUser:
#Require the container to run without root.
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
Эта политика безопасности обеспечивает следующее:
- Запретить запуск контейнера в привилегированном режиме.
- Ограничить контейнер, которому нужен рут.
- Ограничьте контейнер, который обращается к другим файлам, кроме тома NFS.
- Разрешить только контейнерам доступ к хост-порту 100.
Активировать политику:
$ kubectl create -f my-psp.yaml
Проверьте политику:
$ kubectl get psp
NAME PRIV RUNASUSER FSGROUP SELINUX VOLUMES
My-psp false MustRunAsNonRoot RunAsAny RunAsAny [nfs]
Теперь, когда политика создана, вы можете проверить ее, попытавшись запустить контейнер с привилегиями root.
$ kubectl run --image=my-root-container
Политика безопасности модуля запрещает запуск и выдает сообщение об ошибке:
$ kubectl get pods
NAME READY STATUS
my-root-pod 0/1 container has runAsNonRoot and image will run as root
Вывод
В этой статье я выделил риск, связанный с запуском контейнера Docker с настройками по умолчанию для пользователя root. Я также предложил несколько способов преодолеть этот риск.
- Если вы запускаете пользовательский образ, создайте нового непривилегированного пользователя и укажите его в своем Dockerfile.
- Если вы используете сторонние изображения, вы можете установить контекст безопасности на уровне контейнера.
- Еще один способ - создать политику безопасности pod, которая не позволит ни одному контейнеру работать с привилегиями root.