• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Trip.com: первый шаг к облачной безопасности

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
В этом посте рассказывается о наших исследованиях облачной нативной безопасности для Kubernetes. а также с старыми рабочими нагрузками, с CiliumNetworkPolicy для доступа L3/L4 контроль в качестве первого шага. Необходимы некоторые базовые знания об управлении доступом в Kubernetes. прежде чем углубляться в детали. Если вы уже знакомы с этими вещами, вы можно просто перемотать вперед.

1.1 Контроль доступа в Kubernetes


В Kubernetes пользователи могут контролировать L3/L4(уровень IP/порта) потоки трафика приложений с NetworkPolicies.
А NetworkPolicy описывает как общаться различным сущностям на уровне OSI L3/L4, где «сущности» здесь могут быть идентифицированы комбинацией следующих 3 идентификаторов:
  • Пространства имен , например модули из/в пространство имен default
  • IP-блоки (CIDR) , например трафик из/в 192.168.1.0/24
  • другие сущности, на пример app=client

1653290780059.png


Мы хотели бы, чтобы все сущности с тегом role=backend (сторона клиента) для доступа к сервису TCP/6379 всех сущностей с тэгом role=db(серверная сторона), а также другие клиенты, не входящие в эту спецификацию, должны быть отклонены. Ниже приведен минимальный NetworkPolicy для достижения цели (при условии, что клиентские и серверные модули в default пространстве имен):
Код с оформлением (BB-коды):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: network-policy-allow-backend
spec:
  podSelector:             # Targets that this NetworkPolicy will be applied on
    matchLabels:
      role: db
  ingress:                 # Apply on targets's ingress traffic
  - from:
    - podSelector:         # Entities that are allowed to access the targets
        matchLabels:
          role: backend
    ports:                 # Allowed proto+port
    - protocol: TCP
      port: 6379

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

Дополнительные сведения о Kubernetes NetworkPolicy см. Сетевые политики

1.2 Реализация и расширение в Cilium


Cilium как сетевое решение Kubernetes реализует, а также расширяет стандартный Кубернет NetworkPolicy. Чтобы быть конкретным, он поддерживает три вида политик:


  1. NetworkPolicy: стандартная сеть Kubernetes, контроль L3/L4 трафика;
  2. CiliumNetworkPolicy(CNP): расширение стандартной Kubernetes NetworkPolicy, L3-L7 трафик;
  3. ClusterwideCiliumNetworkPolicy(CCNP): без пространства имен CCNP
Код:
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "Allow HTTP GET /public from env=prod to app=service"
  endpointSelector:
    matchLabels:
      app: service
  ingress:
  - fromEndpoints:
    - matchLabels:
        env: prod
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/public"

1.3 Проблемы крупных развертываний​

Как показано выше, с помощью NetworkPolicy/CNP/CCNP можно обеспечить доступ L3-L7 элементам управления внутри кластера Kubernetes. Однако для крупных развертываний в реальные кластеры с работающими критически важными предприятиями, нужно гораздо больше информации, некоторую я представлю вам сейчас.


  1. Как управлять политиками , и что такое интерфейс для конечных пользователей (разработчики, службы безопасности и т. д.)?
    • Самым «ленивым» способом управления политиками может быть создание git-репозиторий и настроить политики через raw policy yam, но -
    • Разработчики приложений в большинстве случаев не имеют доступа к Kubernetes инфраструктуры, как мы (команда инфраструктуры), поэтому мы не можем полагаться на них в манипуляциях необработанными файлами yaml, как мы.
  2. Как выполнять аутентификация и авторизация при манипулировании политикой?

    Изучите 4A model (Учет, Аутентификация, Авторизация, Аудит).
    • Как проверить (человека) пользователя (разработчика, администратора и т. д.)?
    • Кто может запросить политику для какого ресурса (приложения, базы данных и т. д.)?
    • Кто отвечает за утверждение/отклонение конкретного запроса политики?
    • Аудит инфраструктуры, жизненно важный для решения безопасности.
  3. Как обрабатывать трансграничный доступ ? Например, прямое соединение между модулями, трафик между кластерами Kubernetes.

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

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

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

4. Как управлять устаревшими рабочими нагрузками (например, VM/BM/non-cilium-pods)?

Для компаний, которые развивались более десяти лет, весьма вероятно, что существует не только прямой трансграничный трафик, но и устаревшие рабочие нагрузки, таких как виртуальные машины в OpenStack, системе BM или модулях Kubernetes на базе сетевые решения, отличных от Cilium.
Некоторые из них могут быть временными, например, миграция машин, не оснащенных non-cilium-pods в кластер на базе Cilium, но некоторые могут и не быть, типа ВМ все равно нельзя, заменены контейнерами в определенных сценариях.
Поэтому возникает естественный вопрос: как скрыть эти объекты в вашем решении для обеспечения безопасности?

5. Соображения производительности

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

  • Производительность пересылки: приведет ли решение к серьезному снижению производительности?
  • Время вступления политики в силу (задержка)
6. Регистрация, мониторинг, оповещение , наблюдаемость и т.д.

Будьте лучше знакомы со своей системой, чем ваши пользователи, вместо того, чтобы вас называли до последнего в полночь.


2. Контроль доступа: от требований к решению​


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

  1. Контроль доступа к гибридным инфраструктурам
    • Поддержка Kubernetes (main case) OpenStack, Baremetal и т. д.
    • Поддержка локальных инфраструктур, а также инфраструктур в общедоступном облаке.
    • Поддержка прямого межкластерного трафика
  2. Эволюционная архитектура
    • Поддержка нескольких политик
  3. Поддержка контроля доступа L3-L7
  4. Высокая производительность

2.1 Применение политик в одном кластере​


Начиная с самого простого случая, рассмотрим контроль доступа в автономный кластер Kubernetes на базе Cilium.


