Deckhouse Kubernetes Platform (DKP) позволяет управлять безопасностью приложений в кластере с помощью набора политик, соответствующих модели Kubernetes Pod Security Standards и дополнительно расширяемых через встроенные механизмы DKP.
Для реализации политик безопасности в DKP используется Gatekeeper.
Применение Pod Security Standards
В DKP поддерживаются три уровня политики безопасности:
privileged— неограничивающая политика с максимально широким уровнем разрешений;baseline— минимально ограничивающая политика, которая предотвращает наиболее известные и популярные способы повышения привилегий. Позволяет использовать стандартную (минимально заданную) конфигурацию пода;restricted— политика со значительными ограничениями. Предъявляет самые жесткие требования к подам.
Политика по умолчанию
Используемая по умолчанию политика определяется следующим образом:
- в версиях DKP до v1.55 политика по умолчанию —
privileged; - начиная с версии DKP v1.55, политика по умолчанию —
baseline.
При обновлении DKP на версию v1.55 или выше политика по умолчанию не изменится автоматически.
Назначение политики
Варианты назначения политики:
- глобально — с помощью параметра
settings.podSecurityStandards.defaultPolicyмодуляadmission-policy-engine; -
для конкретного пространства имён — с помощью лейбла
security.deckhouse.io/pod-policy=<POLICY_NAME>.Пример команды для назначения политики
restrictedна все поды в пространстве имёнmy-namespace:d8 k label ns my-namespace security.deckhouse.io/pod-policy=restricted
Режимы применения политики
Допустимые режимы применения политик:
deny— запрещает выполнений действий.dryrun— не влияет на выполнение действий и используется для отладки. Информацию о событиях можно посмотреть в Grafana или в консоли с помощью командыkubectl.warn— работает какdryrun, но дополнительно выводит предупреждение с указанием причины, по которой бы произошёл запрет действия в режимеdeny.
По умолчанию, политики Pod Security Standards в DKP применяются в режиме deny.
В этом режиме поды приложений, не удовлетворяющие политикам, не могут быть запущены в кластере.
Как и в случае с назначением политик, режим их применения можно задать:
- глобально — с помощью параметра
settings.podSecurityStandards.enforcementActionмодуляadmission-policy-engine; -
для конкретного пространства имён — с помощью лейбла
security.deckhouse.io/pod-policy-action=<POLICY_ACTION>.Пример команды для установки режима
warnна все поды в пространстве имёнmy-namespace:d8 k label ns my-namespace security.deckhouse.io/pod-policy-action=warn
Расширение политики
Вы можете расширить политики baseline и restricted с помощью шаблонов Gatekeeper,
добавив необходимые проверки к уже существующим.
Чтобы расширить политику, выполните следующее:
- Создайте шаблон проверки с помощью ресурса ConstraintTemplate.
- Примените созданный шаблон к политике
baselineилиrestricted.
Пример шаблона для проверки адреса репозитория с образом контейнера:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
spec:
crd:
spec:
names:
kind: K8sAllowedRepos
validation:
openAPIV3Schema:
type: object
properties:
repos:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package d8.pod_security_standards.extended
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
Пример применения шаблона к политике restricted:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: prod-repo
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaceSelector:
matchLabels:
security.deckhouse.io/pod-policy: restricted
parameters:
repos:
- "mycompany.registry.com"
В этом примере проверяется адрес репозитория в поле image
у всех подов в пространстве имён с лейблом security.deckhouse.io/pod-policy: restricted.
Если адрес в поле image создаваемого пода начинается не с mycompany.registry.com, под создан не будет.
Вспомогательные ресурсы при создании расширенных политик:
- примеры пользовательских политик;
- документация Gatekeeper с информацией о шаблонах и языке политик;
- библиотека Gatekeeper с примерами шаблонов проверок.
Операционные политики
DKP предоставляет механизм создания операционных политик с помощью ресурса OperationPolicy. В операционных политиках задаются требования к объектам в кластере: допустимые репозитории, требуемые ресурсы, наличие проб и т. д.
Команда разработки DKP рекомендует установить следующую политику с минимально необходимым набором требований:
apiVersion: deckhouse.io/v1alpha1
kind: OperationPolicy
metadata:
name: common
spec:
policies:
allowedRepos:
- myrepo.example.com
- registry.deckhouse.ru
requiredResources:
limits:
- memory
requests:
- cpu
- memory
disallowedImageTags:
- latest
requiredProbes:
- livenessProbe
- readinessProbe
maxRevisionHistoryLimit: 3
imagePullPolicy: Always
priorityClassNames:
- production-high
- production-low
checkHostNetworkDNSPolicy: true
checkContainerDuplicates: true
match:
namespaceSelector:
labelSelector:
matchLabels:
operation-policy.deckhouse.io/enabled: "true"
Эта политика задаёт базовые операционные требования к объектам в кластере,
включая разрешённые container registries контейнеров, обязательные ресурсы и пробы, запрет на использование образов с тегом latest,
допустимые классы приоритетов и другие настройки, повышающие безопасность и стабильность работы приложений.
Чтобы назначить данную операционную политику,
примените лейбл operation-policy.deckhouse.io/enabled=true к необходимому пространству имён:
d8 k label ns my-namespace operation-policy.deckhouse.io/enabled=true
Политики безопасности
Используя ресурс SecurityPolicy, вы можете создавать политики безопасности, задающие ограничения на поведение контейнеров в кластере: доступ к host-сетям, привилегии, использование AppArmor и т. д.
Пример политики безопасности:
---
apiVersion: deckhouse.io/v1alpha1
kind: SecurityPolicy
metadata:
name: mypolicy
spec:
enforcementAction: Deny
policies:
allowHostIPC: true
allowHostNetwork: true
allowHostPID: false
allowPrivileged: false
allowPrivilegeEscalation: false
allowedFlexVolumes:
- driver: vmware
allowedHostPorts:
- max: 4000
min: 2000
allowedProcMount: Unmasked
allowedAppArmor:
- unconfined
allowedUnsafeSysctls:
- kernel.*
allowedVolumes:
- hostPath
- projected
fsGroup:
ranges:
- max: 200
min: 100
rule: MustRunAs
readOnlyRootFilesystem: true
requiredDropCapabilities:
- ALL
runAsGroup:
ranges:
- max: 500
min: 300
rule: RunAsAny
runAsUser:
ranges:
- max: 200
min: 100
rule: MustRunAs
seccompProfiles:
allowedLocalhostFiles:
- my_profile.json
allowedProfiles:
- Localhost
supplementalGroups:
ranges:
- max: 133
min: 129
rule: MustRunAs
match:
namespaceSelector:
labelSelector:
matchLabels:
enforce: mypolicy
Чтобы назначить данную политику безопасности,
примените лейбл enforce: "mypolicy" к необходимому пространству имён.
Частичное применение политик
Чтобы применить отдельные политики безопасности, не отключая весь предустановленный набор, выполните следующие шаги:
- Добавьте в необходимое пространство имён лейбл
security.deckhouse.io/pod-policy: privileged, чтобы отключить встроенный набор политик. - Создайте ресурс SecurityPolicy, соответствующий уровню
baselineилиrestricted. В секцииpoliciesукажите только необходимые вам настройки безопасности. - Добавьте в пространство имён дополнительный лейбл, соответствующий селектору
namespaceSelectorв SecurityPolicy.
Пример конфигурации SecurityPolicy, соответствующий уровню baseline:
apiVersion: deckhouse.io/v1alpha1
kind: SecurityPolicy
metadata:
name: baseline
spec:
enforcementAction: Deny
policies:
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowPrivilegeEscalation: true
allowPrivileged: false
allowedAppArmor:
- runtime/default
- localhost/*
allowedCapabilities:
- AUDIT_WRITE
- CHOWN
- DAC_OVERRIDE
- FOWNER
- FSETID
- KILL
- MKNOD
- NET_BIND_SERVICE
- SETFCAP
- SETGID
- SETPCAP
- SETUID
- SYS_CHROOT
allowedHostPaths: []
allowedHostPorts:
- max: 0
min: 0
allowedProcMount: Default
allowedUnsafeSysctls:
- kernel.shm_rmid_forced
- net.ipv4.ip_local_port_range
- net.ipv4.ip_unprivileged_port_start
- net.ipv4.tcp_syncookies
- net.ipv4.ping_group_range
- net.ipv4.ip_local_reserved_ports
- net.ipv4.tcp_keepalive_time
- net.ipv4.tcp_fin_timeout
- net.ipv4.tcp_keepalive_intvl
- net.ipv4.tcp_keepalive_probes
seLinux:
- type: ""
- type: container_t
- type: container_init_t
- type: container_kvm_t
- type: container_engine_t
seccompProfiles:
allowedProfiles:
- RuntimeDefault
- Localhost
- undefined
- ''
allowedLocalhostFiles:
- '*'
match:
namespaceSelector:
labelSelector:
matchLabels:
operation-policy.deckhouse.io/baseline-enabled: "true"
Пример конфигурации SecurityPolicy, соответствующий уровню restricted:
apiVersion: deckhouse.io/v1alpha1
kind: SecurityPolicy
metadata:
name: restricted
spec:
enforcementAction: Deny
policies:
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowPrivilegeEscalation: false
allowPrivileged: false
allowedAppArmor:
- runtime/default
- localhost/*
allowedCapabilities:
- NET_BIND_SERVICE
allowedHostPaths: []
allowedHostPorts:
- max: 0
min: 0
allowedProcMount: Default
allowedUnsafeSysctls:
- kernel.shm_rmid_forced
- net.ipv4.ip_local_port_range
- net.ipv4.ip_unprivileged_port_start
- net.ipv4.tcp_syncookies
- net.ipv4.ping_group_range
- net.ipv4.ip_local_reserved_ports
- net.ipv4.tcp_keepalive_time
- net.ipv4.tcp_fin_timeout
- net.ipv4.tcp_keepalive_intvl
- net.ipv4.tcp_keepalive_probes
allowedVolumes:
- configMap
- csi
- downwardAPI
- emptyDir
- ephemeral
- persistentVolumeClaim
- projected
- secret
requiredDropCapabilities:
- ALL
runAsUser:
rule: MustRunAsNonRoot
seLinux:
- type: ""
- type: container_t
- type: container_init_t
- type: container_kvm_t
- type: container_engine_t
seccompProfiles:
allowedProfiles:
- RuntimeDefault
- Localhost
allowedLocalhostFiles:
- '*'
match:
namespaceSelector:
labelSelector:
matchLabels:
operation-policy.deckhouse.io/restricted-enabled: "true"
Кастомные ресурсы Gatekeeper
Gatekeeper предоставляет расширенные возможности для модификации ресурсов Kubernetes с помощью настраиваемых политик (mutation policies). Эти политики описываются через следующие кастомные ресурсы:
- AssignMetadata — для изменения секции
metadataв ресурсе; - Assign — для изменения других полей, кроме
metadata; - ModifySet — для добавления или удаления значений из списка, например, аргументов для запуска контейнера;
- AssignImage — для изменения параметра
imageресурса.
Подробнее о возможности изменения ресурсов Kubernetes с помощью настраиваемых политик можно прочитать в документации Gatekeeper.
Примеры пользовательских политик Gatekeeper
Здесь приведены примеры политик Gatekeeper, с помощью которых вы можете расширить стандартные механизмы безопасности кластера.
Запрет на удаление узла без указанного лейбла
Операции DELETE обрабатываются Gatekeeper по умолчанию.
Вы можете создать политику Gatekeeper, запрещающую удаление узла без специального лейбла.
В примере ниже используется поле oldObject для проверки лейблов удаляемого узла:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: d8customnodedeleteguard
spec:
crd:
spec:
names:
kind: D8CustomNodeDeleteGuard
validation:
openAPIV3Schema:
type: object
properties:
requiredLabelKey:
type: string
requiredLabelValue:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package d8.custom
is_delete { input.review.operation == "DELETE" }
is_node { input.review.kind.kind == "Node" }
has_required_label {
key := input.parameters.requiredLabelKey
val := input.parameters.requiredLabelValue
obj := input.review.oldObject
obj.metadata.labels[key] == val
}
violation[{"msg": msg}] {
is_delete
is_node
not has_required_label
msg := sprintf("Удаление Node запрещено. Добавьте метку %q=%q.", [input.parameters.requiredLabelKey, input.parameters.requiredLabelValue])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: D8CustomNodeDeleteGuard
metadata:
name: require-node-delete-label
spec:
enforcementAction: warn
match:
kinds:
- apiGroups: [""]
kinds: ["Node"]
parameters:
requiredLabelKey: "admission.deckhouse.io/allow-delete"
requiredLabelValue: "true"
Запрет на выполнение операций kubectl exec и kubectl attach в определённые поды
Вебхук модуля admission-policy-engine направляет запросы CONNECT для pods/exec и pods/attach через Gatekeeper.
Это позволяет создавать пользовательские политики для разрешения или запрета операций kubectl exec и kubectl attach.
Встроенная политика для подов с лейблом heritage: deckhouse
Для защиты системных компонентов под управлением Deckhouse
в модуле admission-policy-engine предусмотрена встроенная политика D8DenyExecHeritage,
которая запрещает выполнение операций kubectl exec и kubectl attach во все поды с лейблом heritage: deckhouse.
Политика не распространяется на следующих пользователей,
которым разрешены операции kubectl exec и kubectl attach в поды с лейблом heritage: deckhouse:
system:sudouser;- сервисные аккаунты из пространств имён
d8-*(system:serviceaccount:d8-*); - сервисные аккаунты из пространств имён
kube-*(system:serviceaccount:kube-*).
Пример пользовательской политики
Вы можете создать собственную политику Gatekeeper
для запрета операций kubectl exec и kubectl attach в определённых пространствах имён.
В примере ниже используются input.review.operation и input.review.resource.resource для проверки операций CONNECT:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: d8customdenyexec
spec:
crd:
spec:
names:
kind: D8CustomDenyExec
validation:
openAPIV3Schema:
type: object
properties:
forbiddenNamespaces:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package d8.custom
is_connect {
input.review.operation == "CONNECT"
}
# requestSubResource предпочтительнее, но на всякий случай падаем в subResource
subresource_is(sub) {
sr := object.get(input.review, "requestSubResource", input.review.subResource)
sr == sub
}
is_exec_or_attach {
input.review.resource.resource == "pods"
subresource_is("exec")
}
is_exec_or_attach {
input.review.resource.resource == "pods"
subresource_is("attach")
}
is_forbidden_namespace {
ns := input.review.namespace
ns == input.parameters.forbiddenNamespaces[_]
}
violation[{"msg": msg}] {
is_connect
is_exec_or_attach
is_forbidden_namespace
msg := sprintf("Exec/attach запрещён в пространстве имён %q", [input.review.namespace])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: D8CustomDenyExec
metadata:
name: deny-exec-in-namespaces
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: ["*"]
kinds: ["*"]
scope: Namespaced
parameters:
forbiddenNamespaces:
- production
- staging
Ключевые данные и проверки, доступные при валидации операций CONNECT:
- Используйте
input.review.operation == "CONNECT"для проверки операцийCONNECT. - Информация о пользователе доступна в
input.review.userInfo.usernameиinput.review.userInfo.groups. - Пространство имён доступно в
input.review.namespace.
Проверка подписи образов
Доступно только в DKP Enterprise edition.
DKP поддерживает проверку подписей образов контейнеров с помощью инструмента Cosign. Проверка позволяет убедиться в целостности и подлинности образов.
Чтобы подписать образ с помощью Cosign, выполните следующее:
-
Сгенерируйте пару ключей:
cosign generate-key-pair -
Подпишите образ:
cosign sign --key <KEY> <IMAGE>
Чтобы включить проверку подписи образов контейнеров в кластере DKP,
используйте параметр policies.verifyImageSignatures ресурса SecurityPolicy.
Пример конфигурации SecurityPolicy для проверки подписи образов контейнеров:
apiVersion: deckhouse.io/v1alpha1
kind: SecurityPolicy
metadata:
name: verify-image-signatures
spec:
match:
namespaceSelector:
labelSelector:
matchLabels:
kubernetes.io/metadata.name: default
policies:
verifyImageSignatures:
- reference: docker.io/myrepo/*
publicKeys:
- |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----
- reference: company.registry.com/*
dockerCfg: zxc==
publicKeys:
- |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----
Согласно данной политике, если адрес какого-либо образа контейнера совпадает со значением параметра reference
и образ не подписан или подпись не соответствует указанным ключам, создание пода будет запрещено.
Пример вывода ошибки при создании пода с образом контейнера, не прошедшим проверку подписи:
[verify-image-signatures] Image signature verification failed: nginx:1.17.2
Использование альтернатив для управления политиками безопасности
Если вместо встроенного механизма управления политиками безопасности в кластере DKP используется альтернативное решение (например, Kyverno), настройте исключения для следующих пространств имён:
kube-system;- все пространства имён с префиксом
d8-*(например,d8-system).
Без этих исключений политики могут блокировать или нарушать работу системных компонентов.