0%
Прочее

Архитектура Zero Trust в Kubernetes с использованием сервисной сети Istio

08.08.2024
~ 12 мин
726 просмотров

Zero Trust Architecture (ZTA) — это методика проектирования и внедрения информационных систем которая имеет девиз «никогда не доверяй, всегда проверяй» и подразумевает, что пользователи и устройства не должны считаться доверенными по умолчанию, а должны подтверждать свою личность и соответствие требованиям перед получением доступа к ресурсам.

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

Таким образом, каждый доступ к ресурсам и данным должен динамически:

  1. Проходить аутентификацию.
  2. Проходить авторизацию.
  3. Соответствовать установленным требованиям и политикам безопасности.
  4. Соблюдать принцип минимальных привилегий, предоставляя доступ только к необходимым ресурсам.

Что такое сервисный меш (Service Mesh)?

Сервисный меш (Service Mesh) представляет собой выделенный уровень (или слой), предназначенный для облегчения коммуникации между микросервисами с использованием прокси (sidecar proxy). Он также обеспечивает мониторинг, распределенное трассирование, безопасные соединения, автоматические ретраи запросов и обработку неудачных запросов.

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

Плоскость передачи данных (data plane) перехватывает вызовы между различными микросервисами и обрабатывает их, в то время как плоскость управления (control plane) является «мозгами» меша, управляющими прокси и предоставляющими API для целей управления и мониторинга.

Архитектура Istio

Istio — это открытая платформа сервис меша. Ниже приведена архитектура Istio:

 

Существуют два основных компонента:

  1. Плоскость передачи данных (Data Plane) представлена прокси-сервером Envoy, развернутым в виде сайдкар контейнера перед каждым микросервисом.
  2. Плоскость управления (Control Plane) включает в себя компоненты Istiod, в котором присутствуют Pilot, Citadel и Galley.

Плоскость передачи данных: прокси-сервер Envoy

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

Плоскость управления: Istiod

Плоскость управления Istiod состоит из нескольких важных компонентов:

  1. Pilot: Этот компонент отвечает за распространение конфигурации к прокси-серверам Envoy, включая настройку маршрутизации, балансировки нагрузки, управление сетью, обнаружение сервисов и обеспечение устойчивости. Он преобразует высокоуровневые правила маршрутизации в конфигурацию, специфичную для Envoy, и передает ее в реальном времени к сайдкар контейнерам.
  2. Citadel: Этот компонент отвечает за управление аутентификацией, контролем доступа и шифрованием, гарантируя безопасность в коммуникации между сервисами. Citadel управляет сертификатами и политиками безопасности.
  3. Galley: Galley отвечает за валидацию и распределение конфигурационных данных в рамках Istio. Он отслеживает изменения в конфигурации и удостоверяется, что они правильно распространяются через Pilot к прокси-серверам Envoy.


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

Реализация архитектуры «Zero Trust» с использованием Istio

В этом разделе мы рассмотрим, как:

  • Развернуть сервисный меш Istio на Kubernetes.
  • Применить взаимную аутентификацию (mTLS).
  • Применить управление доступом и авторизацию между микросервисами.

Развертывания сервисного меша Istio на Kubernetes

Для этого мы используем Helm-чарты:

resource "kubernetes_namespace" "istio" {
  metadata {
    name = "istio-system"
  }
}

resource "helm_release" "istio_base" {
  name       = "lupass-istio-base"
  repository = "https://istio-release.storage.googleapis.com/charts"
  chart      = "base"
  version    = "1.18.0"
  namespace  = kubernetes_namespace.istio.metadata.0.name
}

resource "helm_release" "istio_control_plane" {
  name       = "lupass-istiod"
  repository = "https://istio-release.storage.googleapis.com/charts"
  chart      = "istiod"
  version    = "1.18.0"
  namespace  = kubernetes_namespace.istio.metadata.0.name
  depends_on = [ helm_release.istio_base ]
}

Используем Helm-чарты istio-base для развертывания ресурсов кластера и CRD для Istio, а также Helm-чарт istiod для развертывания плоскости управления Istio.

После выполнения развертывания, выполняем команду terraform apply и проверяем, что результат соответствует ожиданиям:

> k get all -n istio-system
NAME                                              READY   STATUS    RESTARTS   AGE
pod/istiod-5f859db56c-kvrms                        1/1     Running   0          21h

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                             AGE
service/istiod           ClusterIP   10.43.208.116   <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP  21h

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/istiod  1/1     1            1           21h

NAME                                DESIRED   CURRENT   READY   AGE
replicaset.apps/istiod-5f859db56c   1         1         1       21h

