Deckhouse Platform (DP) позволяет управлять безопасностью приложений в кластере с помощью набора политик, соответствующих модели Kubernetes Pod Security Standards и дополнительно расширяемых через встроенные механизмы DP.

Для реализации политик безопасности в DP используется Gatekeeper.

Применение Pod Security Standards

В DP поддерживаются три уровня политики безопасности:

  • privileged — неограничивающая политика с максимально широким уровнем разрешений;
  • baseline — минимально ограничивающая политика, которая предотвращает наиболее известные и популярные способы повышения привилегий. Позволяет использовать стандартную (минимально заданную) конфигурацию пода;
  • restricted — политика со значительными ограничениями. Предъявляет самые жесткие требования к подам.

Политика по умолчанию

По умолчанию используется политика baseline. Она применяется к несистемным неймспейсам без лейбла security.deckhouse.io/pod-policy.

В версиях DP до v1.78 политика privileged применялась по умолчанию в кластерах, установленных с версией DP ниже v1.55, или если версию установки не удалось определить. Начиная с DP v1.78 политика по умолчанию — baseline независимо от версии установки кластера.

Если параметр enforcementAction имеет значение Deny, поды, не соответствующие политике baseline, отклоняются при пересоздании. Обновление таких кластеров до DP v1.78 блокируется, пока политика по умолчанию не задана явно.

Если модуль admission-policy-engine выключен, обновление не блокируется. После включения модуля применяется политика baseline.

Чтобы сохранить политику privileged, задайте её явно в параметре settings.podSecurityStandards.defaultPolicy.

Назначение политики

Варианты назначения политики:

  • глобально — с помощью параметра 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 — не влияет на выполнение действий и используется для отладки. Информацию о событиях можно посмотреть в веб-интерфейсе Deckhouse или с помощью команды d8 k.
  • warn — работает как dryrun, но дополнительно выводит предупреждение с указанием причины, по которой бы произошёл запрет действия в режиме deny.

По умолчанию политики Pod Security Standards в DP применяются в режиме 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
    

Политики в системных неймспейсах

Неймспейсы с именами d8-* и kube-* содержат компоненты самой платформы. Политики применяются к ним иначе, чем к прикладным неймспейсам, и это поведение не настраивается. Создаваемые платформой неймспейсы дополнительно помечены лейблом heritage: deckhouse. Неймспейс с этим лейблом, но с другим именем не попадает под политики и мутации, написанные для прикладных неймспейсов; Pod Security Standards ниже действуют по именам.

Каждый неймспейс с именем d8-* или kube-* проверяется по стандарту restricted. Лейбл security.deckhouse.io/pod-policy и параметр settings.podSecurityStandards.defaultPolicy в них не действуют. Нарушение стандарта фиксируется в отчётах безопасности и отображается в веб-интерфейсе Deckhouse, нагрузка при этом запускается. Исключение — неймспейс, в котором владеющий им модуль включил принудительное применение: там стандарты применяются, и нарушающая их нагрузка блокируется.

Настроить эти проверки извне платформы нельзя. Управляющие ими лейблы и сама нагрузка принадлежат владеющему неймспейсом модулю, и Deckhouse Platform возвращает их к заявленному состоянию при следующем применении конфигурации. Модуль сам выводит из-под проверки собственную нагрузку — ресурсом SecurityPolicyException, который поставляется вместе с модулем.

Ресурсы OperationPolicy и SecurityPolicy в системных неймспейсах тоже действуют в режиме warn. Политика с enforcementAction: Deny блокирует запуск нагрузки в прикладных неймспейсах, а в системном неймспейсе только сообщает о нарушении. Лейблы неймспейса на это не влияют: модуль, ужесточающий проверки в собственном неймспейсе, поднимает там Pod Security Standards, а к политике, написанной для прикладной нагрузки, это не относится.

Поэтому политика с enforcementAction: Deny, которая доходит до системных неймспейсов, разворачивается в два ограничения Gatekeeper, видимых в отчётах безопасности и в веб-интерфейсе Deckhouse:

  • собственное имя политики — для прикладных неймспейсов, с тем действием, которое задано в политике;
  • d8-system-default-<политика> — для системных неймспейсов, в режиме warn.