Как показано в логической архитектуре ниже, агент Cilium на каждом сервере Kubernetes рабочий узел слушает два хранилища ресурсов :

Кластер Kubernetes на базе Cilium

1653293415180.png



  1. API-сервер Kubernetes (до k8s-etcd): для просмотра CNP/CCNP
  2. KVStore (cilium-etcd): для просмотр идентификаторов струкур (и другие метаданные) всего кластера

В этой стандартной конфигурации с одним кластером собственного CNP/CCNP будет достаточно для применение политики, в том, что cilium-agent на каждом узле кэширует все активное идентификационное пространство кластера . Пока клиенты приходят из одного и того же кластера, каждый агент будет знать свой идентификатор безопасности, просматривая его локальный кеш, затем решите, куда направить трафик:

Применение политики Ingress внутри узла Cilium


1653293876729.png

Этапы обработки (включая применение политик) трафика в кластере Kubernetes на базе Cilium


1653294095026.png



2.2 Применение политики в нескольких кластерах​


Теперь рассмотрим случай с несколькими кластерами.


Представьте, что модули серверов находятся в одном кластере, но клиентские модули разбросаны по нескольким кластерам , а клиенты обращаются к серверам напрямую (без шлюзов).


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

2.2.1 Кластерная сетка​


Cilium поставляется со встроенным многокластерным решением под названием ClusterMesh. По сути, он настраивает каждый cilium-агент на прослушку KVStore других кластеров . Таким образом, каждый агент получает информацию об идентификаторе безопасности модулей в удаленных кластерах . Ниже представлен случай с двумя кластерами как с сеткой:

1653294436196.png


ClusterMesh: каждый cilium-агент также слушает KVStore других кластеров

Таким образом, при поступлении трафика от удаленных кластеров локальный агент может определить его контекст безопасности с локальной базой знаний:

1653294612235.png

Межкластерный контроль доступа с помощью Cilium ClusterMesh

2.2.2 KVStoreMesh​


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


Короче говоря, вместо того, чтобы позволять каждому агенту извлекать удаленные удостоверения для всех удаленных KVStore мы разработали для этого оператор области кластера, который синхронизирует удаленные удостоверения KVStore локального кластера . Проще говоря, в каждом кластере Kubernetes мы запускаем kvstoremesh-operator, который
  1. Слушает изменения метаданных Cilium (например, идентификаторы безопасности) из KVStore Cilium всех других кластеров
  2. Запишите изменения в локальный Cilium KVStore

Двухкластерный: Настройка мультикластера с помощью KVStoreMesh


1653295260299.png

Настройка мультикластера с помощью KVStoreMesh (оператор kvstoremesh для краткости опущен)


1653295333558.png



Технически, с KVStoreMesh агенты получают удаленные удостоверения от своих локальный kvstore напрямую. Это гарантирует - только что как ClusterMesh, но без проблем со стабильностью и гибкостью. Сравнение ClusterMesh и KVStoreMesh будет подробно описано позже.
Превосходный дизайн Cilium заставляет вышеописанную идею работать большую часть времени, и мы исправили некоторые ошибки (большинство из которых уже находится на рассмотрении), чтобы остальные случаи также работали.

2.3 Применение политики к устаревшим клиентам

С помощью CNP/CCNP и KVStoreMesh мы решили задачи с одним и несколькими кластерами. Теперь давайте сделаем один шаг Кроме того, рассмотрим как поддерживать устаревшие рабочие нагрузки, например, виртуальные машины из OpenStack.


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

2.3.1 CiliumExternalResource​


Основываясь на нашем понимании дизайна и реализации Cilium, мы ввели пользовательское расширение по сравнению с Cilium Endpoint модель:

Код:
// pkg/endpoint/endpoint.go

// Endpoint represents a container or similar which can be individually
// addresses on L3 with its own IP addresses.
//
// The representation of the Endpoint which is serialized to disk for restore
// purposes is the serializableEndpoint type in this package.
type Endpoint struct {
    ...
    IPv4 addressing.CiliumIPv4
    SecurityIdentity *identity.Identity `json:"SecLabel"`
    K8sPodName string
    K8sNamespace string
    ...
}


Мы назвали его CiliumExternalResource (CER), чтобы отличить его от более позднего CiliumExternalWorkload (CEW).

CEW поставлялся с Cilium 1.9.x, а CER был развернут внутри компании с версии 1.8.x. Их сравнение будет подробно описано в следующем разделе.
И причина, по которой мы не назвали это ExternalEndpoint это уже есть externalEndpointпонятие в Cilium, которое используется для совершенно других целей. Мы подробнее остановимся на этом позже.

Запись CER — это часть Метаданные с поддержкой ресничек, хранящиеся в KVStore (cilium-etcd), что соответствует одной устаревшей рабочей нагрузке , например, экземпляр виртуальной машины. Благодаря этому взлому каждый cilium-агент распознает эти устаревшие рабочие нагрузки. при выполнении входного контроля доступа для модулей Non-cilium-CN


2.3.2 cer-apiserver​


Мы также предоставили API ( cer-apiserver), чтобы позволить устаревшим платформам или инструментам (например, OpenStack, система BM, Non-cilium-CNI) для загрузки своей рабочей нагрузки в Хранилище метаданных Cilium.


Обеспечивая синхронный вызов cer-apiserverкогда есть последующие операции с рабочей нагрузкой (такие как создание или удаление экземпляра ВМ), кластер Cilium сохраняет последние состояния устаревших рабочих нагрузок.


2.3.3 Подведение итогов: гибридная плоскость данных​

  • CER (подача метаданных внешних ресурсов в один кластер)
  • KVStoreMesh (вытягивать метаданные ресурсов из всех остальных кластеров, в итоге метаданные во всех кластерах сходятся к одному и тому же),

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


1653296400858.png


Гибридная плоскость данных за счет объединения CER и KVStoreMesh.

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


2.4 Панель управления​