NAME                                          REFERENCE           TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/istiod    Deployment/istiod   0%/80%   1         5         1         21h

> k api-resources | grep -i istio
wasmplugins.extensions.istio.io/v1alpha1      true      WasmPlugin
istiooperators.iop,io.install.istio.io/v1alpha1 true  IstioOperator
destinationrules.dr.networking.istio.io/v1beta1 true DestinationRule
envoyfilters.networking.istio.io/v1alpha3      true      EnvoyFilter
gateways.gw.networking.istio.io/v1beta1        true      Gateway
proxyconfigs.networking.istio.io/v1beta1       true      ProxyConfig
serviceentries.se.networking.istio.io/v1beta1  true      ServiceEntry
sidecars.networking.istio.io/v1beta1           true      Sidecar
virtualservices.vs.networking.istio.io/v1beta1  true      VirtualService
workloadentries.we.networking.istio.io/v1beta1 true      WorkloadEntry
workloadgroups.wg.networking.istio.io/v1beta1  true      WorkloadGroup
authorizationpolicies.security.istio.io/v1     true      AuthorizationPolicy
peerauthentications.pa.security.istio.io/v1beta1 true    PeerAuthentication
requestauthentications.ra.security.istio.io/v1  true      RequestAuthentication
telemetries.telemetry.telemetry.istio.io/v1alpha1 true  Telemetry

Для включения взаимной аутентификации (mTLS) и других функций Istio, необходимо внедрить сайдкар с Envoy (istio сайдкар) в наши поды.

Для этого мы просто применяем метку «istio-injection» со значением «enabled» на желаемых неймспейсах. Для этого создаем неймспейс с именем «my-application» и применяем к нему метку «istio-injection» со значением «enabled».

resource "kubernetes_namespace" "my_application" {
  metadata {
    name = "my-application"
    labels = {
      "istio-injection" = "enabled"
    }
  }
}

Таким образом, во все создаваемые в этом неймспейсе поды автоматически внедряются сайдкар прокси Envoy. Если попробовать развернуть 3 пода: microservice-A, microservice-B и microservice-C, то произойдет следующее:

apiVersion: v1
kind: Pod
metadata:
name: microservice-a
namespace: my-application
labels:
app: microservice-a
spec:
containers:
- name: microservice-a
image: nginx
---
kind: Service
apiVersion: v1
metadata:
name: microservice-a
namespace: my-application
spec:
selector:
app: microservice-a
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
  

apiVersion: v1
kind: Pod
metadata:
name: microservice-b
namespace: my-application
labels:
app: microservice-b
spec:
containers:
- name: microservice-b
image: nginx
---
kind: Service
apiVersion: v1
metadata:
name: microservice-b
namespace: my-application
spec:
selector:
app: microservice-b
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80

  
apiVersion: v1
kind: Pod
metadata:
name: microservice-c
namespace: my-application
labels:
app: microservice-c
spec:
containers:
- name: microservice-c
image: nginx
---
kind: Service
apiVersion: v1
metadata:
name: microservice-c
namespace: my-application
spec:
selector:
app: microservice-c
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
  1. Каждый из подов microservice-A, microservice-B и microservice-C будет автоматически внедрен сайдкар прокси Envoy.
  2. Эти сайдкар прокси будут обеспечивать безопасность, маршрутизацию и управление трафиком, между этими микросервисами.
  3. Istio позволит управлять межсервисным взаимодействием, включая взаимную аутентификацию и безопасное соединение (mTLS), а также маршрутизацию и авторизацию между этими сервисами.Запустите команду kubectl get all -n my-application для получения информации обо всех ресурсах (поды, службы и т. д.) в неймспейсе «my-application».

    > k get all -n my-application
    NAME                 READY   STATUS    RESTARTS   AGE
    pod/microservice-a    2/2     Running   0          19h
    pod/microservice-c    2/2     Running   0          19h
    pod/microservice-b    2/2     Running   0          17h
    
    NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
    service/microservice-a  ClusterIP   10.43.3.92       <none>        80/TCP    20h
    service/microservice-b  ClusterIP   10.43.139.10     <none>        80/TCP    20h
    service/microservice-c  ClusterIP   10.43.129.47     <none>        80/TCP    20h
    

Мы видим, что все поды имеют по два контейнера:

Контейнер приложения, который мы определили и развернули внутри каждого пода. Это ваше приложение или микросервис.