На период перехода к проверке контроллеров политика с enforcementAction: Deny по умолчанию только предупреждает о нарушениях в контроллерах, создающих поды. Контроллеры проверяются ещё одним ограничением, d8-controllers-<политика>, в режиме warn, а ограничение с собственным именем политики проверяет только поды. Параметр модуля podSecurityStandards.controllerValidationEnforcement: Policy или параметр политики spec.controllerValidation.mode: Enforce включает проверку контроллеров с действием политики в одном ограничении.

Префиксы d8-system-default-, d8-pod-security- и d8-controllers- зарезервированы: политика, имя которой начинается с одного из них, отклоняется при создании. По той же причине имя политики ограничено 235 символами: имена производных ограничений должны укладываться в 253 символа, отведённые для имени объекта Kubernetes.

Политика формируется как одно ограничение, когда разделение ничего не меняет:

  • политика использует действие Warn или Dryrun;
  • выбранные политикой неймспейсы не включают системные;
  • политика уже исключает все выбранные ею системные неймспейсы;
  • перечень неймспейсов использует маску в начале имени, например *-system, которую нельзя точно пересечь с d8-*, — тогда единственное ограничение сохраняет действие политики и просто исключает системные неймспейсы;
  • политика выбирает только системные неймспейсы — тогда единственное ограничение сохраняет имя политики и только предупреждает о нарушениях.

Мутации Gatekeeper в системных неймспейсах не применяются, какие бы лейблы на неймспейсе ни стояли. Платформа задаёт параметры своих компонентов сама, поэтому ресурсам Assign и ModifySet не разрешено изменять их параметры.

Расширение политики

Вы можете расширить политики baseline и restricted с помощью шаблонов Gatekeeper, добавив необходимые проверки к уже существующим.

Чтобы расширить политику, выполните следующее:

  1. Создайте шаблон проверки с помощью ConstraintTemplate.
  2. Примените созданный шаблон к политике 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, под создан не будет.

Вспомогательные ресурсы при создании расширенных политик:

Операционные политики

DP предоставляет механизм создания операционных политик с помощью OperationPolicy. В операционных политиках задаются требования к объектам в кластере. Допустимые хранилища образов, требуемые ресурсы, наличие проб и т. д.

Команда разработки DP рекомендует установить следующую политику с минимально необходимым набором требований:

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"

Эта политика задаёт базовые операционные требования к объектам в кластере, включая разрешённые хранилища образов контейнеров, обязательные ресурсы и пробы, запрет на использование образов с тегом 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" к необходимому неймспейсу.

Частичное применение политик

Чтобы применить отдельные политики безопасности, не отключая весь предустановленный набор, выполните следующие шаги:

  1. Добавьте на необходимый неймспейс лейбл security.deckhouse.io/pod-policy: privileged, чтобы отключить встроенный набор политик.
  2. Создайте SecurityPolicy, соответствующий уровню baseline или restricted. В секции policies укажите только необходимые вам настройки безопасности.
  3. Добавьте на неймспейс дополнительный лейбл, соответствующий селектору 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.

Проверка подписи образов

Доступно в следующих редакциях DP: SE+, EE, Ultimate, Certified Core, Certified Pro.

Поддерживается Cosign не выше v2. Версии v3 и выше не поддерживаются.

DP поддерживает проверку подписей образов контейнеров с помощью инструмента Cosign.
Проверка подписи образов контейнеров позволяет убедиться в их целостности (что образ не был изменен после его создания) и подлинности (что образ был создан доверенным источником). Включить проверку подписи образов контейнеров в кластере можно с помощью параметра policies.verifyImageSignatures SecurityPolicy.

При подписании образа в хранилище образов контейнеров создается специальный тег, который содержит подпись образа.
Подпись формируется для хеш-суммы образа (дайджест образа). Например, для образа my-repo/app:latest с хешем sha256:abc123EXAMPLE, в хранилище образов появится образ my-repo/app:sha256-abc123EXAMPLE.sig. Таким образом, процесс подписи образа заключается в расчете и публикации дополнительного тега в хранилище образов контейнеров, без изменения существующего образа.

После подписания образа повторная его загрузка (push) в хранилище не требуется. Необходимо только авторизоваться в хранилище образов контейнеров с правами на запись.

