“”/
 
5 февраля 2024


Статья 1. Контейнерная инфраструктура, Kubernetes и где тут вписать слово «безопасность»

Цикл "Kaspersky Container Security (KCS)"
Введение
Приветствуем всех читателей на образовательном портале TS University!
С каждым годом контейнерные технологии ставятся все более и более востребованными, поэтому мы решили создать большую обзорную статью, в которой будут затронуты основные понятия, связанные с технологией контейнеризации, а также вопросы безопасности контейнерных сред.

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

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

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

Однако в создании и администрировании микросервисной архитектуры есть определенные нюансы, например, сложности в управлении и тестировании. Важное значение имеет и обеспечение безопасности.

Поэтому в культуру DevOps добавился новый подход — DevSecOps. Его концепция предполагает встраивание проверок безопасности на всех этапах разработки и интеграции ПО. Таким образом, мы можем устранять ошибки на каждом шаге, избегая больших проблем после завершения процесса разработки.
Грамотно сконфигурированная защищенная микросервисная архитектура ускоряет развитие бизнеса, формируя тенденции мирового IT-рынка.
Микросервисы
На самом деле выше описан «распределенный монолит». Микросервисная архитектура представляет собой разбиение приложения на несколько независимых под-приложений (микросервисов), которые взаимодействуют друг с другом с помощью API-вызовов.

Под независимостью понимается, что микросервис, А может быть заменен микросервисом Б с сохранением логики работы без изменения других микросервисов.

Монолитная архитектура предполагает создание одного приложения, в котором модули объединены друг с другом. Все функции такого приложения тесно связаны между собой. Монолитную архитектуру проще развертывать на начальных этапах и легче тестировать, однако ее использование создает и ряд других проблем. В существующую конфигурацию монолитного приложения сложно внести какие-либо изменения. Мы не можем взять отдельный модуль, А и заменить его на другой, так как придется вносить правки в код всего остального приложения.

Мы будем рассматривать именно микросервисную архитектуру, поэтому обозначим ее ключевые преимущества (слова «микросервис», «сервис» и «модуль» здесь синонимы):

  • Каждый сервис мал по сравнению с целым приложением, поэтому в сервисы легко вносить изменения.
  • Масштабируемость. Так как микросервисы являются независимыми, архитектура позволяет выборочно расширить ресурсы для отдельных модулей с высокой нагрузкой без влияния на все остальные сервисы.
  • Гибкий процесс разработки. Сервисы разрабатываются отдельно, поэтому ответственные за них команды могут работать по разным методологиям и использовать собственные стеки технологий.
  • Надежность в плане обновления. При обновлении одного микросервиса не требуется остановка всего приложения.
Микросервисная архитектура решает множество проблем, однако у такой технологии есть и недостатки:
  • Ненадежность в плане эксплуатации. Из-за распределенного характера приложения проблема может возникнуть в любом месте – от рантайма контейнера до отошедшего контакта в патч-корде.
  • Усложнение инфраструктуры, сложности в администрировании и распределении нагрузки между серверами.
Подробнее о принципах работы микросервисной архитектуры и ее особенностях вы можете узнать из курса «Основы виртуализации»
Контейнеры
Контейнер — это абстракция на уровне приложений.

До появления технологии виртуализации под каждый элемент необходимо было выделять физический сервер. Затем с развитием аппаратной виртуализации появилась возможность создания нескольких виртуальных машин на одном физическом сервере, что позволило достичь эффективного распределения ресурсов. При аппаратной виртуализации эмулируется полноценный ПК, у которого есть виртуальные процессоры, оперативная память, диски и сетевые адаптеры.

Однако если перед нами стоит задача запуска определенного приложения, изолированного от других, то в этом случае использование виртуальной машины будет нецелесообразным. Нужно изолировать лишь приложение, а не целую операционную систему.
В результате появилась технология виртуализации приложений, которая позволяет отдельно от основной операционной системы запускать приложения в пользовательских пространствах (контейнерах). Контейнер представляет собой некую «обертку», в которой содержится все необходимое для его запуска: легковесный образ ОС (базовый слой), исполняемые файлы и зависимости.