Сайдкар контейнер, который представляет собой прокси-сервер Envoy, внедренный Istio. Этот сайдкар прокси обеспечивает функциональность Istio, такую как маршрутизация, безопасность и управление трафиком для вашего приложения. Это позволяет Istio контролировать и обеспечивать безопасное и управляемое межсервисное взаимодействие в рамках вашей инфраструктуры.Например, если вы выполните команду kubectl get pod microservice-a -o yaml -n myapplication, вы увидите, что она отличается от вашего определения, потому что Istio внедрил сайдкар контейнер envoy proxy. По умолчанию весь трафик между подами, с внедреннымы сайдкар контейнерами Istio (envoy proxy), шифруется с использованием взаимной аутентификации (mTLS) в режиме PERMISSIVE.

В Istio вы можете настроить режим взаимной аутентификации (mTLS) для сайдкар прокси  в режим PERMISSIVE или STRICT:

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

STRICT: В режиме STRICT разрешен только mTLS-трафик. Это означает, что каждое взаимодействие между сервисами обязательно должно быть зашифровано с использованием взаимной аутентификации mTLS. Если сервис не поддерживает mTLS, то взаимодействие с этим сервисом будет отклонено.

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

Чтобы гарантировать, что разрешен только трафик с взаимной аутентификацией (mTLS), необходимо переключить режим на STRICT. Для этого создадим следующий ресурс:

resource "kubernetes_manifest" "peer_authentication_my_application" {
  manifest = yamldecode(<<EOT
    apiVersion: security.istio.io/v1beta1
    kind: PeerAuthentication
    metadata:
      name: default
      namespace: ${kubernetes_namespace.my_application.metadata.0.name}
    spec:
      mtls:
        mode: STRICT
EOT
  )
}

Этой конфигурацией мы сообщаем Istio, что для неймспейса «my-application» мы хотим принимать только трафик с взаимной аутентификацией (mTLS). Если мы хотим включить режим STRICT для всех неймспейсов, мы просто применяем эту конфигурацию в неймспейсе «istio-system». После выполнения команды terraform apply можно проверить, что все настроено как ожидалось.

> k get peerauthentication -n my-application
NAME      MODE     AGE
default   STRICT   20h
> k get peerauthentication -n my-application -o yaml
apiVersion: v1
items:
  - apiVersion: security.istio.io/v1beta1
    kind: PeerAuthentication
    metadata:
      creationTimestamp: "2023-07-09T14:20:29Z"
      generation: 3
      name: default
      namespace: my-application
      resourceVersion: "626785"
      uid: c73071a5-ca7e-4432-87c0-1ccb0126c384
    spec:
      mtls:
        mode: STRICT
kind: List
metadata:
  resourceVersion: ""

Давайте протестируем эту конфигурацию, развернув еще один под в другом неймспейсе, в котором Istio сайдкар не внедрен. Для этого мы используем неймспейс по умолчанию и создаем под с именем «external-a»:

apiVersion: v1
kind: Pod
metadata:
  name: external-a
  namespace: default
  labels:
    app: external-a
spec:
  containers:
    - name: external-a
      image: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: external-a
  namespace: default
spec:
  selector:
    app: external-a
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: 80

Затем разверните этот под, войдите в него с помощью команды bash и выполните curl, чтобы проверить, можем ли мы подключиться к микросервису «microservice-a»:

