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

Во второй части я продолжу рассказ про проект, с которым мы уже познакомились ранее

Напомню про требования:

  • Замкнутый контур;

  • Отсутствие CVE во всех используемых продуктах;

  • Контроль безопасности уже имеющейся инфраструктуры;

  • Контроль доступа до среды;

  • Автоматизация процессов.

Но как быть, если ваша инфраструктура располагается в рамках kubernetes оркестратора? Как быть, если вы используете managed решение? Какие подходы для организации безопасности будут применимы? Под катом — про это, а еще про Managed Service for Kubernetes и Yandex Cloud, Kyverno, Tetragon, Falco и многое другое. 

Давайте посмотрим, что было дальше? 

Приглашаю вас подписаться на наш блог Хабр, TG-канал DevOps FM и познакомиться с YouTube — мы всегда рады новым друзьям :)

Глава 1: Что было дальше?

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

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

Что мы сейчас имеем.
Что мы сейчас имеем.

В этот момент каждый DevOps задаётся сразу вопросом:

А что же это означает для меня?

Если ранее весь процесс заключался в деплое landing, frontend и backend, то в потенциальном будущем количество контейнеров в инфраструктуре может превысить 20 штук и это мы говорим только о prod среде, но нам ведь будет необходимы как минимум dev и test среды приближенные к проду, а возможно и динамические окружения — как быть?

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

Глава 2: Готовое решение или тернистый путь?

Когда появляется необходимость в использовании kubernetes, нужно сделать нелёгкий выбор: использовать готовые managed решения, предоставляемые облаками, или самостоятельно развернуть кластер через kubespray на ранее созданных нодах. У каждого из таких решений есть свои плюсы и свои минусы. 

Явными плюсами любого managed решения являются сниженные трудозатраты на поддержку и продукта, но не надо забывать, что это также свяжет ваши руки в дальнейшем администрировании. В этом случае теряется возможность гибкого контроля исходных версий пакетов и ОС ваших master и worker нод, ведь, как правило, все облачные решения не всегда успевают соответствовать тенденциям обновлений. Отмечу, что на текущем высококонкурентном рынке каждый пытается максимально закрыть все основные потребности пользователей и именно поэтому нужно лишь внимательно выбирать облако и изучать его roadmap по развитию.

К плюсами self-hosted разворачивания K8S можно отнести полный контроль всего, что имеется, со стороны администратора, однако это приводит к увеличению трудозатрат. В каждом случае правильное решение выбирать вам и универсального ответа на вопрос “Что будет правильно?” здесь нет. Главное помнить — выбор зависит не только от требований со стороны Кибербезопасности, но и от потребностей тонкого тюнинга по оптимизации использования ресурсов, например таких как GPU.

Зная все плюсы и минусы, для нас вопрос выбора в условиях работы проекта не стоял. Напомню исходные требования описанные ранее:

  • Замкнутый контур;

  • Отсутствие CVE во всех используемых продуктах;

  • Контроль безопасности уже имеющейся инфраструктуры;

  • Контроль доступа до среды;

  • Автоматизация процессов.

Так как раннее наша инфраструктура уже была в Yandex Cloud, то было решено использовать Managed Service for Kubernetes. Развернуть его весьма просто, если не забывать про главный подход, которого мы начали придерживаться ранее. IaC или по другому говоря Infrastructure-as-Code. В ходе дальнейшего нашего разворачивания необходимо использовать Terraform и Yandex предлагает замечательный провайдер для этих средств, как и весьма хорошую документацию к нему. Итоговый результат можно описать визуально следующим образом.

Для обеспечения отказоустойчивости нам по-прежнему необходимы 3 подсети, в 3-х различных зонах доступности. Тут же появляется возможность создать node-pools, в которых можно заложить возможности увеличения инфраструктуры, например, на время роста входящего трафика. В случае, если будет необходимость увеличить ресурсы,то можно воспользоваться как горизонтальным масштабированием (увеличение количества серверов в каждом пуле), так и вертикальным масштабированием (изменением конфигурации сервера в большую сторону). Вслед за этим и первые изменения со стороны безопасности, которые требуют перекрытия порта API K8S открытого на локальном ip адресе, доступ к которому будет идти через наш ранее развернутый OpenVPN.

Глава 3: Организация разделения прав и привилегий доступа микросервисов