2.4.1 Моделирование политики управления доступом (ACP)​


Одна из наших целей — сделать плоскость управления достаточно общей, даже если базовая мода на применение политики однажды изменилась (например, CCNP прекратила свое существование), плоскость управления не претерпела бы никаких изменений (или как можно меньше). Таким образом, мы в конечном итоге абстрагировались от AccessControlPolicy модель, это дает много преимуществ:
  1. Обеспечьте независимое развитие плоскостей управления и данных
  2. Возможность интеграции различных типов плоскостей данных в единый элемент управления плоскости, такие как CCNP на основе eBPF, Istio на основе mTLS AuthorizationPolicy, или даже некоторые методы на основе WireGuard в будущем.

AccessControlPolicy похож на АВС AccessPolicyи многие другие модели управления доступом на основе RBAC, все это концептуально управление доступом на основе ролей :

1653296796500.png
Модель AccessControlPolicy
Некоторые удобные сопоставления, если вы не знакомы с терминами RBAC:

  • Субъекты/Директора -> клиенты
  • Ресурсы -> серверы

Ниже показан пример, который позволяет приложению 888а и 999 получить доступ к кластеру Redis bobs-cluster:

Код:
kind: AccessControlPolicy
spec:
  statements:
  - actions:
    - redis:connect
    effect: allow
    resources:
    - trnv1:rsc:trip-com:redis:clusters:bobs-cluster
    subjects:
    - trnv1:rsc:trip-com:iam:sa:app/888
    - trnv1:rsc:trip-com:iam:sa:app/889

2.4.2 Адаптеры Enforcer​


Агностицизм плоскости управления требует адаптеры для преобразования ACP в определенные форматы Enforcer.


  • Адаптер ACP->CCNP (основной вариант использования в настоящее время)
  • Адаптер ACP->AuthorizationPolicy (вариант использования Istio, POC проверен)

2.4.3 Отправка (и согласование) политики в кластеры Kubernetes​


Другая часть плоскости управления толкает трансформированный трафик политике учитывающие уровень данных в кластеры Kubernetes.


Мы используем kubefed (v2) для достижения этой цели:


  1. AccessControlPolicyреализован как CRD в kubefed
  2. acp2ccnp-adapter прослушивает ресурсы ACP и преобразует их в FCCNP (Federated CCNP)
  3. kubefed-controller-manager прослушивает ресурсы FCCNP, преобразует их в CCNP и подталкивает последний к указанным кластерам-членам kubernetes в спецификации FCCNP.

Теперь все технические опоры нашего храма безопасности завершены, т.к. пример — теперь вы можете создать ACP из файла yaml, и он будет автоматически преобразуется в FCCNP, затем преобразуется в CCNP и далее передается в отдельные кластеры Kubernetes — но только если вы можете получить доступ к kubefed и знать «сырой» yaml, модель ACP и т. д.


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


2.4.4 Интеграция в платформу CD​


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


AuthN и AuthZ здесь относятся к материалам проверки и предоставления привилегий. участвует во время запроса изменения политики (добавить/обновить/удалить) от пользователей.


1653297072192.png


Рабочий процесс запроса политики на стороне пользователя

Если авторизованный пользователь является владельцем приложения, он может отправить запрос с чем-то вроде этого:

Содержание: я владелец приложения <appid>, и я хотел бы получить доступ к вашему ресурс <resource identifier>( например, имя кластера Redis).

Причина: <some reason>.

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

  • Клиентский доступ
    • Владелец клиентского приложения мог видеть, к каким ресурсам может получить доступ приложение;
    • Владелец серверного приложения мог видеть, какие клиентские приложения могут получить доступ к этому ресурсу;
  • Доступ администратора

2.4.5 Подведение итогов​

1653298203861.png



Высокоуровневая архитектура управления

2.5 Типовой рабочий процесс​


Предположим, у нас есть
  • Клиентское приложение с меткой модуля appid=888(уникальный для каждого приложения), принадлежащий Алисе,
  • База данных в памяти с меткой модуля redis-cluster=bobs-cluster(уникальный для каждой базы данных), принадлежащий Бобу,
Затем Алиса хочет, чтобы ее приложение получило доступ к базе данных Боба, вот рабочий процесс:

  • Алиса
    • вход на платформу CD
    • перейти к app 888 страница
    • нажмите «Redis Access Request»,
    • выберите bobs-cluster, Отправить запрос
  • Запрос отправляется лицам из списка утверждения
  • Запрос рассмотрен и одобрен
    • Одобрено непосредственным руководителем Алисы
    • Одобрено Бобом
    • Утверждено службой безопасности (при необходимости)
  • Платформа CD: вызываем API уровня управления, добавляем ACP в kubefed
  • Добавлен ACP в kubefed
  • Адаптер ACP->CCNP: при прослушивании добавленного ACP создает FCCNP
  • kubefed-controller-manager: при прослушивании созданного FCCNP рендерит CCNP и отправляет в указанные кластеры Kubernetes (kube-apiserver)
  • Все cilium-агенты во всех (покрытых CCNP) кластерах Kubernetes: на прослушивании при создании CCNP выполняет принудительное применение политики для модуля (если есть redis-cluster=bobs-clusterpod находится на узле). Применяется CCNP.

3 Внедрение в производство​


3.1 Оценка пропускной способности​


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


3.1.1 Идентификационное пространство​


Cilium описывает свою концепцию идентичности в Documentation: Identity . Он имеет тождественное пространство 64K для одного кластера что происходит от его представления 16-битного идентификатора идентификатора :


Код:
// pkg/identity/numericidentity.go

// NumericIdentity is the numeric representation of a security identity.
//
// Bits:
//    0-15: identity identifier
//   16-23: cluster identifier
//      24: LocalIdentityFlag: Indicates that the identity has a local scope
type NumericIdentity uint32

Идентичности разных кластеров избегают перекрытия уникальным кластером cluster-idс.


