Как стать автором
Поиск
Написать публикацию
Обновить
116.59
MWS
Единый контур решений для цифровой трансформации

Как спрятать сontrol plane Kubernetes от любопытных глаз

Уровень сложностиСредний
Время на прочтение13 мин
Количество просмотров2.4K

Control plane Kubernetes — как сейф с главными ключами. Он управляет кластером, хранит sensitive-информацию и зачастую представляет собой лакомую цель для злоумышленников. 

В этой статье — разбор того, как спрятать control plane в сервисе Managed Kubernetes, предоставляемом в облаке: зачем это нужно, какие варианты существуют и с какими проблемами мы столкнулись на практике. Мы рассмотрим несколько open-source решений, которые протестировали у себя в поисках надёжного способа изолировать control plane-ноды от пользователя и сделать их недоступными для какого-либо внешнего взаимодействия.

Меня зовут Каиржан, я DevOps/Software-инженер в команде разработки MWS Cloud Platform, пишу на Go под Kubernetes и ClusterAPI. Наша команда разрабатывает сервис Managed Kubernetes для публичного облака — от инфраструктуры до собственных провайдеров для ClusterAPI. Поэтому вопрос безопасности control plane (CP) для нас стоит особенно остро. 

А для чего вообще прятать control plane?

Поговорим немного о том, зачем мы вообще решили заморочиться со скрытием CP от пользователя. Какие это даёт плюсы?

В ванильной инсталляции Kubernetes, control plane-ноды содержат sensitive-информацию кластера: сертификаты, ключи от сервис-аккаунтов, ключи от etcd, зачастую саму базу etcd. Все эти секреты могут послужить вектором атаки. Если атакующий завладеет корневым сертификатом от кластера и ключом к нему, он фактически завладеет всем кластером и сможет подделывать identity в конфигах подключения к кластеру и других местах, где используется данный кластер.

Атаке также может быть подвержен любой софт, запущенный на мастерах. К примеру, поды контроллеров CNI/CSI, которые зачастую содержат kubeconfig с расширенными правами, секреты от инфраструктуры и т. п.

Помимо этого, точка входа в кластер — kube-api endpoint, если она светится в незащищённой сети в открытом виде, — тоже может послужить предметом атаки. Тут в качестве примера можно вспомнить уязвимость в гошной библиотеке net/http2, позволяющую провести DoS-атаку на сервис.

Ну и никто не отменял того, что сами ноды, если они видны, могут также стать целью атаки. Например, существовала уязвимость в NodeRestriction-плагине для kube-api, которая позволяла путём подмены лейблов от мастеров переместить workload с мастеров на произвольные (читай — скомпрометированные) ноды и получить доступ к sensitive-информации.

Тут приводим несколько конкретных уязвимостей, эксплуатируемых при открытых control plane нодах и на которые мы наткнулись в процессе наших изысканий:

Компонент

CVE

Описание

kube-apiserver

CVE-2023-5408

Обход NodeRestriction plugin: можно подделать label-ноды, переместить workload c etcd/СР-нод, получив эскалацию привилегий

kube-apiserver

CVE-2023-45288

Уязвимость НТТР/2: удалённый атакующий может перегрузить API-сервер (DoS). 

GKE рекомендовал ограничить доступ к АРІ через список доверенных

ІР-адресов (authorized networks)

etcd

CVE-2023-32082

Утечка ключей lease API: можно узнать имена ключей,
даже без прав на чтение их содержимого

secrets-store CSI driver

CVE-2023-32082

Утечка токенов: токены service-account  попадали в открытом виде в логи

А помимо устранения уязвимостей, что ещё это может дать?

Если в кластере не будет мастеров, то пользователь не сможет разместить на них свою нагрузку. Это позволит снизить количество аварий, когда пользователь прибивает мастера своей нагрузкой, тем самым приводя к отказу весь кластер. С другой стороны, и облачному провайдеру становится проще планировать свои ресурсы под control plane.

Упрощаются процессы поддержки кластера, его регулярного обслуживания, периодического обновления: как версии Kubernetes, так и версий системного софта. Можно проводить обновления по своему расписанию, выделять сервисные окна или дать возможность автоматического обновления пользователем, как это сделано в GKE Autopilot.

Изоляция control plane и kube-api endpoint в принципе является  хорошей практикой в построении Kubernetes-кластеров. Google и Amazon пишут об этом в своих рекомендациях по безопасности кластеров.