Контейнеры создаются из образов (можно рассматривать образ как шаблон контейнера) и запускаются на операционной системе хоста при помощи среды запуска контейнеров («легковесный гипервизор» — Container Engine). Один запущенный контейнер — это один запущенный процесс, который логически изолирован от других процессов. Образы создаются разработчиками и могут загружаться как в локальные репозитории, так и в публичные (например, в Docker Hub).
Краткую выжимку о том, что такое контейнеры, можно посмотреть в видео из цикла «Основы виртуализации»
Оркестрация контейнеров и Kubernetes
В настоящий момент микросервисная архитектура крупных компаний состоит из нескольких сотен контейнеров. Администрировать такую инфраструктуру вручную или даже с помощью скриптов нереально, поэтому были созданы системы оркестрации, решающие проблемы планирования, связности, масштабируемости и управления контейнерами.

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

Kubernetes (K8s) — это open source фреймворк для оркестрации контейнерных приложений, являющийся стандартом среди всех прочих систем оркестрации.
Далее мы кратко опишем архитектуру K8s и его компоненты.
Архитектура Kubernetes
K8s работает по принципу «master-slave», где есть мастер-ноды (control plane), контролирующие работу кластера, и рабочие ноды (воркеры, worker nodes), на которых и запускаются контейнеры.

Control Plane состоит из следующих компонентов:
  • API Server: связующее звено кластера. Kube-api-server аутентифицирует пользователей, взаимодействует (I/O) с базой данных etcd и позволяет остальным компонентам кластера взаимодействовать между собой;
  • База данных etcd: в ней хранится состояние компонент кластера, метаданные и различные секреты;
  • Controller Manager: контролирует состояние рабочих узлов кластера и отвечает за перезапуск подов на доступных нодах. При выходе ноды из строя помечает ее как недоступную;
  • Scheduler: планировщик, отслеживающий поды, которым не были назначены рабочие узлы. Компонент, в соответствии с настроенными правилами, выбирает доступные ноды, на которых будут запускаться поды
В свою очередь, на рабочих узлах есть:
  • Kubelet: агент K8s на рабочем узле, который регистрирует ноду в кластере, запускает контейнеры внутри подов, осуществляет мониторинг состояния узлов и подов. Компонент передает информацию мастер-ноде с помощью запросов к API серверу;
  • Kube Proxy: прокси-сервис на воркере. Компонент осуществляет наблюдение за конфигурацией Kubernetes-сервисов и применяет горизонтальную (east-west) балансировку нагрузки на узлах через NAT в IPtables;
  • Container Runtime (Docker, containerd, cri-o и другие): среда исполнения контейнеров, тот самый «легковесный гипервизор» для их запуска
Более подробное описание архитектуры кластера и взаимодействия компонент можно найти в статьях на Хабре и Devopscube
Здесь же мы оставим иллюстрацию взаимодействия мастер- и воркер-нод (картинка взята из второй ссылки выше)
Объекты Kubernetes
K8s состоит из различных объектов, которые взаимодействуют между собой.

Кратко опишем основные из них:
  • Pod: минимальная структурная единица Kubernetes. Поды можно рассматривать как обертку над контейнерами, или как маленькую виртуальную машину, на которой они запускаются. Все запущенные контейнеры на одном поде делят один «внешний» IP адрес и могут общаться через localhost. Оркестратор работает именно с подами;
  • Service: срок жизни пода мал, а при каждом его перезапуске он будет получать новый IP адрес — это тяжело отслеживать и с этим тяжело работать. Поэтому есть сервисы. Они представляют собой сущности, которые «резервируют» IP адрес для пода и/или группы подов. Цикл жизни сервиса не зависит от подов, к которым он привязан. Поды рождаются и умирают, а сервис остается.