Сам кластер развернут и находится в замкнутом контуре и здесь возникает вариативность хода выполнения работ —  если бы мы работали с self-hosted инфраструктурой, то нас бы ожидали дополнительные объемные работы по сканированию безопасности ОС серверов и конфигураций настроек кластера. Для данного процесса подходит ПО, о котором мы уже упоминали ранее в другой статье kube-hunter и kube-bench (ссылка на статью), но в managed решении мы лишены подобных возможностей. 

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

Итак нам нужно настроить деплой нашего микросервиса. Как правило любой pipeline содержит 3 стадии:

  • Build Dockerfile нашего образа

  • Тестирование пакетов и кода

  • Деплой микросервиса в инфраструктуру.

В текущем вопросе нас интересует последний пункт и если мы говорим про деплой в kubernetes, то мы приходим к Helm Chart-у. Для автоматизации и ускорения процесса деплоя можно либо написать свой кастомизированный вариант, либо воспользоваться готовым чартом с целью экономии трудозатрат на подготовительные работы. На наших проектах мы используем nxs-universal-chart

Пройдёмся по основным пунктам безопасности манифестов инфраструктуры.

1. Использование RBAC (Role-based access control) и ServiceAccount

Каждый pod в рамках нашей инфраструктуры должен быть запущен не из-под default сервисного аккаунта. Если вы располагаете в рамках одного namespace несколько приложений, то в случае проникновения в оболочку одного контейнера, в потенциальной перспективе злоумышленник сможет получить доступ до рядом развернутых контейнеров.

apiVersion: v1
kind: ServiceAccount
metadata:
 name: front-prod-sa
 namespace: prod
automountServiceAccountToken: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: front-prod
 namespace: prod
spec:
 serviceAccountName: build-robot-sa
 automountServiceAccountToken: false
 ...

Также важно помнить, что если сервисный аккаунт обладает чрезмерными привилегиями RBAC, то появляется возможность получения доступа до любых сущностей и компонентов кластера, таких как deployment, ingress, namespace и т.д.

2. Context Security.

Если мы говорим про безопасную инфраструктуру, то и речи быть не может о запуске init процесса контейнера из под root пользователя. Отсюда следует вывод, что нам необходимо будет предоставить приложению права для работы с файлами внутри контейнера и для этого решения лучше всего подойдёт Context Security, который выставит как нужные права на группу файлов, так и нужного владельца.

apiVersion: apps/v1
kind: Deployment
metadata:
 name: front-prod
 namespace: prod
spec:
...
securityContext:
 runAsUser: 1000
 runAsGroup: 1000
 fsGroup: 2000
       fsGroupChangePolicy: "OnRootMismatch"

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

apiVersion: apps/v1
kind: Deployment
metadata:
 name: front-prod
 namespace: prod
spec:
...
securityContext:
  capabilities:

          add: ["NET_ADMIN", "SYS_TIME"]

3. Контроль на уровне сети.

Чтобы ограничить предоставление сетевого доступа вашего запущенного пода к другому необходимо воспользоваться сетевыми политиками (Network Policy). Выдача подобных привилегий может происходить по ряду параметров:

  • Разрешение доступа по namespace.

  • Разрешение доступа до конкретных pods.

  • Ограничение по подсети ip адресов.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
 name: front-prod-network-policy
 namespace: prod
spec:
 podSelector:
   matchLabels:
     role: front-prod
 policyTypes:
   - Ingress
   - Egress
 ingress:
   - from:
       - ipBlock:
           cidr: 172.17.0.0/16
           except:
             - 172.17.1.0/24
       - namespaceSelector:
           matchLabels:
             project: prod
     ports:
       - protocol: TCP
         port: 80
 egress:
   - to:
       - ipBlock:
           cidr: 10.0.0.0/24
     ports:
       - protocol: TCP
         port: 80

4. mTLS-шифрование входящего трафика.

Использование mTLS обеспечивает аутентификацию как клиента, так и сервера, устанавливая двустороннее TLS‑шифрование между ними. Это гарантирует, что и клиент, и сервер являются теми, за кого себя выдают, обеспечивая дополнительный уровень безопасности соединения. Реализация подобного механизма с лёгкостью описывается через ingress annotations и заранее заготовленный secret в рамках нужного namespace. После чего все запросы с клиента к серверу должны содержать сертификаты для корректной аутентификации в конечной точке.

metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
    nginx.ingress.kubernetes.io/auth-tls-secret: default/ca-secret
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"