Но что для нас значит 64K? Ознакомимся механизм распределения идентификаторов .


3.1.2 Механизм распределения идентификаторов​


Короткий ответ заключается в том, что Cilium выделяет идентификаторы для структур с определенными метками безопасности : модули с одинаковыми группами меток имеют одинаковую идентичность.

1653299172972.png



Распределение удостоверений в Cilium из Cilium Doc


Здесь возникает одна проблема для больших кластеров: список меток по умолчанию, используемый для получения тождества очень маленький, что приводит к назначению каждому компьютеру своих меток - например, если вы используете statefulsets, pod-name лейбл будет присвоен, и он уникален для каждого модуля, как показано ниже:

Код:
$ cilium endpoint list
ENDPOINT   IDENTITY   LABELS (source:key[=value])                              IPv4       STATUS
2362       322854     k8s:app=cilium-smoke                                     10.2.2.2   ready
                      k8s:io.cilium.k8s.policy.cluster=default
                      k8s:io.cilium.k8s.policy.serviceaccount=default
                      k8s:statefulset.kubernetes.io/pod-name=cilium-smoke-2

2363       288644     k8s:app=cilium-smoke                                     10.2.2.5   ready
                      k8s:io.cilium.k8s.policy.cluster=default
                      k8s:io.cilium.k8s.policy.serviceaccount=default
                      k8s:statefulset.kubernetes.io/pod-name=cilium-smoke-3

Хотя это не повредит окончательному применению политики (например, при указании app=cilium-smokeв CNP он будет охватывать все машины этого statefulset), это запрещает масштабирование кластера Kubernetes : 64K структуры будут верхней границей для каждого кластера, что неприемлемо для крупных компаний.


Эту проблему можно обойти, указав собственные метки, относящиеся к безопасности.


3.1.3 Настройка меток, относящихся к безопасности​


Например, если мы хотим
  • Все структуры с одинаковым com.trip/appid=<appid> иметь одну и ту же личность
  • Все структуры с одинаковым com.trip/redis-cluster-name=<name> разделять ту же идентичность

тогда мы могли бы настроить label-опцию cilium-agent:

Код:
reserved:.* k8s:!io.cilium.k8s.namespace.labels.* k8s:io.cilium.k8s.policy k8s:com.trip/appid k8s:com.trip/redis-cluster-name

С этой настройкой все структуры com.trip/appid=888 (и в том же кластер с той же учетной записью службы) будет иметь один и тот же идентификатор (другие две метки автоматически вставляются агентом Cilium)

Код:
$ cilium endpoint list
ENDPOINT   IDENTITY   LABELS (source:key[=value])                        IPv4       STATUS
2113       322854     k8s:com.trip/appid=888                             10.5.1.4   ready
                      k8s:io.cilium.k8s.policy.cluster=k8s-cluster-1
                      k8s:io.cilium.k8s.policy.serviceaccount=default
2114       322854     k8s:com.trip/appid=888                             10.5.1.8   ready
                      k8s:io.cilium.k8s.policy.cluster=k8s-cluster-1
                      k8s:io.cilium.k8s.policy.serviceaccount=default

3.2 Прозрачность бизнес - процессов​


С технической точки зрения одним из преимуществ решения на основе CNP является то, что весь процесс контроля доступа является прозрачен как для клиентов, так и для серверов , что означает, что никаких изменений клиент/сервер не требуется.


Но означает ли это также прозрачное развертывание в бизнес? Ответ - нет.


Чтобы быть конкретным, CNP — однократный переключатель :


  • Если политика не указана (по умолчанию), она действует как allow-any
  • Если вы создали политику, например разрешающую appid=888 чтобы получить доступ к ресурсу, то все остальные клиенты, не входящие в эту политику, сразу получат отключение доступа, что может легко привести к сбоям в бизнесе, так как трудно получить accurate initial policy при этом не привлекая бизнес-пользователей если у вас есть только один шанс сделать это (применить политику).
Мы решили эту проблему путем многократное применение или уточнение политики с помощью режима аудита политик. При включенном режиме аудита и применении CNP все доступы, которые не разрешены CNP, будут по-прежнему разрешены, но показаны как audit, вместо прямого получения denied. Затем мы можем неторопливо уточнить наш CNP/CCNP, обновив проверенных клиентов в CNP.

3.3 Переключатель режима детального аудита политик​


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

Код:
# Node-level
$ cilium config PolicyAuditMode=true

Код:
# Endpoint-level
$ cilium endpoint config <ep_id> PolicyAuditMode=true

что является хорошим началом, но еще недостаточно.

Во время расследования мы заметили что режим аудита политики на уровне CNP был предложен. Это правильный путь, но нет четкого графика (на самом деле не закончил до написания этого поста).

3.3.1 Режим аудита политик на уровне ресурсов

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

Реализация:
  1. Добавьте контроллер в kubefed для переключения режима аудита ресурса, по сути, это изменит конкретную метку на всех модулях ресурс, что-то вроде policy-audit-mode=true/false
  2. Отправка в кластеры Kubernetes с помощью kubefed-controller-managerтак же, как нажатие CCNP
  3. Взломанный cilium-agent для соблюдения метки модели аудита политик.

Эта функция реализована как необязательная функция, поэтому мы можем включать/выключать ее с параметром конфигурации cilium-agent. При настройке его как выкл., cilium-agent вернется к поведению сообщества и просто проигнорирует метки.


3.3.2 Пережить перезагрузку (сохранить конфигурацию)​


Изменения в агенте были небольшими, так как мы повторно использовали код включения/выключения аудита на уровне конечной точки. Но нужна одна дополнительная конфигурация чтобы настройка режима аудита выдержала перезагрузку . Хорошей новостью является то, что cilium также обеспечивают эту конфигурацию. просто добавляю keep-config: trueв карту конфигурации агента.


3.4 Управление белым списком​