Есть несколько видов сервисов:
  • CluserIP: базовая сущность сервисной иерархии. Используется для коммуникации подов внутри кластера;
  • NodePort: позволяет опубликовать приложение «наружу», присвоив ему определенный внешний порт (из диапазона 30 000−32 767). Внутри кластера привязывается к ClusterIP и передает запросы на него;
  • LoadBalancer: балансировщик. Предыдущие два вида тоже выполняют балансировку нагрузки на приложение, однако LB позволяет управлять внешним балансировщиком нагрузки
  • Ingress: применяет правила «роутинга» к запросам на основе FQDN, используется как единая точка доступа к приложению;
  • NetworkPolicy: ACL Kubernetes. По умолчанию весь трафик между приложениями внутри кластера разрешен. NetworkPolicies позволяют точечно выдать доступы одним сервисам для других;
  • PersistentVolume (PV) и PersistentVolumeClaim (PVC): работа с хранилищем данных. PV — логический том данных, позволяет пользователям кластера игнорировать физическое устройство хранилища, а работать с ними через единый интерфейс. PVC — это запрос на ресурсы: пользователь создает PVC, а кластер выдает ему PV, который удовлетворяет запросу;

Объекты для запуска подов:
  • ReplicaSet позволяет запустить определенное количество подов и применять правила для масштабирования приложения;
  • DaemonSet запускает экземпляр пода на каждом узле кластера. По умолчанию на мастер-нодах поды не запускаются благодаря служебным меткам (Taints). Однако это ограничение можно обойти с помощью поля Tolerations в конфигурации пода;
  • StatefulSet: объект, позволяющий разворачивать приложения, которым важно сохранять свое состояние (например, СУБД)
  • Метки (теги, лейблы): с их помощью компоненты кластера понимают, с какими конкретно объектами они должны работать. По сути — это основной механизм, который связывает разрозненные объекты K8s в рабочее приложение
Советуем посмотреть видео из курса «Основы виртуализации», которое сформирует представление об этапах развития Kubernetes, архитектуре и принципах работы платформы
CI/CD pipeline
Одна из практик в идеологии DevOps — непрерывная интеграция и доставка (Continuous Integration/Continuous Delivery — CI/CD).

Она включает в себя следующие этапы:
  • Код: для каждого модуля пишется код от разных разработчиков. Далее все ветви разработки через систему контроля версий интегрируются в единую ветку репозитория;
  • Сборка: на следующем этапе происходит автоматизированная сборка продукта с помощью таких систем как Jenkins, GitLab CI/CD и т. д;
  • Тест: выполняется ручное и интеграционное тестирование;
  • Релиз;
  • Развертывание: осуществляется доставка кода для клиентов. Полученная версия кода развертывается на тестовых или продуктивных серверах;
  • Поддержка;
  • Мониторинг;
  • Планирование: создается план разработки дополнительного функционала, который подразумевает под собой написание нового кода. Таким образом, цикл замыкается

Благодаря CI/CD ускоряется разработка и последующее обновление продукта, что позволяет гибко подстроиться под запросы рынка. Автоматизация процессов значительно снижает нагрузку на команду разработчиков, кроме того, в среде CI/CD значительно легче выявлять и устранять ошибки в коде.
А что насчет безопасности?
Изначально в идеологии DevOps не было отведено должного внимания вопросам безопасности, а был упор только на автоматизацию рутинных процессов и оптимизацию параметра time-to-market.

Подход DevSecOps — это про добавление к непрерывной доставке и развертке так же и процессов безопасности. На каждом этапе жизненного цикла внедряются инструменты, которые проверяют артефакты разработки на соответствие заданным политикам безопасности: линтеры, сканеры образов и конфигурационных файлов, агенты для сканирования запущенных контейнеров.

Используется принцип «shift-left», при котором безопасность внедряется с самых первых этапов разработки (исходный код и написание конфигурационных файлов). При этом мы не забываем про безопасность на остальных этапах.
Когда мы разворачиваем свое приложение на микросервисах, которые работают в кластере (или даже в нескольких кластерах) Kubernetes, площадь атаки огромна.

Основной целью для атакующих становится Kubernetes API — так как он является ядром кластера, поэтому его защите следует уделять особое внимание. Также в базе etcd по умолчанию данные хранятся в незашифрованном виде и получение доступа к ней будет фактически означать компрометацию кластера.