5. Блокирование запуска неподписанных контейнеров или по другому говоря:

Kyverno — автоматизированное решение для проверки образов контейнера и повышения безопасности платформ kubernetes. Основные плюсы в следующем:

  • Простая установка через Helm. (Также имеется нативная установка чарта в рамках Yandex Cloud)

  • Создание политики, как CRD (CustomResourceDefinition) и описание её через YAML.

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

  • Наличие GUI

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

Пройдёмся кратко по примеру работы с Kyverno:

5.1. Добавляем репозиторий Kyverno Helm и обновляем список репозиториев, после чего устанавливаем чарт в нужны namespace:

helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace

5.2. Устанавливаем политики Kyverno

Теперь, когда Kyverno установлен, вы можете заняться установкой политик. При первой установке рекомендуется настроить их на работу в режиме «аудита», чтобы ни один из запросов на подключение к кластеру не был заблокирован. Вы можете узнать, настроена ли политика на “audit”, просто проверив свойство validationFailureAction в манифесте политики. К примеру данная политика проверяет, что все запускаемые образы в подах в рамках namespace prod при сборке были подписаны нашим приватным ключом.

apiVersion: kyverno.io/v1
kind: Policy
metadata:
    name: check-image
    namespace: prod
spec:
    validationFailureAction: audit
    background: true
    webhookTimeoutSeconds: 30
    failurePolicy: Fail
    rules:
    - name: check-image
      match:
        any:
        - resources:
            kinds:
            - Pod
      verifyImages:
      - image: "nexus-dashboard.test.ru/repository/docker-master/*"
        key: |-
          -----BEGIN PUBLIC KEY-----
          Your advertisement could be here
          -----END PUBLIC KEY-----
P.S. Установка Kyverno после релиза.

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

Для установки типовых политик используйте команду:

helm install kyverno-policies kyverno/kyverno-policies --namespace kyverno

Далее проверяем корректность применяемых манифестов установленных глобально на весь кластер:

kubectl get clusterpolicies

Ожидаемый результат вывода:

NAME                             BACKGROUND   ACTION   READY
deny-privilege-escalation        true         audit    true
disallow-add-capabilities        true         audit    true
disallow-host-namespaces         true         audit    true
disallow-host-path               true         audit    true
disallow-host-ports              true         audit    true
disallow-privileged-containers   true         audit    true
disallow-selinux                 true         audit    true
require-default-proc-mount       true         audit    true
require-non-root-groups          true         audit    true
require-run-as-non-root          true         audit    true
restrict-apparmor-profiles       true         audit    true
restrict-seccomp                 true         audit    true
restrict-sysctls                 true         audit    true
restrict-volume-types            true         audit    true

Так и в рамках нашего репозитория.

kubectl -n prod get policies

Ожидаемый результат вывода:

NAME          BACKGROUND   ACTION   READY
check-image   true         audit    true

5.3. Просматриваем результаты отчётов о нарушениях политик.

Просмотреть результаты отчётов можно либо в рамках конкретного namespace:

kubectl -n prod get policyreports

Либо в глобальных отчётах кластера:

kubectl get clusterpolicyreports

Либо можно настроить весьма простой dashboard в Grafana на основе имеющихся весьма полных metric kyverno с последующем пушем алертов в случае возникновения статуса Fail в итоговом отчёте. Отлично, если ознакомление с базовыми аспектами безопасности на уровне манифестов закончено, то можно идти дальше.

Глава 4: Защита от угроз в реальном времени

Описание безопасной инфраструктуры не говорит о безопасности системы. Всегда важно не забывать как про человеческий фактор, так и про нелегитимные действия. Именно поэтому контроль инфраструктуры во время выполнения процессов в реальном времени является важным аспектом общей стратегии кибербезопасности проекта, но не стоит забывать и про стандартный мониторинг состояния системы, который также реализуется через Zabbix или Prometheus, Alertmanager, Grafana. В облачных средах безопасность выполнения становится еще более важной, поскольку приложения и рабочие нагрузки часто распределяются по нескольким сервисам и платформам. Для этого может подойти ряд технологий, о которых поговорим дальше.