Нормальный АКП должен быть [app list] -> specific-resource политика контроля входящего трафика, но есть это также [app list] -> *требование, например, некоторые инструменты управления должны получить доступ ко всем ресурсам. Поэтому нам нужна поддержка групповой политики или белого списка. В частности, мы поддерживаем два вида белых списков.


3.4.1 Белый список ACP​


Пример, показанный ниже,


Код:
kind: AccessControlPolicy
metadata:
  name: management-tool-whitelist
spec:
  description: ""
  statements:
  - actions:
    - credis:connect
    effect: allow
    resources:
    - trnv1:rsc:trip-com:redis:clusters:*
    subjects:
    - trnv1:rsc:trip-com:iam:sa:app/858
    - trnv1:rsc:trip-com:iam:sa:app/676

он будет преобразован в следующий FCCNP:

Код:
apiVersion: types.kubefed.io/v1beta1
kind: FederatedCiliumClusterwideNetworkPolicy
metadata:
spec:
  placement:
    clusterSelector: {}
  template:
    metadata:
      labels:
        name: management-tool-whitelist
    spec:
      endpointSelector:
        matchExpressions:
        - key: k8s:com.trip/redis-cluster-name
          operator: Exists
      ingress:
      - fromEndpoints:
        - matchLabels:
            k8s:com.trip/appid: "858"
        - matchLabels:
            k8s:com.trip/appid: "676"
        toPorts:
        - ports:
          - port: "6379"
            protocol: TCP

затем обрабатывается и передается в кластеры-члены как CCNP.

3.4.2 Белый список CIDR​


В настоящее время мы создаем белый список CIDR напрямую через FCCNP:

Код:
apiVersion: types.kubefed.io/v1beta1
kind: FederatedCiliumClusterwideNetworkPolicy
metadata:
  name: cidr-whitelist-1
spec:
  placement:
    clusterSelector: {}    # Push to all member k8s clusters
  template:
    metadata:
      labels:
        name: cidr-whitelist-1
    spec:
      endpointSelector:
        matchExpressions:
        - key: k8s:com.trip/redis-cluster-name
          operator: Exists
      ingress:
      - fromCIDR:
        - 10.5.0.0/24
        toPorts:
        - ports:
          - port: "6379"
            protocol: TCP
      - fromCIDR:
        - 10.6.0.0/24
        toPorts:
        - ports:
          - port: "6379"
            protocol: TCP


3.5 Пользовательские конфигурации​


Наши настройки, среди которых некоторые непосредственно связаны с безопасностью, а некоторые для надежность (например, быть более устойчивым к отказам компонентов):


  • allocator-list-timeout: 48h
  • api-rate-limit: {"endpoint-create":"rate-limit:1000/s,rate-burst:256,auto-adjust:false,parallel-requests:256", "endpoint-delete":"rate-limit:1000/s,rate-burst:256,auto-adjust:false,parallel-requests:256", "endpoint-get":"rate-limit:1000/s,rate-burst:256,auto-adjust:false,parallel-requests:256", "endponit-patch":"rate-limit:1000/s,rate-burst:256,auto-adjust:false,parallel-requests:256", "endpoint-list":"rate-limit:10/s,rate-burst:10,auto-adjust:false,parallel-requests:10"}
  • cluster-id: <unique id>
  • cluster-name: <unique name>
  • disable-cnp-status-updates: true
  • enable-hubble: true
  • k8s-sync-timeout: 600s
  • keep-config: true
  • kvstore-lease-ttl: 86400s
  • kvstore-max-consecutive-quorum-errors: 5
  • labels: <custom labels>
  • log-driver: syslog
  • log-opt: {"syslog.level":"info","syslog.facility":"local5"}
  • masqurade: false
  • monitor-aggregation: maximum
  • monitor-aggregation-interval: 600s
  • sockops-enable: true
  • tunnel=disabled: прямая маршрутизация с BIRDкак агент BGP

3.6 Регистрация, мониторинг и оповещение​


  • Написал простенькую программу Hubble adapter for OpenTelemetry в Журнал аудита плоскости управления общего назначения в режиме реального времени
    • Запуск в качестве «сайдкара» к каждому cilium агенту
    • Подобно Hubble adapter for OpenTelemetry релиз в недавней Cilium v1.11
  • Отправлять журналы аудита в ClickHouse, таким же образом мы агрегировали наши первоначальные политики для каждого ресурса.
  • Визуализация с помощью внутренней инфраструктуры (на основе Kibana)
  • Оповещение с внутренней инфраструктурой

1653302128846.png

Журнал аудита в нашем формате журнала аудита общего назначения

1653302257454.png

Некоторые общие сводки журналов аудита

3.7 Стратегия развертывания​


Учитывая все вышесказанное, вот наша стратегия развертывания:


  1. Включить режим аудита политик : проверять все и отправлять журналы аудита центральная инфраструктура регистрации
  2. Определить первоначальную политику : запустить простую программу для агрегирования ACP для определенного ресурса из его журнала истории и применить ACP
  3. Уточнить начальную политику : обновить ACP, если effect=audit найдены обращения к ресурсу
  4. Опубликовать план контроля доступа : пусть разработчики приложение знают, что контроль доступа будет включен, а также процедуры запроса политики самопомощи, интегрированные в платформу CD
  5. Официально включить политику : отключить режим аудита политик для ресурс через переключатель уровня ресурса, и все новые клиентские приложения, которые хотели бы получить доступ на этот ресурс должны пройти процесс запроса тикета.

3.8 Даунгрейд при системных сбоях​


Одной из ключевых подготовительных работ перед запуском чего-либо в производство является планы реагирования на системные сбои мы использовали cilium-compose для развертывания Cilium, и вот наша СОП пониженной версии:
1653302702440.png