Кроме того, за последние несколько лет у облачных провайдеров в принципе появился тренд на хостинг control plane на своей стороне и сокрытие его от клиента (в дальнейшем будем такую инсталляцию называть hosted-CP). Вспомнить хотя бы GKE Autopilot или AWS EKS.

Постановка задачи

В настоящее время наша команда занимается построением сервиса Managed Kubernetes в MWS Cloud Platform — новом публичном облаке MWS. В этом разделе попытаемся описать State of the Art инсталляцию mk8s. Ниже кратко приводим ключевые пункты, которые мы считаем критичными для нашего сервиса:

  1. На фундаментальном уровне используем технологию ClusterAPI для управления полным жизненным циклом клиентского Kubernetes. Исходя из этого, любые наши решения должны быть бесшовно совместимы и нативно интегрированы с ClusterAPI и его библиотеками;

  2. control plane ноды предоставляемых кластеров должны быть скрыты и недоступны для воздействия со стороны клиента. Иными словами, клиенту предоставляется kube-api endpoint и воркера, доступные для размещения нагрузок;

  3. control plane должен быть изолирован как инфраструктурно — ноды не видны и недоступны из кластера, так и по сети — ноды находятся в отдельном сетевом сегменте, отличном от клиентского VPC. В идеале сделать две точки входа в кластер (два endpoint-а на kube-api): сервисную, по которой будет ходить служебный трафик со стороны ClusterAPI, и пользовательскую, которая будет доступна клиенту;

  4. весь процесс бутстраппинга control plane и worker-нод должен быть максимально приближен к ванильному. Управление и все процессы в кластере должны быть неотличимы от Vanilla Kubernetes со стороны пользователя.

Схема архитектуры MK8S в облаке
Схема архитектуры MK8S в облаке

Также для нас важны: 

  • Возможность работы в High Availability-режиме, возможность иметь несколько инстансов мастер-нод и инстансов etcd.

  • Создание CP-провайдером изолированных друг от друга пользовательских control plane. Иными словами, чтобы пользовательские кластеры были разграниченными (например, по неймспейсам на управляющем кластере) друг от друга.

  • Немаловажным является и лёгкость в установке, обслуживании и управлении выбранным control plane-провайдером CAPI. Например, хотелось бы, чтобы его было легко обновлять, он нативно цеплялся к capi-контроллерам и не требовал больших доработок.

  • Из предыдущего пункта также вытекает гибкость к различной инфраструктуре. Не хотелось бы иметь провайдера, который можно развернуть, например, только на Vsphere или только в Openstack. 

Пару слов о Hosted-CP: наша мотивация и тренды

Опишем архитектуру hosted-control-plane в целом: что это и чем отличается от ванильного провижининга Kubernetes тем же ClusterAPI?

Пару слов о том «что такое этот ваш Hosted-CP?»:

Первое и самое важное отличие — собственно, скрытый control plane кластера. Мастера в таком кластере не находятся во владении пользователя, не видны ему и не доступны для взаимодействия. Они даже не находятся в пользовательском сетевом сегменте, а расположены либо в провайдерской сети рядом с управляющим management-кластером, либо даже в отдельном сервисном сегменте.

В примерах, которые мы рассмотрим, control plane представляет собой либо поды, внутри которых крутятся бинари k8s: kube-api, scheduler, controller-manager, либо полноценные виртуалки, созданные инфраструктурным провайдером capi, внутри которых также запущены бинари k8s без регистрации такой инсталляции в качестве ноды кластера.

Воркер-ноды при этом могут быть расположены где угодно, бутстрапиться как угодно нативным процессом через kubeadm.

Зачем это нужно?

  1. Масштабируемость.  При традиционном подходе каждый полноценный кластер съедает много ресурсов — etcd, kube-apiserver, controller-manager, scheduler и т. д. Hosted сontrol planes при определённых конфигурациях позволяют держать тысячи кластеров на одних и тех же физических мощностях.

  2. Стоимость.  Снижение затрат на инфраструктуру — не нужно поднимать отдельные виртуалки или bare metal для каждого control plane.

  3. Изоляция и безопасность. Каждый control plane работает изолированно на уровне сети и прав доступа.

Схема инсталляции Hosted-CP: control plane кластера отделён от workload и находится в провайдерском сегменте
Схема инсталляции Hosted-CP: control plane кластера отделён от workload и находится в провайдерском сегменте

