Что бы запретить выполнение приложения в контейнере с правами пользователя root, в контейнере необходимо добавить securityContext.runAsNonRoot: true
или securityContext.runAsUser: НЕ_0
.
По идее, devops должен знать, с правами какого пользователя будет выполняться приложение в контейнере. По этому лучше использовать вариант securityContext.runAsUser
.
Предположим, что пользователи могут деплоить приложения только в определенных namespaces:
- default
- user1
- user2
Создавать отдельную политику для каждого namespace не удобно. Поэтому создадим ClusterPolicy
. За основу возьмём готовую политику с сайта kyverno.io. Там есть много готовых политик. Но большинство из них предполагают только аудит (validationFailureAction: audit
). Что не плохо для исследования работы вашего кластера.
Мы поправим политику под свои нужды. Поменяем название и описание.
Изменим validationFailureAction
с audit
на enforce
. Что бы политика блокировала попытки деплоя манифестов.
В разделе match
определим:
resources.kinds
:Pod
- будем учитывать только манифесты типа Pod. Учтите, что к ним относятся разделыspec.template
у манифестовDeployment
,StatefulSet
,DaemonSet
иReplicaSet
.resources.namespaces
:default
,user1
,user2
- явно определяем namespaces, в которых будет работать правило.resources.operations
:CREATE
,UPDATE
- если не определить этот параметр, то указанные значения - это значения по умолчанию. Но в качестве примера, мы явно указываем параметр и его значения по умолчанию.
validate:
- мы явно указываем, что политика будет проверять манифесты.
message
- сообщение, которое будет выдано в случае нарушения.pattern
- шаблон, что мы будем проверять.
Разберемся с шаблоном.
Тут указывается часть манифеста пода, которую мы хотим проверять. В частности, раздел spec
.
В нём нас интересуют следующие поля:
spec.securityContext
spec.containers[*].securityContext
spec.initContainers[*].securityContext
spec.ephemeralContainers[*].securityContext
В которых должен быть определен параметр runAsUser
. Именно наличие этого параметра и его значение мы хотим контролировать.
С контролем значения все "прозрачно" - оно должно быть больше 0. Мы так и пишем: ">0"
. Про операторы сравнения можно прочитать на сайте проекта kyverno
Дальше необходимо разобраться с якорями (конструкции с круглыми скобками).
=()
- если соответствие якорю найдено в манифесте, будем проверять дальнейшее вложение:
Например:
pattern:
spec:
=(containers):
- securityContext:
runAsGroup: ">0"
Если в манифесте определён параметр spec.containers
- будем дальше смотреть массив контейнеров. В определении контейнеров обязан присутствовать securityContext
, в котом обязательно должен быть определен параметр runAsGroup
, значение которого должно быть больше нуля: >0
Подробно про якоря написано на сейте проекта kyverno.
В политике добавим аналогичное правило, но проверяющее наличие параметра runAsGroup
.
Добавим политику:
kubectl apply -f polices/01-non-root.yaml
Попробуем запустить тестовое приложение:
kubectl apply -f manifests/user-manifests/01-test-app.yaml
В результате получим сообщение об ошибке:
Resource: "apps/v1, Resource=deployments", GroupVersionKind: "apps/v1, Kind=Deployment"
Name: "test-hostpath", Namespace: "default"
for: "manifests/user-manifests/01-test-app.yaml": error when patching "manifests/user-manifests/01-test-app.yaml": admission webhook "validate.kyverno.svc-fail" denied the request:
resource Deployment/default/test-hostpath was blocked due to the following policies
require-run-as-non-root:
autogen-run-as-non-root-group: 'validation error: Running as root is not allowed.
The fields spec.securityContext.runAsGroup, spec.containers[*].securityContext.runAsGroup,
spec.initContainers[*].securityContext.runAsGroup, and spec.ephemeralContainers[*].securityContext.runAsGroup
must be set to a number greater than zero. rule autogen-run-as-non-root-group
failed at path /spec/template/spec/containers/0/securityContext/'
autogen-run-as-non-root-user: 'validation error: Running as root is not allowed.
The fields spec.securityContext.runAsUser, spec.containers[*].securityContext.runAsUser,
spec.initContainers[*].securityContext.runAsUser, and spec.ephemeralContainers[*].securityContext.runAsUser
must be set to a number greater than zero. rule autogen-run-as-non-root-user failed
at path /spec/template/spec/containers/0/securityContext/'
Добавим в манифест Deploymnet, в шаблоне пода:
spec:
template:
spec:
containers:
- name: alpine
securityContext:
runAsUser: 1000
runAsGroup: 1000
И повторим попытку деплоя:
kubectl apply -f manifests/user-manifests/01-test-app.yaml
На это раз, приложение будет запущено.
Удалим приложение:
kubectl delete -f manifests/user-manifests/01-test-app.yaml
Подключение локального диска - это volumes типа NodePath. Т.е. нам надо запретить использовать тома этого типа в манифестах.
За основу возьмем готовую политику с сайта проекта kyverno.
Итоговый файл политики.
В дополнение к применению шаблонов для проверки ресурсов, правило проверки может отклонять запрос на основе набора условий, записанных в виде выражений. Условие отклонения (deny) - это выражение, состоящее из ключа, оператора, значения и необязательного поля сообщения. В отличие от шаблона, когда deny принимает значение true, оно блокирует ресурс. Выражения шаблона, напротив, при значении true разрешают ресурс.
Подробнее про deny можно почитать в документации kyverno.
В нашем случае мы перечисляем те типа томов, которые разрешены. Все, что не разрешено - запрещено.
В поле key
мы используем JMESPath.
При помощи которого выбираем данные из объекта AdmissionReview.
Добавляем политику:
kubectl apply -f polices/02-hostpath.yaml
Обратите внимание на то, что сейчас в манифесте Deployment используется том типа hostPath.
Деплоим приложение:
kubectl apply -f manifests/user-manifests/01-test-app.yaml
Ожидаемо, получаем сообщение об ошибке:
Error from server: error when creating "manifests/user-manifests/01-test-app.yaml": admission webhook "validate.kyverno.svc-fail" denied the request:
resource Deployment/default/test-hostpath was blocked due to the following policies
disable-host-path-volumes:
autogen-restricted-volumes: 'Only the following types of volumes may be used: configMap,
csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, and secret.
Заменим тип на emptyDir и снова задеплоим приложение:
kubectl apply -f manifests/user-manifests/01-test-app.yaml
В этот раз все прошло нормально.
Учтите, что кроме томов типа hostPath, получить доступ к локальным дискам можно при помощи томов типа local. Доступ к таким томам возможен только при помощи PersistentVolume создаваемых статически. Т.е. для их создания нельзя использовать PersistentVolumeClaim. Что облегчает написание соответствующей политики kyverno. Попробуйте самостоятельно написать политику для этого типа PersistentVolume.