Сценарии понижения версии при системных сбоях
Вкратце, когда происходят системные сбои, которые требуют от нас отключить контроль доступа , мы бы реагировали по трем основным сценариям:


  1. Кластер Kubefed и кластеры-члены Kubernetes готовы: мы можем отключить ACP путем переключения режима аудита политик на уровне ресурсов.
  2. Кластер Kubefed уже вышел из строя, но кластеры-участники Kubernetes готовы, мы можем:
    1. Отключите нашу функцию аудита на уровне ресурсов на cilium-agent, это вернет cilium-agent к поведению сообщества, а затем
    2. cilium config PolicyAuditMode=true чтобы открыть режим аудита для всех модулей на узле

    Мы могли бы сделать это для одного узла или группы узлов с salt.
  3. Кластер Kubefed и все кластеры Kubernetes-членов вышли из строя:
    начала полностью отключите cilium-agent (чтобы он не согласовывал политики для конечных точек), затемиспользованием bpftoolчтобы напрямую написать необработанный all any правило для каждого модуля (конечной точки), команды, показанные ниже:
    Код:
     # Check if allow-any rule exists for a specific endpoint 3240
     root@cilium-agent:/sys/fs/bpf/tc/globals# bpftool map lookup pinned cilium_policy_03240 key hex 00 00 00 00 00 00 00 00
     key:
     00 00 00 00 00 00 00 00
    
     Not found
    
    # Insert an allow-any rule
     root@cilium-agent:/sys/fs/bpf/tc/globals# bpftool map update pinned cilium_policy_03240 key hex 00 00 00 00 00 00 00 00 value hex 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 noexist
    
    # Check again
     root@cilium-agent:/sys/fs/bpf/tc/globals# bpftool map lookup pinned cilium_policy_03240 key hex 00 00 00 00 00 00 00 00
     key:
     00 00 00 00 00 00 00 00
     value:
     00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00
    Запускаем команду bpftool в контейнере, созданном с образом cilium-agent  чтобы избежать возможных проблем с несоответствием версий.



3.9 Среды и текущий статус развертывания​


Это решение развернуk в наших UAT и производственных средах. и проработал более полугода.


Версия некоторых компонентов:
  • cilium 1.9.5с пользовательскими исправлениями (sts с фиксированным IP-адресом и режим аудита на уровне ресурсов)
  • Ядро: 4.14/4.19/5.10( 5%/80%/15%)
Некоторые цифры на момент написания статьи:
  • 7 000+ cilium-узлов (серверы без ПО), пересекающие несколько кластеров Kubernetes.
  • 170 тыс.+ cilium
  • 40 000+ CER (BM/VM/модули без питания cilium )
  • 4K+ CCNP (уменьшено с пикового 10K+с некоторой работой по агрегации политик)
  • 800 тыс.+ потоков в минуту
Возможности CNP, которые мы использовали:
  • Правила L3/L4
  • Селекторы ярлыков
  • Селекторы CIDR
Возможности, которые мы не использовали:

  • Правила L7 (сделал только POC, написал плагин L7 для управления доступом на уровне приложений)
  • Селекторы полных доменных имен
  • Селекторы пространства имен

4 Обсуждения​


4.1 Сравнение ClusterMesh и KVStoreMesh

ClusterMesh для больших кластеров имеет проблемы со стабильностью, что приводит к каскадным сбоям. Поведение подробно описанои здесь показан только типичный сценарий:
1653304343301.png


Распространение и усиление отказа в ClusterMesh
Следуя номерам шагов на картинке:

  1. kube-apiserver@cluster-1 терпит неудачу
  2. Все cilium-agents@cluster-1 терпят неудачу, так как не могут подключиться к kube-apiserver@cluster-1
  3. Все cilium-agents@cluster-1 начинают перезапускаться, и при запуске они подключатся к
    • kube-apiserver@cluster-1
    • kvstore@cluster-1
    • kvstore@cluster-2
  4. kvstore@cluster-1 вниз, большие объемы одновременных операций LisWatch с тысяч узлов привели к его сбою (например, он выполнял резервное копирование, уже в состоянии высокого уровня ввода-вывода),
  5. Все cilium-agents@cluster-1и все cilium-agents@cluster-2 вниз, когда они соединяются с kvstore@cluster-1,
  6. Все cilium-agents@cluster-1и все cilium-agents@cluster-2 начинают перезапускаться, и аналогичным образом это оказывает значительное давление:
    • kvstore@cluster-1
    • kvstore@cluster-2
    • kube-apiserver@cluster-2
  7. kvstore@cluster-2терпит неудачу
  8. Все cilium-agents@cluster-1и все cilium-agents@cluster-2 down
  9. Все cilium-agents@cluster-1и все cilium-agents@cluster-2 begin to restart
  10. kube-apiserver@cluster-2происходит сбой, так как он не может одновременно обслуживать ListWatch от тысяч агентов в кластере-2.

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

4.2 CER против CEW​

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

  • CER ненавязчив и прозрачен для устаревших рабочих нагрузок. Нужен только синхронизировать метаданные рабочих нагрузок с кластером Cilium; CEW с другой стороны навязчива для устаревших систем, так как необходимо установить cilium-agent в каждую виртуальную машину со значительными изменениями.
  • CER работает только тогда, когда устаревшие рабочие нагрузки действуют как клиенты на входе модулей Cilium. точка применения политики, в то время как CEW поддерживает собственную политику входящего/исходящего трафика.

4.2.2 CiliumEndpoint / CiliumEndpoint / externalEndpoint​


Эти три понятия очень похожи по названию, постараемся их немного пояснить.

CiliumEndpoint является концептом, локальным для узла, и его данные сериализуются в локальный файл на узле:​

Код:
// pkg/endpoint/endpoint.go

// Endpoint represents a container or similar which can be individually
// addresses on L3 with its own IP addresses.
//
// The representation of the Endpoint which is serialized to disk for restore
// purposes is the serializableEndpoint type in this package.
type Endpoint struct {
    ...
    IPv4 addressing.CiliumIPv4
    SecurityIdentity *identity.Identity `json:"SecLabel"`
    K8sPodName string
    K8sNamespace string
    ...
}