Схожие решения, используемые другими облачными провайдерами

  • Red Hat/OKD — проект  Hypershift: OpenShift на базе hosted control plane. Под капотом — операторы, управляющие отдельными control plane'ами внутри выделенных namespace.  ([Doc], [Doc]) 

  • Amazon EKS by-design прячет мастера в приватной сети и предоставляет только kube-api endpoint. [Doc

  • Google GKE Autopilot  — концептуально очень близкий подход: полностью управляемый control plane без необходимости заботиться о его инфраструктуре.  ([Doc]) 

  • Полезная ссылка про тренд на Hosted-CP от Clastix (Kamaji)

Обзор Open-Source решений

Мы рассмотрели и попробовали три open-source проекта, которые позволяют создать нужную нам инсталляцию Kubernetes. В рамках данной статьи представляем обзор на них.

k0smotron

Первое Hosted-CP решение, рассмотренное нами. 

По сути, это логическое продолжение проекта k0s, который запускает control plane Kubernetes не так, как это делается нативно — через kubelet и контейнеры с apiserver, scheduler, controller-manager и etcd, а как простые исполняемые файлы и процессы на linux-машине. При этом, т. к. у вас отсутствует kubelet и все компоненты запускаются просто как исполняемые файлы, у вас фактически отсутствует понятие мастер-ноды — виртуальная машина, где запущен control plane в кластере не регистрируется в кластере и не видна клиенту.

Что делает k0smotron — по сути он связывает k0s с ClusterAPI. Это оператор, который берёт на себя bootstrap и управление k0s control plane. Причём k0smotron может создавать control plane двух видов: как поды внутри management-кластера, где установлены все компоненты CAPI, и как отдельные виртуальные машины, которые создаются инфрапровайдером в вашем облаке, например в VSphere или Openstack-е, и в которых запускается k0s. В рамках статьи рассматривается инсталляция k0smotron с интеграцией с ClusterAPI и созданием control plane вне контура управляющего кластера.

На рисунке можно увидеть описанную архитектуру k0smotron-а в формате In- и Out- of Cluster
На рисунке можно увидеть описанную архитектуру k0smotron-а в формате In- и Out- of Cluster

В первом случае CP представляет собой просто деплоймент из подов в namespace управляющего кластера. В подах запущены apiserver, scheduler, controller manager, etcd при этом может располагаться там же в виде StatefulSet или быть внешним кластером. Сам деплоймент вывешивается наружу через кубовый сервис — ClusterAPI или Loadbalancer. Этот эндпоинт также можно дополнительно запроксировать и передать пользователю. Соответственно, каждый клиентский кластер — это отдельный deploy в отдельном namespace.

Worker-ноды в свою очередь располагаются в пользовательском сегменте и соединены с apiserver-ом с помощью специальной утилиты — Konnectivity proxy, по сути, это backchannel-прокси, который позволяет выстроить коннект между нодами и apiserver-ом в разных сегментах.

Во втором же случае kosmotron выступает в роли bootstrap провайдера и бутстрапит нам CP на отдельной виртуальной машине. Процесс бутстрапа достаточно прост — он поднимает k0s, который уже запускает нам бинарники Kubernetes в виде linux-процессов. Worker-ноды в обоих случаях ничем не отличаются, находятся в пользовательском сегменте, цепляются через backchannel-прокси.

Собственно, так работает k0smotron. Небольшой спойлер: все решения hosted-CP, которые мы нашли и которые рассматриваем в статье, будут построены примерно схожим образом. Это либо поды в namespace управляющего кластера, либо отдельно стоящие виртуальные машины с кастомным бутстрапом кубера.

Пример манифестов k0smotron control plane
Пример манифестов k0smotron control plane

Так как провайдер совместим с CAPI, он поддерживает стандартные манифесты для Cluster/InfrastructureCluster и K0sControlPlane. На примере показаны манифесты для развёртки k0s control plane на виртуальных машинах Vsphere. Эта инсталляция, которую мы использовали в наших исследованиях.

Тут достаточно применить манифест Cluster, в его ownerRef-ах указать ссылки на control plane провайдера — K0sControlPlane и инфраструктурного — в нашем случае это VsphereCluster и VsphereMachineTemplate.

Манифесты vsphere обрабатываются capi-vsphere-провайдером, на инфраструктуре создаются виртуалки, которые затем подхватывает k0smotron и бутстрапит мастера Kubernetes согласно конфигурации, которую мы описали в соответствующем манифесте. Таким образом получаем мастера на виртуалках, которые не видны пользователю и которые можно создавать либо в провайдерской сети, рядом с capi-кластером, либо в любом произвольном сегменте. Главное — обеспечить связность с capi-кластером.

И всё было бы так радужно и работало бы хорошо, если бы не нюансы…

У k0smotron есть несколько достаточно критичных вещей, которые требуют доработки. 

С точки зрения etcd отсутствует полноценный менеджмент над etcd-кластером. В одномастерной конфигурации кластера проблем нет — k0smotron создаст вам под или vm под мастер. В случае пода etcd будет в виде StatefulSet рядом с деплойментом, в случае VM etcd будет располагаться на той же виртуалке, где и крутится apiserver. 

Сложности начинаются при скейлинге или роллауте. У k0smotron нет механизма управления etcd-кластером. Другими словами, он не умеет добавлять или удалять member'ов etcd, а это очень критично, т. к. etcd использует кворум и, если у вас периодически будут отваливаться члены этого кворума, существует неиллюзорная вероятность положить весь кластер и получить аварию. В документации k0smotron-a об этом даже написано, что в HA-инсталляциях манипуляции с etcd нужно производить самостоятельно ручками.

Также есть некоторые вопросы к совместимости провайдера с нативным flow ClusterAPI, а именно со статусной моделью контроллера машин. Этот контроллер работает следующим образом: он дожидается того, что машины под мастера и под воркеры будут созданы инфрапровайдером и на них забутстрапится кубер. После этого контроллер capi соотнесёт ноду в кубере, providerID и объект capi'шной Machine. Тут-то и возникают трудности. Так как на наших мастерах запускается k0s, нет kubelet-а и ноды не регистрируются, capi не с чем соотносить объекты Machine и они не выходят в конечный статус Ready. Флоу capi нарушается, и статусная модель не соблюдается.

Статусная модель контроллера Machine нарушена
Статусная модель контроллера Machine нарушена

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

Openshift

Архитектура Openshift схожа с k0smotron in-cluster: сontrol plane в виде подов на управляющем кластере, worker-ы в клиентском сегменте
Архитектура Openshift схожа с k0smotron in-cluster: сontrol plane в виде подов на управляющем кластере, worker-ы в клиентском сегменте

Тут одновременно всё просто и всё сложно. Простота в том, что Openshift уже достаточно давно разрабатывает собственное решение для HostedCP, проект называется Hypershift или OKD Hosted Control Plane. Это набор операторов, которые управляют развёрткой CP и etcd.

Принцип работы схож с k0smotron in-cluster: CP — это деплоймент в namespace management-кластера, etcd — это StatefulSet. 

Сложность же в том, что это всё-таки не кубер в чистом виде, а операторы Openshift/OKD, сущности Openshift/OKD и кластер Openshift/OKD. Процесс развёртки похож на ClusterAPI, но это не ClusterAPI. Вместо Cluster тут объекты HostedCluster, вместо MachineDeployment тут NodePool. И на выходе вы, собственно, тоже получите кластер Openshift.

Из приятностей — менеджмент etcd тут есть, кластер также разворачивается оператором и поддерживает управление всем жизненным циклом: созданием, скейлингом, обновлением и удалением. Все процедуры достаточно подробно описаны в документации, и такой анархии, как в k0smotron, в Openshift всё же нет.

Очень подробно на том, как это работает и как это установить, останавливаться не будем, т.к. и у Openshift, и у OKD есть замечательная и подробная документация, Step-By-Step guide о том, как сконфигурировать все операторы и как развернуть Openshift в режиме Hosted-CP.

Pros & Cons OKD Hosted-CP

Из плюсов — всё делается операторами и, соответственно, имеем полный менеджмент над клиентским кластером. Управляется такой кластер централизованно через API и объекты Openshift. Плюс есть достаточно хорошая и подробная документация о том, как это всё развернуть и обслуживать.

Из минусов — это всё же завязка на вендора, неванильный кубер. Другими словами, немного не то, что мы бы хотели предоставлять у себя. Достаточно непростой процесс первичной установки и поддержки всех операторов, по сути, это уже не ClusterAPI и все процессы придётся настраивать и изучать заново. 

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

Kamaji

Теперь перейдём к ещё одному интересному CP-провайдеру — Kamaji. Kamaji — это проект, который очень похож на то, что мы уже рассмотрели. Это оператор, который разворачивает deployment с подами, в которых крутятся бинари Kubernetes. 

Отличие заключается в том, что Kamaji более тесно интегрирован с CAPI, написан capi-provider-kamaji, который полностью берёт на себя управление над Kamaji-сущностью (Tenant Control Plane) и Capi-сущностью (Control Plane). 

Таким образом, в отличие от k0smotron, со статусами здесь всё в порядке, CP в своей основе использует не стандартный CAPI ресурс Machine, а управляется через custom resource KamajiControlPlane.

Схема инсталляции Kamaji
Схема инсталляции Kamaji

Etcd в этом проекте всегда external cluster. Это может быть как StatefulSet (команда kamaji заботливо написала helm-чарт, который разворачивает нам такой etcd), так и внешний кластер, собранный на ваше усмотрение. 

Пример манифеста KamajiControlPlane
Пример манифеста KamajiControlPlane

Чтобы развернуть KamajiCP достаточно поднять кластер etcd, например через helm, и применить стандартный capi-манифест Cluster с рефом на KamajiControlPlane. 

Следует обратить внимание, что секция infrastructureRef необходима только для worker-нод. Другими словами, Kamaji предоставляет нам только control plane, а как, где и с помощью чего разворачивать воркеры — уже наша забота. К примеру, воркеры можно разворачивать на инфре Vsphere, бутстрапить с помощью ванильного kubeadm-провайдера и без проблем таким образом джойнить к подам мастеры, созданные Kamaji.

После применения манифестов получим деплой из подов, содержащих бинари куба и endpoint kube-api, который можно также запроксировать и отдать пользователю как точку входа в кластер и использовать для join-а воркеров. Весь процесс бутстрапа Kubernetes нативный, на kubeadm-процессе, соответственно, у нас создаются все необходимые сервисные RBAC, токены и сервис-аккаунты для join-а воркеров.

Плюсы и минусы Kamaji

Kamaji — это провайдер, в котором частично решены недостатки k0smotron и который, в отличие от Openshift, предоставляет ванильный Kubernetes. Его достаточно просто поставить, и он практически не зависит от инфраструктуры, так как CP разворачивается в единственном варианте — в виде деплоймента в management-кластере. 

Из видимых проблем можно выделить: 

  • отсутствующий менеджмент над etcd, хоть это частично сглаживается тем, что установку StatefulSet при развёртке кластера можно делать по хуку или использовать GitOps-подход. Но всё же полноценное решение отсутствует.

  • На наш взгляд, проект Kamaji относительно мал по сравнению с тем же k0s илиOpenshift. Провайдер для capi содержит баги и иногда может крашиться. Баги регулярно чинят, регулярно появляются новые — в целом при условиях серьёзного продакшена следует отнестись к кодовой базе весьма настороженно.

Подведём итоги

Общая сравнительная таблица провайдеров CAPI
Общая сравнительная таблица провайдеров CAPI

На наш взгляд:

k0smotron — интересный провайдер, самый гибкий из всех, предлагающий построить Kubernetes на собственной инфре либо как поды.

Openshift/OKD — полностью вендорское решение, достаточно зрелое и задокументированное, но достаточно сложное в имплементации. Подойдёт тем, кто и так работает с Openshift, предоставляет данный сервис и хочет захарденить control plane.

Kamaji — что-то среднее. Прост в инсталляции, быстр в установке и развёртке, митигирует некоторые шероховатости k0smotron'а. Независим от инфраструктуры, достаточно иметь кластер Kubernetes с установленными контроллерами capi. Хороший вариант, чтобы потестить hosted-CP с минимальными затратами на разбор документации и подготовку пререквизитов.

А что в итоге взяли мы?

Вкратце: в чистом виде — ничего из перечисленного. Конечно же, серебряной пули нет и какого-либо идеального провайдера, отвечающего всем нашим внутренним нуждам, мы не нашли. Мы решили собрать всё лучшее из представленных вариантов — бутстрап кубера на ВМ на инфре из k0smotron, нативный процесс бутстрапа из kamaji и флоу оператора из Openshift и написать свой собственный kubeadm-провайдер для ClusterAPI.

Пример бутстрапа на kubeadm без регистрации CP-ноды в кластере
Пример бутстрапа на kubeadm без регистрации CP-ноды в кластере

Под капотом — бутстрап на определённых kubeadm-стейджах, используем kubelet как инструмент для запуска статических подов, в конфиге того же kubelet не регистрируем ноду. Наш провайдер сейчас находится в активной разработке, возможно, о нём я или мои коллеги ещё расскажем в будущих статьях. Stay tuned.


Интересные статьи про MWS Cloud Platform:

  1. Как мы строим Development Platform в новом облаке MWS и развиваем inner source-культуру

  2. Как построили собственный Object Storage

  3. DHCP-сервер облачной сети MWS Cloud Platform: раздаём одинаковые IP на разные виртуалки

Теги:
Хабы:
+17
Комментарии0

Публикации

Информация

Сайт
mws.ru
Дата регистрации
Дата основания
Численность
501–1 000 человек
Местоположение
Россия