Чтобы подписать образ с помощью Cosign, выполните следующее:

  1. Убедитесь, что используется Cosign версии 2 или ниже.

    Проверить версию: cosign version.

    cosign version
    
  2. Сгенерируйте пару ключей (публичный и приватный):

    cosign generate-key-pair
    
  3. Подпишите образ в хранилище образов контейнеров с помощью сгенерированного приватного ключа:

    cosign sign --key <KEY> <REGISTRY_IMAGE_PATH>
    

    Здесь:

    • <REGISTRY_IMAGE_PATH> — путь к образу, который нужно указать при запуске, например, registry.private.ru/labs/application/image:latest.

Чтобы включить проверку подписи образов контейнеров в кластере DP:

  1. Используйте параметр policies.verifyImageSignatures SecurityPolicy, указав сгенерированный публичный ключ.

    Пример конфигурации SecurityPolicy для проверки подписи образов контейнеров, размещенных в хранилище registry.private.ru по пути /labs/application/:

    apiVersion: deckhouse.io/v1alpha1
    kind: SecurityPolicy
    metadata:
      name: verify-image-test
    spec:
      enforcementAction: Deny
      match:
        namespaceSelector:
          labelSelector:
            matchLabels:
              example-security-policy/enabled: true
      policies:
        allowHostIPC: true
        allowHostNetwork: true
        allowHostPID: false
        allowPrivilegeEscalation: true
        allowPrivileged: false
        allowRbacWildcards: true
        verifyImageSignatures:
          - publicKeys:
              - |-
                -----BEGIN PUBLIC KEY-----
                ...
                -----END PUBLIC KEY-----
            reference: registry.private.ru/labs/application/*
    

    Название лейбла, указанного в match.namespaceSelector.labelSelector.matchLabels, может быть любым. Лейбл в селекторе политики должен совпадать с лейблом соответствующего неймспейса.

    Подробнее — в разделе «Как настроить селекторы политик».

  2. Создайте OperationPolicy, ограничивающий запуск подов из сторонних хранилищ образов:

    apiVersion: deckhouse.io/v1alpha1
    kind: OperationPolicy
    metadata:
      name: test-operation-policy
    spec:
      enforcementAction: Deny
      match:
        namespaceSelector:
          labelSelector:
            matchLabels:
              example-operation-policy/enabled: "true"
      policies:
        allowedRepos:
          - registry.private.ru
    

    Название лейбла, указанного в match.namespaceSelector.labelSelector.matchLabels, может быть любым. Лейбл в селекторе политики должен совпадать с лейблом соответствующего неймспейса.

    Подробнее — в разделе «Как настроить селекторы политик».

  3. Добавьте лейбл на неймспейс, в котором нужно включить проверку подписи (укажите нужный неймспейс):

    d8 k label ns <NAMESPACE> example-security-policy/enabled=true
    
  4. Добавьте лейбл на неймспейс, в котором нужно ограничить запуск подов из сторонних хранилищ образов (укажите нужный неймспейс):

    d8 k label ns <NAMESPACE> example-operation-policy/enabled=true
    
  5. Для проверки работы механизма подписи образов разверните в неймспейсе поды с подписанным и неподписанным образами (укажите нужный неймспейс):

    d8 k -n <NAMESPACE> run signed-pod --image=<SIGNED_IMAGE>
    d8 k -n <NAMESPACE> run unsigned-pod --image=<UNSIGNED_IMAGE>
    

Согласно данной политике, если адрес какого-либо образа контейнера совпадает со значением параметра reference и образ не подписан или подпись не соответствует указанным ключам, создание пода будет запрещено.

Пример вывода ошибки при создании пода с образом контейнера, не прошедшим проверку подписи:

[verify-image-signatures] Image signature verification failed: nginx:1.17.2

Использование альтернатив для управления политиками безопасности

Если вместо встроенного механизма управления политиками безопасности в кластере DP используется альтернативное решение (например, Kyverno), настройте исключения для следующих неймспейсов:

  • kube-system;
  • все неймспейсы с префиксом d8-* (например, d8-system).

Без этих исключений политики могут блокировать или нарушать работу системных компонентов.

Дополнительные ресурсы