> k exec -it external-a -n default -- bash
root@external-a:/# curl -v microservice-a.my-application
*   Trying 10.43.3.92:80...
*   Connected to microservice-a.my-application (10.43.3.92) port 80 (#0)
> GET / HTTP/1.1
> Host: microservice-a.my-application
> User-Agent: curl/7.88.1
> Accept: */*
> 
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer
root@external-a:/#

Отлично! Сообщение «The connection is reset by peer» означает, что режим mTLS STRICT работает как ожидалось.

> k exec -it external-a -n default -- bash
root@external-a:/# curl -v microservice-a.my-application
*   Trying 10.43.3.92:80...
*   Connected to microservice-a.my-application (10.43.3.92) port 80 (#0)
> GET / HTTP/1.1
> Host: microservice-a.my-application
> User-Agent: curl/7.88.1
> Accept: */*
> 
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer
root@external-a:/#

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

Однако в архитектуре «Zero Trust» механизм взаимной аутентификации (mTLS) не является единственной мерой безопасности. Поэтому в следующем разделе мы рассмотрим, как реализовать управление доступом (Access Control) к нашим микросервисам, чтобы разрешать только необходимое взаимодействие. Это позволит более точно контролировать и ограничивать коммуникацию между микросервисами с учетом политик безопасности и авторизации.

Реализация управления доступом с помощью Istio: AuthorizationPolicy! Предположим, у нас есть 3 микросервиса: microservice-a, microservice-b и microservice-c, и мы хотим убедиться, что только microservice-b имеет право вызывать microservice-a и только с использованием GET-запроса к пути «/» (root path).

Как мы можем это сделать? С помощью кастомного ресурса Istio AuthorizationPolicy мы можем добиться этого, и даже большего уровня контроля. Таким образом, мы можем определить следующий AuthorizationPolicy:

resource "kubernetes_manifest" "authorization_policy_microservice_a" {
  manifest = yamldecode(<<EOT
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "microservice-b-call-microservice-a"
  namespace: ${kubernetes_namespace.my_application.metadata.0.name}
spec:
  action: ALLOW
  selector:
    matchLabels:
      app: microservice-a
  rules:
    - from:
        - source:
            principals: 
              - "cluster.local/ns/${kubernetes_namespace.my_application.metadata.0.name}/sa/microservice-b"
      to:
        - operation:
            methods: 
              - "GET"
            paths: 
              - "/"
EOT
  )
}

Что мы говорим Istio этой настройкой? Мы сообщаем Istio, что только поды с сервисным аккаунтом «microservice-b» в неймспейсе «my-application» могут вызывать поды с меткой «app»=»microservice-a» с операцией GET по пути «/». Итак, вкратце, на основе нашей конфигурации, только microservice-b может вызывать microservice-a, и только с операцией GET по пути «/». Все остальные вызовы будут отклонены!

ServiceAccount: (Сервисный аккаунт)

   resource "kubernetes_service_account_v1" "microservice_b" {
  metadata {
    name = "microservice-b"
    namespace = kubernetes_namespace.my_application.metadata.0.name
  }
}
resource "kubernetes_token_request_v1" "microservice_b" {
  metadata {
    name = kubernetes_service_account_v1.microservice_b.metadata.0.name
    namespace = kubernetes_namespace.my_application.metadata.0.name
  }
  spec {
  }
}

microservice-b:

apiVersion: v1
kind: Pod
metadata:
  name: microservice-b
  namespace: my-application
  labels:
    app: microservice-b
spec:
  serviceAccountName: microservice-b
  containers:
  - name: microservice-b
    image: nginx
---
kind: Service
apiVersion: v1
metadata:
  name: microservice-b
  namespace: my-application
spec:
  selector:
    app: microservice-b
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80
Для тестирования этой AuthorizationPolicy, мы можем выполнить три операции:
  • Вызов HTTP GET / из microservice-c к microservice-a должен быть ЗАПРЕЩЕН.
  • Вызов HTTP GET / из microservice-b к microservice-a должен быть РАЗРЕШЕН.
  • Вызов HTTP POST /create из microservice-b к microservice-a должен быть ЗАПРЕЩЕН.
root@microservice-c:/# curl microservice-a -v
*   Trying 10.43.3.92:80...
*   Connected to microservice-a (10.43.3.92) port 80 (#0)
> GET / HTTP/1.1
> Host: microservice-a
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden
< content-length: 19
< content-type: text/plain
< date: Mon, 10 Jul 2023 12:23:46 GMT
< server: envoy
< x-envoy-upstream-service-time: 10
< 
* Connection #0 to host microservice-a left intact
RBAC: access denied

Отлично! Полученное сообщение «RBAC: access denied» говорит о том, что доступ был отклонен в соответствии с нашей политикой авторизации. Прекрасно! Перейдем ко второму тесту.

Из микросервиса «microservice-b» выполните GET-запрос к микросервису «microservice-a».

 

root@microservice-b:/# curl microservice-a -v
*   Trying 10.43.3.92:80...
*   Connected to microservice-a (10.43.3.92) port 80 (#0)
> GET / HTTP/1.1
> Host: microservice-a
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< server: envoy
< date: Mon, 10 Jul 2023 12:26:20 GMT
< content-type: text/html
< content-length: 615
< last-modified: Tue, 13 Jun 2023 15:08:10 GMT
< etag: "6488865a-267"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 12
< 
* Connection #0 to host microservice-a left intact

Отлично, это работает как ожидалось! Перейдем к последнему тесту Из микросервиса «microservice-b» выполните POST-запрос на путь «/create».

root@microservice-b:/# curl -X POST microservice-a/create -v
*   Trying 10.43.3.92:80...
*   Connected to microservice-a (10.43.3.92) port 80 (#0)
> POST /create HTTP/1.1
> Host: microservice-a
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden
< content-length: 19
< content-type: text/plain
< date: Mon, 10 Jul 2023 13:28:16 GMT
< server: envoy
< x-envoy-upstream-service-time: 7
< 
* Connection #0 to host microservice-a left intact
RBAC: access denied

Отлично он ЗАПРЕЩЕН! Но это всего лишь несколько из множества функций, которые предоставляет Istio. Читайте наши статьи чтобы узнавать больше о Istio и других технологий из мира DevOps =)