Tetragon и Falco —  обе технологии у многих на слуху, когда речь идёт про Run Time Security, но мы нуждались в другом важном аспекте  — блокировке нелегитимных действий и если Falco в двух словах можно описать, как механизм для аудита инфраструктуры, то про Tetragon так сказать нельзя. Так как помимо обнаружения потенциальных «дыр в безопасности» он способен  произвести завершение процесса, зафиксировав за ним нелегитимную активность, которая попадает под описанные правила. Здесь остановлюсь чуть подробнее и пройдусь по ряду функций и параметров.

Что нужно сделать в первую очередь?

  • Оценка среды запуска микросервисов - это включает в себя рассмотрение версии ядра и архитектуры.

  • Определите цели обнаружения угроз.

Что нужно сделать во вторую очередь?

Необходимо произвести инсталляцию самого Tetragon. Сделать это можно простым способом используя заранее готовый Helm Chart из общедоступного registry.

helm repo add cilium https://helm.cilium.io/
helm install tetragon cilium/tetragon --version 1.0.0 --namespace tetragon

Что нужно сделать в третью очередь?

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

Звучит неплохо, но что может мониторить Tetragon?

  • Мониторинг жизненного цикла процесса

  • Мониторинг произведения действий чтения/изменения с файлами наблюдения.

  • Мониторинг tcp соединений на уровне сети в кластере.

  • Мониторинг произведения изменений (монтирование, создание файловой системы) в хостовой части системы.

  • Мониторинг учётных данных процесса (UID/GID)

  • Мониторинг capabilities предоставляемых процессу контейнера.

В целом подобный скоуп тестов и возможных вариаций уверенно покрывает большую часть потребностей со стороны КБ, но самое главное позволяет реализовывать функционал реакции на возникновение события мониторинга. К примеру политика запрещает какому-либо файлу производить операцию чтения с файлами /etc/shadow, /etc/passwd:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: ""
spec:
  kprobes:
  - call: "fd_install"
    syscall: false
    args:
    - index: 0
      type: "int"
    - index: 1
      type: "file"
    selectors:
    - matchBinaries:
      - operator: "In"
        values:
        - "/bin/cat"
      matchArgs:
      - index: 1
        operator: "Equal"
        values:
        - "/etc/shadow"
        - "/etc/passwd"
      matchActions:
      - action: Sigkill

Не смотря на элегантность этого решения в концепции контроля запущенных процессов у технологии имеется как ряд плюсов, так и ряд минусов:

Плюсы:

  • Манифесты политик описанные в yaml имеют гибкую и понятную схему описания условий.

  • Инструмент постоянно развивается и выходят обновления, как основного продукта, так и Helm Chart.

  • Поды Tetragon имеют возможность отдавать встроенные метрики на внешний порт, что позволяет интегрировать весь этот процесс с Grafana и Prometheus, описав push уведомления на критические события.

  • Поды Tetragon имеют цельные логи экспортируемые в /dev/stdout контейнера, которые описывают поведение всех процессов попадающих под политики. Всё это позволяет в дальнейшем экспортировать эту информацию в любую систему анализа и хранения данных.

Минусы:

  • Так как инструмент ещё развивается, то у него имеются проблема в изучении из-за нехватки целостной информации в документации.

  • Также, что явно бросается в глаза - это не полнота текущих возможностей политик, для покрытия всех возможных элементов инфраструктуры. Явным примером является работа только с TCP соединениями в Network Policy на уровне  L4.

  • Существует проблема регулирования политик. На данный момент исключение namespace, которые не будут подпадать под условия созданных правил описывается глобально на уровне Tetragon агентов, что ограничивает в гибкости выстраивания процессов.

И так, теперь когда мы интегрировали несколько инструментов в нашу инфраструктуру, посмотрим, что из этого получается?

Глава 5: Легче всего идти по пути, когда видишь его конец

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

Пройдёмся по требованиям?

  • Замкнутый контур;
    Требование выполнено и выдержано согласно описанному ранее регламенту.

  • Отсутствие CVE во всех используемых продуктах;

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

  • Контроль безопасности уже имеющейся инфраструктуры;

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

  • Контроль доступа до среды;

    Задача была также успешно реализована через Freeipa и OpenVPN как и ранее, а часть функционала по распределению доступов относительно сущностей k8s прекрасно решается на уровне распределения ролей IAM в Yandex Cloud.

  • Автоматизация процессов.

    Была реализована посредством изменения текущих pipeline-ов и применением IaC.

Но получается это конец и продолжать нет смысла?

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

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

А что будет дальше вы узнаете в следующей части.

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