00. О чём?

Рассмотрим безобидную, на первый взгляд, ситуацию. Вы развернули новый кубернетес кластер, подключили сетку и стораджи, накатили мониторинги и квоты. Казалось бы, осталось нарезать неймспейсы и передать их в пользование коллегам в разработке. Однако, вы в курсе, что разработчики будут запускать команды через kubectl, а значит, по-хорошему, надо накинуть хотя бы "базовые" ограничения на их команды, ведь только так можно оградить себя от большинства проблем в процессе предстоящей эксплуатации кластера.

Но, как-то, под рукой не нашлось готового гайда со списком "базовых" ограничений на кластер, а старый добрый сеньор-админ ушёл на повышение в большую корпорацию? И вот, вы быстрым серчем из поиска залетаете на эту статью. Что ж... не будем заставлять ваших разработчиков ожидать слишком долго, всего каких-то полдня. Хотя, ничто не сможет вам помешать пропустить теоретические банальности этой дружеской беседы и, используя примеры, размещённые в самом её окончании, за час описать необходимые вам политики безопасности.

Конечно вы правы, это будет не то, что бы готовый туториал. Тем не менее, каким бы сложным действием ни казалась настройка кластера, если у вас есть некоторый запас времени, то милости прошу в краткое рассуждение о возможностях кубернетес контроллеров и практических способах применения Validating Admission Policy.

ИНСТРУКЪЦЫЯ ПО НАСТРОЙКЕ КУБЪ-КЛАСТЕРА
...
шаг 998 Подключите Validating Admission Policies
шаг 999 Отдайте, наконецъ, кластеръ в эксплуатацию
...

01. Зачем?

Когда разработчики выполняют kubectl apply -f my-deployment.yaml

  • данная команда направляется в виде реквеста к Kubernetes API серверу

  • если authn / authz пройдены успешно, то реквест форвардит на адмишен контроллеры

  • реквест пройдёт по цепочке все необходимые контроллеры

  • каждый "подходящий" контроллер проведёт свою валидацию

  • если все контроллеры ОК, то, в конце концов, дойдёт до Scheduler

  • Scheduler спланирует ресурсы и запустит создание подов на основе их спецификаций

Когда запрос дойдёт до Scheduler, то спецификации на ресурс будут уже как бы сформированы, а квоты на его создание уже почти как бы выделены. Тут уже "на лету" ничего не поменять, останется только прикрутить что-то дополнительное и запустить в виде нового процесса, что уже само по себе как бы намекает: любое наше начинание на данном этапе вызовет гарантированный оверхэд на ресурсы кластера. Поэтому нам предлагается вмешаться чуть пораньше.

Admission controllers - это важная часть Кубернетес, которая управляет ресурсами и реализует критикал политики безопасности ещё до того, как наступят важные действия и события. Адмишены предназачены:

  • фильтровать создание, изменение ресурсов на этапе реквеста
    // успешно выполнятся только те запросы к API серверу, которые удовлетворяют нашим правилам

  • применять политики безопасности для разных видов ресурсов
    // динамически "на лету" изменять спеки ресурса под нужную специфику (лейблы, аннотации, хелсчеки, инжекторы...)

  • следить за событиями, которые могут зааффектить рантайм кластера
    // "опасные" изменения квот,нод,стораджей,системных сервисов...

02. Но как?

Для этого описываются адмишен правила, в которых указывают, к каким ресурсам следует их применять. Например, команда создать деплоймент задействует цепочку нескольких контроллеров, каждый из которых пройдёт через свои адмишены (которые тоже, в свою очередь, представляют из себя 2 вида контроллеров - mutate и validate):

...
DEPLOYMENT CONTROLLER (mutate, validate, persist to etcd, request to api)
REPLICASET CONTROLLER (mutate, validate, persist to etcd, request to api)
POD CONTROLLER (mutate, validate, persist to etcd, request to api)
...

mutate, validate - это виды адмишен-контроллеров, которые займутся вашим реквестом, если в нём производится тот ЭКШЕН над тем РЕСУРСОМ, которые этот самый контроллер "контролирует" (binding). И именно в такой последовательности:

. mutate веб-хук изменяет манифест из вашего реквеста
// и то, что получится на выходе, оценивает на валидность с Object Schema Validation
// так инжектятся сайдкары, добавляются лейблы, аннотации и т.д.

. validate хуки прогоняют итоговый манифест через свои правила
// реквест отменится, если итогоая версия манифеста не метчит хотя бы с одним из правил,
// хотя, вместо отмены отдельные контроллеры можно скипнуть.