Вот еще небольшой список возможных угроз:
  • Уязвимости ОС узлов кластера или ОС базового слоя контейнеров;
  • Разработчики оставили чувствительные данные (логины, пароли, токены) в исходниках;
  • Уязвимости в образах контейнеров, которые позволяют монтировать лишние тома и повышать права;
  • Человеческий фактор: оставили открытый порт после дебага пода; при написании YAML-файла NetworkPolicy указали селектор меток с неправильным отступом — и дали подам больше возможностей для коммуникации; для тестов скачали образ из недоверенного репозитория, потому что в приватном Nexus их не оказалось, а результат нужен был «вотпрямсейчас» и т. д.;
  • Политики RBAC не соответствуют принципу наименьших привилегий;
  • И так далее…
Специализированные средства защиты и почему с ними спокойнее
Может возникнуть вопрос: «У нас уже есть антивирус на хостах, WAF для анализа API- и веб-трафика, на периметре — NGFW, песочница и SIEM-система. Зачем тратить дополнительные деньги и встраивать в инфраструктуру еще одно решение?».

Существует две причины:
  • короткий срок жизни контейнеров;
  • их архитектурные особенности
Проясним первую причину. Представим, что к нам в кластер попал хакер, вышел за пределы контейнера и поломал что-то на хостовой ОС. Соответственно, EDR отработал и отправил лог на свой сервер и в SIEM-систему. При расследовании инцидента цепочка событий раскручивается в обратном порядке. Выясняется, что точкой входа было «что-то» с IP-адресом из Kubernetes кластера.
А вот дальше мы вряд ли сможем идентифицировать это «что-то», ибо пока мы проводили расследование, контейнеры уже несколько раз перезапустились и, следовательно, найденный нами IP-адрес либо свободен, либо принадлежит другому контейнеру с другими функциями (заметка: при перезапуске контейнеров у них меняется IP-адрес).

Одной из архитектурных особенностей контейнеров является изолированность. Изоляция контейнера означает, что он самодостаточен и ему не нужно читать и/или записывать какие-либо файлы на рабочей станции, чтобы работать — все необходимое уже «упаковано».

Вторая особенность: контейнер получает доступ к kernel space в обход user space хостовой ОС. User space — виртуальное адресное пространство, в котором работают пользовательские программы (от bash, ls и cat до nginx, браузера и музыкального плеера). При запуске контейнера внутри него создается собственное user space. Уже из этого внутреннего user space контейнер отправляет системные вызовы — через «прослойку» в виде container runtime (containerd, cri-o, docker) — к kernel space хостовой ОС.
Теперь самое важное: антивирус работает на уровне user space хостовой ОС — он просто не видит, что делает контейнер. А так как контейнер изолирован, то антивирус еще и не может проверить, что у него происходит внутри.

Кратко про решения вида WAF и NGFW: трафик может вообще не выйти за пределы кластера. Это мощные инструменты, которые помогут предотвратить доступ к кластеру извне, но если уж злоумышленник (злоукошленник, ха-ха) прорвался через защиту, то они уже не помогут.
Здесь же и приходят на помощь специализированные средства защиты контейнерных сред, и сканирование вот этой маленькой дверки — одна из их задач. Дополнительно реализуется сканирование образов контейнеров и CI/CD (не тащим «грязь» в production), конфигурационных манифестов IaC (секреты, misconfigurations), а также визуализация ресурсов кластера — загрузка CPU/RAM, Deployment-ы, Pod-ы, ReplicaSet-ы.

Защищая свою контейнерную инфраструктуру, мы добавляем себе баллов к общей защищенности бизнес-процессов и уменьшаем вероятность того, что наши пользователи зайдут на наш сайт и увидят ошибку 404.
Картинка взята с ресурса: https://www.true.nl/blog/het-kubernetes-platform-bestaat-6-jaar/
Резюме
Не хотим называть эту часть «итоги», ведь это только начало!

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

Все описанные технологии — мощные инструменты, которые позволяют создавать производительные и масштабируемые приложения и быстро доставлять их до пользователя. Но за все нужно платить — здесь мы платим сложностью и комплексностью настройки всей системы и, как следствие, большой площадью атаки. И нам будет спокойнее, если мы будем знать, что происходит внутри нашего «цеха приложений».

В России использование контейнерной инфраструктуры продолжает развиваться, на рынок выходят решения по ее защите. В следующих материалах мы будем непосредственно разбирать эти решения.

Оставайтесь с нами и следите за новостями!

Авторы: Нестеренко Влада и Разумов Кирилл, Инженеры TS Solution