Код:
root@node-1 $ cilium endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                       IPv4       STATUS
           ENFORCEMENT        ENFORCEMENT

139        Disabled           Disabled          263455     k8s:io.cilium.k8s.policy.cluster=cluster-1        10.2.4.4   ready
                                                           k8s:io.cilium.k8s.policy.serviceaccount=default
                                                           k8s:io.kubernetes.pod.namespace=default

CiliumEndpointэто Cilium CRD в Kubernetes:

Код:
root@master: $ k get pods cilium-smoke-0 -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP         NODE     NOMINATED NODE   READINESS GATES
cilium-smoke-0   1/1     Running   2          10d   10.2.4.4   node-1   <none>           <none>

root@master: $ k get ciliumendpoints.cilium.io cilium-smoke-0
NAME             ENDPOINT ID   IDENTITY ID   INGRESS ENFORCEMENT   EGRESS ENFORCEMENT   VISIBILITY POLICY   ENDPOINT STATE   IPV4
cilium-smoke-0   139           263455                                                                       ready            10.2.4.4

root@master: $ k get ciliumendpoints.cilium.io cilium-smoke-0 -o yaml
apiVersion: cilium.io/v2
kind: CiliumEndpoint
metadata:
  ....
status:
  external-identifiers:
    container-id: 44c4bdb1f0533c6d7cef396
    k8s-namespace: default
    k8s-pod-name: cilium-smoke-0
    pod-name: default/cilium-smoke-0
  id: 139
  identity:
    id: 263455
    labels:
    - k8s:io.cilium.k8s.policy.cluster=cluster-1
    - k8s:io.cilium.k8s.policy.serviceaccount=default
    - k8s:io.kubernetes.pod.namespace=default
  named-ports:
  - name: cilium-smoke
    port: 80
    protocol: TCP
  networking:
    addressing:
    - ipv4: 10.2.4.4
    node: 10.6.6.6
  state: ready

ciliumexternalEndpoint представляет собой внутреннюю структуру, включающую в себя все конечные точки в удаленных кластерах в настройках ClusterMesh. Например, если кластер-1 и кластер-2 настроены как ClusterMesh, то все конечные точки в кластере-2 будут отображаться как externalEndpoint в cilium-agent кластера-1.

Код:
// pkg/k8s/endpoints.go

// externalEndpoints is the collection of external endpoints in all remote
// clusters. The map key is the name of the remote cluster.
type externalEndpoints struct {
    endpoints map[string]*Endpoints
}

// Endpoints is an abstraction for the Kubernetes endpoints object. Endpoints
// consists of a set of backend IPs in combination with a set of ports and
// protocols. The name of the backend ports must match the names of the
// frontend ports of the corresponding service.
type Endpoints struct {
    // Backends is a map containing all backend IPs and ports. The key to
    // the map is the backend IP in string form. The value defines the list
    // of ports for that backend IP, plus an additional optional node name.
    Backends map[string]*Backend
}

4.3 Режим аудита политик на уровне ресурсов и на уровне CNP​


Мы считаем, что режим аудита на уровне CNP — правильный способ выполнить эту работу. Для сравнения, наш хак не является достойным решением, так как он включает в себя введение еще одного контроллера для согласования конкретных меток модулей. Если бы уровень CNP был завершен и готов к использованию в производстве в будущем, мы бы подумайте о том, чтобы принять это.


4.4 Перенос идентификаторов: асинхронный, туннельный и SPIFFE​


Еще одна важная вещь, касающаяся идентичности Cilium, о которой не говорилось: как определяется личность пакета когда пакет прибывает в точку применения политики? Ответ: это зависит от прямого режима маршрутизации , Cilium выделяет и синхронизирует удостоверения через KVStore, ниже приведена краткая временная последовательность, показывающая, как синхронизируется идентификация и применяется политика:


1653305815955.png




Распространение удостоверений во время масштабирования клиента Cilium

  • Модули сервера находятся на узле Node2.
  • Новый клиентский модуль создается на узле Node1.

Картинка пытается проиллюстрировать как удостоверение клиентского модуля прибыло на Node2 до прибытие его пакетов . Теоретически, есть вероятность, что личность приходит после пакетов , что приведет к немедленному отказу.


Соответствующие стеки вызовов

Код:
__section("from-netdev")
from_netdev
  |-handle_netdev
      |-validate_ethertype
      |-do_netdev
          |-identity = resolve_srcid_ipv4()                // extract src identity
          |-ctx_store_meta(CB_SRC_IDENTITY, identity)      // save identity to ctx->cb[CB_SRC_IDENTITY]
          |-ep_tail_call(ctx, CILIUM_CALL_IPV4_FROM_LXC)   // tail call
                               |
 |------------------------------
 |
__section_tail(CILIUM_MAP_CALLS, CILIUM_CALL_IPV4_FROM_LXC)
tail_handle_ipv4_from_netdev
  |-tail_handle_ipv4
      |-handle_ipv4
          |-ep = lookup_ip4_endpoint()
          |-ipv4_local_delivery(ctx, ep)
              |-tail_call_dynamic(ctx, &POLICY_CALL_MAP, ep->lxc_id);

Туннельный (VxLAN) режим встраивает личность в в tunnel_idполе (соответствует Поле VNI в заголовке VxLAN ) каждого отдельного пакета, поэтому вышеупомянутый сценарий отказа никогда не произойдет:
Код:
handle_xgress                               // for packets leaving container
 |-tail_handle_ipv4
    |-encap_and_redirect_lxc
       |-encap_and_redirect_lxc
           |-__encap_with_nodeid(seclabel)  // seclabel==identity
              |-key.tunnel_id = seclabel
              |-ctx_set_tunnel_key(&key)
                 |-skb_set_tunnel_key()     // or call xdp_set_tunnel_key__stub()
                    |-bpf_skb_set_tunnel_key  // kernel: net/core/filter.c