Так работают встроенные (BUILD-IN) адмишены для каждого из контроллеров, которые крутятся в контроллер-менеджере. Они работают по стандартным правилам со стандартными ресурсами кластера, то есть вызывают стандартные веб-хуки. С недавнего времени их список можно посмотреть, подключить, отключить...

kube-apiserver -h | grep enable-admission-plugins
kube-apiserver --enable-admission-plugins=.., .., ...
kube-apiserver --disable-admission-plugins=.., .., ...

А если мы хотим немного расширить список таких правил, скажем, запрещать создание сервисов типа NodePort, или, например, ограничивать создание определённых ресурсов на продовых окружениях по ряду условий?

Чтобы такое реализовать... РАНЬШЕ нужно было использовать Third-party-tool.
Это дорого, но необходимо в случаях, когда важно:
. автоматически инжектить сайдкары для сервис-меш (Linkerd, Istio)
. добавлять аннтоации для обсервабилити (Prometheus-like в специфичных инсталляциях)

Чтобы такое реализовать... ТЕПЕРЬ (начиная с версии 1.26)
. не потребуется Third-party-tool со всеми бонусами
. не придётся ожидать оверхэд, тк не нужно создавать новые веб-хуки
. не придётся возиться с авторизациями, сертами...
. но изучить язык выражений всё же придётся (CEL https://github.com/google/cel-spec)

03. Ну и как?

Максимально кастомизируемо и опционально параметрически. Не сложнее, чем написать правила в Alert Manager.

Для начала проверьте, подключены ли в вашем кластере admission плагины и нужный api
controlplane$ vi /etc/kubernetes/manifests/kube-apiserver.yaml

spec:
  containers:
  - command:
    - kube-apiserver
	- ...
	- --enable-admission-plugins=NodeRestriction              #1
    - --runtime-config=admissionregistration.k8s.io/v1beta1   #2
    - --feature-gates=ValidatingAdmissionPolicy=true          #3
	- ...

В случае, если API-server устанавливали как systemd сервис, то настройки ожидаемо править в systemd unit файлике.
Итак, плагины подключены и проверены. Теперь вам надо создать как минимум 2 ресурса

1. Validating Admission Policy
Полиси - это по сути список правил, описанных с помощью языка выражений CEL (см.выше)

2. ValidatingAdmissionPolicyBinding
Биндинг - это по сути скоуп ресурсов, на который мы хотим распространить данные правила

3. *
Параметр - это всего лишь опция, но вероятно game-changer кандидат.
не исключено, что однажды вам понадобится избавиться от внешних источников для приложений, например, пошарить стейт занятости вашей GPU между разными сервисами внутри кластера.
Вам понадобится создать отдельный ресурс для хранения нужного GPU стейта в виде параметра и продумать логику - кто, как и при каких условиях будет изменять его значение.
Готово. вы привязали поведение разных сервисов к одному общему параметру внутри кластера

Попробуйте создать тестовые полиси и биндинги, используя официальную доку
https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/

По примеру тестовых полиси создайте свои собственные. Для этого накинем несколько вариантов в качестве примера. Только не забудьте вместе с ними обновить биндинги соответствующим образом.

Hidden text
// запретить в деплойменте использовать неизвестные адреса
// реджистри для пулла имиджей в кластер
---
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  variables:
  - name: containers
    expression: "object.spec.template.spec.containers"
  validations:
    - expression: "!(variables.containers.filter(c, c.image.contains('company.ru/'))"
      messageExpression: "'only approved company.ru images are allowed'"


// запретить создавать сервисы с типом NodePort
---
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["*"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["services"]
  validations:
    - expression: "object.spec.type == 'NodePort'"
      reason: Invalid


// запретить создание новых джобов в кастомном ресурсе, пока GPU находится в статусе >0
---
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apiextensions.k8s.io"]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["jobs"]
  validations:
    - expressions: "object.spec.needState < params.stateGPU"
      messageExpression: "'GPU is busy with state ' + string(params.stateGPU)"

Если всё получилось, значит вы уже готовы покрыть политиками безопасности (policy) необходимые вам кейсы и обеспечить защиту вашего кластера. Скорее добавляйте "базовые" полиси и отдавайте уже этот кластер разработчикам, бизнес не ждёт!

Заметьте, вам не пришлось устанавливать third-party-tools и рассчитывать их нагрузку.
А если понадобится накинуть новые ограничения, то вы уже знаете как это делать.

04. И всё?

Постойте... А какие минусы?
На первых этапах, кроме ограничений, которые вам нужны, в принципе, никаких минусов. Но с ростом количества новых полиси, нагрузка на веб-хуки возрастёт, и тогда, скорее всего, вам захочется их масштабировать. Что можно для этого сделать?

  • повысить доступность веб-хуков, задеплоив несколько инстансов webhook server в некий отдельный webhook-namespace

  • обернуть адмишен веб-хуки (точнее, webhook server) в мониторинги для отслеживания их перформанса и доступности

  • продумать чтение и анализ /var/log/webhook-server.log, дабы иметь уверенность в секьюрности использования полиси

  • использовать dry-run валидатор YAML-файлов по адмишен-контроллерам, например, kubeconform

Вам есть, что предложить, добавить, возразить? Милости прошу в комменты, буду рад интересным вариантам из вашего опыта, неожиданным мнениям и фантастическим гипотезам.

gracefull shutdown
gracefull shutdown

Душевное спасибо за ваше внимание, если дочитали до этих строк. Да, и будь то техническая часть в работе компонентов куберенетес, архитектура и безопасность развёртывания приложений в k8s, не стесняйтесь проявлять здоровую критику моих изысканий в комментариях.

P.S. в том числе, не скупитесь на критику в такой непростой области, как орфография текста статьи и корректность изложения сложных понятий на простом русском языке, дабы исключать двусмысленность и мислидинг и помогать делать тексты лучше.

P.P.S. Если у вас уже есть готовый "велосипед" или только идея разработать нечто полезное для работы с кубером, позволяющее упростить работу над конфигурацией куб-приложений, в т.ч расширить возможности куба для специфичных задач ML/AI-operations, in-memory db, кастомных балансировщиков и шедулеров, или просто автоматизировать какую-то часть рутины, то не стесняйтесь писать в личку с предложениями на предмет взаимовыгодного сотрудничества.

Комментарии (4)


  1. angapov
    28.04.2024 06:43
    +5

    В статье ряд неточностей:

    1) Флаг--enable-admission-plugins=NodeRestriction никакого отношения к Validating Admission Policy не имеет.

    2) Версия API admissionregistration.k8s.io/v1alpha1 уже очень сильно устарела. В документации написано использовать как минимум admissionregistration.k8s.io/v1beta1 , но на самом деле начиная с 1.22 по дефолту используется admissionregistration.k8s.io/v1 .

    3) "повысить доступность веб-хуков, задеплоив несколько инстансов webhook server в некий отдельный webhook-namespace". О каких вебхуках вообще идет речь? Validating Admission Policy - это фича самого аписервера, вебхуки тут не используются.

    По самой статье - нужно больше примеров, хотя бы даже из документации.

    По самой фиче - интересная и полезная штука, но мы используем Kyverno. Гораздо более мощный функционал и огромное количество готовых политик.


    1. LinkinBack Автор
      28.04.2024 06:43
      +1

      Благодарю за дельные комментарии. Вы правы, альфа началась с 1.26, и в офиц.доке упоминается бета. Думается мне, благодаря успеху Kyverno и похожих проектов разработчики куба не стали делать обширных комплексных фичей в этом направлении, дабы не конкурировать напрямую с теми, кто немного опережает и, тем самым, варианты решений и развития показывает. Дождёмся очередного релиза, любопытно, какие из них добавят в куб через год.

      Офиц.документация с примерами CEL-выражений является очень подробной, а посему мне хотелось раскрыть саму суть фичи, предложив всего 2-3 годных примера, дабы оценить лёгкость в её использовании. Статья с плотным наборов готовых решений, мне кажется, будет походить на приличный воркшоп или гайд с переводом на русский, чего не хотелось бы. Ну, по-крайней мере, не в этот раз.

      enable NodeRestriction не имеет отношения. Это я всего лишь хотел показать, что validating admission policy такой же адмишен плагин, как и остальные, которые включены по дефолту в списке enable-admission-plugins. Правда, чтобы он заработал, необходимы остальные 2 опции.

      Вебхук-сервер это уже некий взгляд вперёд, к чему можно прийти по части адмишен-плагинов. Накопление множества различных validating admission policy, рано или поздно, приведёт или к переезду на Kyverno , или к созданию собственных адмишен-плагинов, и это, с большой вероятностью, скажется на скорости работы кластера. В доке я нашёл только общие рекомендации о том, как создать собственный сервер для вебхуков и вынести туда Mutating, Validating: kubernetes.io / docs / Reference / API Access Control / Dynamic Admission Control. По этой части я встречал материалы с примерами, кто-то занимается целыми командами, анализирует и масштабирует самописные инстансы


  1. Amareis
    28.04.2024 06:43

    А почему в хабах Rust?


    1. LinkinBack Автор
      28.04.2024 06:43

      Вы правы, я всего лишь вскользь упомянул про сервис-меш Linkerd, который, хоть и имеет компонент прокси, написанный на Rust, но, тему про разработку никак не раскрывает. Если вам станет критично, постараюсь поправить этот лейбл для статьи.