Также есть проблем отслеживание SPIFFE (Secure Production Identity Framework для Всем) поддержка в Cilium, которая восходит к 2018 году и продолжается до сих пор.

4.5 Вопросы производительности​


Возможно, самая удивительная часть сетевых политик на базе Cilium: включение CNP не замедлит работу - наоборот повысит производительность немного! Ниже приведен один из наших тестов:
1653306247189.png


где мы можем видеть, что после применения входного CCNP к серверному модулю его количество запросов в секунду увеличивается, а задержка уменьшается. Но почему? Код говорит правду. Если политика не применяется (по умолчанию), Cilium вставит политику по умолчанию, allow-all политика для каждого модуля:
Код:
    |-regenerateBPF                                                           //    pkg/endpoint/bpf.go
       |-runPreCompilationSteps                                               //    pkg/endpoint/bpf.go
       |  |-regeneratePolicy                                                  //    pkg/endpoint/policy.go
       |  |  |-UpdatePolicy                                                   //    pkg/policy/distillery.go
       |  |  |  |-cache.updateSelectorPolicy                                  //    pkg/policy/distillery.go
       |  |  |     |-cip = cache.policies[identity.ID]                        //    pkg/policy/distillery.go
       |  |  |     |-resolvePolicyLocked                                      // -> pkg/policy/repository.go
       |  |  |-e.selectorPolicy.Consume                                       //    pkg/policy/distillery.go
       |  |     |-if !IngressPolicyEnabled || !EgressPolicyEnabled
       |  |     |  |-AllowAllIdentities(!IngressPolicyEnabled, !EgressPolicyEnabled)

И при поиске политики для входящего пакета вот соответствующая логика:

Код:
__policy_can_access                         // bpf/lib/policy.h
  |-if p = map_lookup_elem(l3l4_key); p     // L3+L4 policy
  |    return TC_ACK_OK
  |-if p = map_lookup_elem(l4only_key); p   // L4-Only policy
  |    return TC_ACK_OK
  |-if p = map_lookup_elem(l3only_key); p   // L3-Only policy
  |    return TC_ACK_OK
  |-if p = map_lookup_elem(allowall_key); p // Allow-all policy
  |    return TC_ACK_OK
  |-return DROP_POLICY;                     // DROP



Соответствующий приоритет:
  1. Политика L3+L4
  2. Политика только L4
  3. Политика только L3
  4. Разрешить все политики
  5. drop
Как видно, политика по умолчанию имеет приоритет только выше, чем DROP. Если применяется CNP, код вернется раньше, чем в случае политики по умолчанию, и мы думаем, что это объясняет увеличение производительности.

4.6 Частые регенерации BPF​

При создании модуля может быть выделена новая идентификация. При получении события создания удостоверения все Cilium агенты будут регенерировать BPF для всех модулей на узле, чтобы соблюдать личности, что является достаточно тяжелой операцией, так как составление и перезагрузка BPF только для одного модуля заняла бы несколько секунд.
Событие создания удостоверения вызовет немедленную регенерацию BPF, но события удаления не будет, так как удаление личности по дизайну проходит через сборщик мусора.
Тогда мы можем задаться вопросом, что большинство модулей в кластере не должны иметь отношения к вновь созданному удостоверению, повторное создание всех модулей для каждого события удостоверения (создать/обновить/удалить) не было бы слишком расточительно (с точки зрения системных ресурсов такие как процессор, память и т. д.)?

Получается, что для неактуальных подов у cilium-agent есть логика «пропуска»:

Код:
// pkg/endpoint/bpf.go

    if datapathRegenCtxt.regenerationLevel > regeneration.RegenerateWithoutDatapath {
        // Compile and install BPF programs for this endpoint
        if regenerationLevel == RegenerateWithDatapathRebuild {
            e.owner.Datapath().Loader().CompileAndLoad()
            Info("Regenerated endpoint BPF program")
            compilationExecuted = true
        } else if regenerationLevel == RegenerateWithDatapathRewrite {
            e.owner.Datapath().Loader().CompileOrLoad()
            Info("Rewrote endpoint BPF program")
            compilationExecuted = true
        } else { // RegenerateWithDatapathLoad
            e.owner.Datapath().Loader().ReloadDatapath()
            Info("Reloaded endpoint BPF program")
        }

        e.bpfHeaderfileHash = datapathRegenCtxt.bpfHeaderfilesHash
    } else {
        Debug("BPF header file unchanged, skipping BPF compilation and installation")
    }

Большинство стручков пойдет в else логика, которая также объясняет, почему время регенерации P99 резко уменьшается после исключения bpfLogProg:
1653306869341.png


Вы можете дважды подтвердить это поведение, просмотрев объектные файлы bpf в /var/run/cilium/state/<endpoint id>а также /var/run/cilium/state/<endpoint id>_next.


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


5 Заключение и дальнейшая работа​


В этом посте рассказывается о нашем дизайне и реализации собственного облачного контроля доступа, решение для рабочих нагрузок Kubernetes (а также для устаревших рабочих нагрузок, если они действуют как клиенты). Решение в настоящее время используется для управления доступом L3/L4, и с более полученным опытом, мы расширим решение для большего количества вариантов использования. Мы хотели бы поблагодарить сообщество Cilium за их блестящую работу, и я лично хотел бы поблагодарить всех моих товарищей по команде и коллег за их замечательную работа над тем, чтобы сделать это возможным.

Перевод статьи https://arthurchiao.art/blog/trip-first-step-towards-cloud-native-security/#1-introduction
 

Вложения

  • 1653302813944.png
    1653302813944.png
    23.2 КБ · Просмотры: 5
  • 1653304376289.png
    1653304376289.png
    58.6 КБ · Просмотры: 3


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх