Skip to content

Глава 12. Service Mesh и mTLS

В Главе 7 мы показали, как SPIFFE/SPIRE даёт рабочим нагрузкам криптографическую идентичность. В Главе 10 — как сетевые политики ограничивают трафик на L3/L4. Теперь соединяем эти концепции: service mesh автоматизирует mTLS между сервисами, добавляет L7-авторизацию и предоставляет наблюдаемость — три ключевых требования Zero Trust для east-west трафика.

Зачем service mesh для Zero Trust

NIST SP 800-207 (Section 3.1.2, Enclave Gateway Model) описывает шлюзы микросегментов как точки применения политик (PEP). Service mesh реализует эту модель: прозрачный прокси перед каждым сервисом выполняет функции PEP — аутентификацию, авторизацию и шифрование трафика.

Три проблемы, которые решает service mesh:

ПроблемаБез meshС mesh
ШифрованиеРазработчик настраивает TLS в каждом приложенииmTLS автоматически между всеми сервисами
ИдентичностьТокены передаются в заголовках, легко подделатьX.509 сертификаты с SPIFFE ID, ротация каждые 24 ч
АвторизацияВ коде приложения или middlewareДекларативные политики на уровне инфраструктуры

Ключевой принцип: service mesh переносит security из кода приложений в инфраструктуру. Разработчик не пишет TLS-код — mesh делает это прозрачно.

Три подхода к data plane

ПодходРазмещение проксиОписаниеПримеры
Sidecar (классический)Pod: App + Envoy sidecarКаждый pod получает свой проксиIstio sidecar, Linkerd
Per-Node (ambient / eBPF)Node: ztunnel DaemonSet для всех podsОдин прокси на ноду, L4 mTLSIstio ambient, Cilium
Waypoint (L7 on demand)Namespace: waypoint proxy для L7 политикДобавляется только когда нужен L7Istio ambient + waypoint

Istio

Istio — наиболее функциональный service mesh, проект CNCF (graduated, Jul 2023). На момент написания актуальная версия — 1.28.3 (Jan 19, 2026), в разработке 1.29.

Архитектура

Плоскость управления — единый бинарник istiod, объединяющий:

  • Pilot — конфигурация Envoy-прокси через xDS API
  • Citadel — выпуск и ротация X.509 сертификатов (встроенный CA)
  • Galley — валидация конфигурации из Kubernetes API

Плоскость данных — два режима:

  1. Sidecar mode — Envoy-прокси в каждом pod. Полная L7-функциональность: маршрутизация, retry, circuit breaking, L7 авторизация.

  2. Ambient mode (beta в Istio 1.22, GA в Istio 1.24) — двухуровневая архитектура:

    • ztunnel — per-node прокси на Rust (DaemonSet). Обеспечивает L4 mTLS, аутентификацию и телеметрию. Протокол HBONE (HTTP CONNECT + HTTP/2 мультиплексирование + mTLS).
    • Waypoint proxy — опциональный Envoy-прокси на уровне namespace/service для L7 политик. Добавляется только при необходимости.

Ambient mode снижает потребление ресурсов: один ztunnel на ноду вместо сотен sidecar. Roadmap 2025-2026 фокусируется на multicluster ambient (alpha в 1.27) и миграции sidecar → ambient.

PeerAuthentication: обязательный mTLS

PeerAuthentication определяет, принимает ли сервис mTLS-соединения:

yaml
# Источник: istio.io/docs/reference/config/security/peer_authentication
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system          # mesh-wide
spec:
  mtls:
    mode: STRICT                   # только mTLS, plaintext отклоняется

Режимы: STRICT (только mTLS), PERMISSIVE (mTLS + plaintext — для миграции), DISABLE.

Для Zero Trust единственный приемлемый режим — STRICT. Рекомендация: включить STRICT на уровне mesh (istio-system), исключения — только для конкретных портов (health checks от kubelet).

AuthorizationPolicy: L7-авторизация

AuthorizationPolicy реализует Zero Trust принцип "запретить всё, разрешить явно":

yaml
# Источник: istio.io/docs/reference/config/security/authorization-policy
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  selector:
    matchLabels:
      app: api-server
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - "cluster.local/ns/production/sa/frontend"
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/v1/*"]

Поле principals использует SPIFFE ID: spiffe://cluster.local/ns/<namespace>/sa/<service-account>. Istio по умолчанию генерирует SPIFFE-совместимые идентичности для всех meshed workloads.

Deny-by-default: создайте пустую политику ALLOW без правил для namespace — это заблокирует весь трафик, кроме явно разрешённого.

yaml
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: production
spec: {}                           # пустой spec = запретить всё

Сертификаты и ротация

Istio по умолчанию выпускает workload-сертификаты с TTL 24 часа (настраивается через DEFAULT_WORKLOAD_CERT_TTL). Максимальный TTL — 90 дней (2160h, задаётся через MAX_WORKLOAD_CERT_TTL). Ротация автоматическая через SDS API — без перезапуска pod, без записи ключей на диск.

Linkerd

Linkerd — легковесный service mesh, проект CNCF (graduated, Jul 2021). Актуальная версия — 2.17 (Dec 5, 2024). Data plane — linkerd2-proxy, написанный на Rust (tokio/hyper/tower), специально для Linkerd.

Лицензирование: с версии 2.15 (Feb 2024) open source проект не выпускает stable-релизы. Стабильные сборки доступны через Buoyant Enterprise for Linkerd (бесплатно для компаний <50 сотрудников). Edge-релизы остаются полностью открытыми (Apache 2.0).

mTLS по умолчанию

Linkerd включает mTLS автоматически при добавлении сервиса в mesh — без конфигурации. Сертификаты генерируются встроенным identity controller на основе Kubernetes ServiceAccount.

С версии 2.15 Linkerd поддерживает SPIFFE ID для идентичности рабочих нагрузок. SPIRE может использоваться как внешний identity provider, в том числе для non-Kubernetes workloads (VM, bare-metal) через mesh expansion.

Авторизация

Linkerd использует набор CRD для авторизации:

yaml
# Источник: linkerd.io/2-edge/reference/authorization-policy/
apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
  name: api-server
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  port: 8080
  proxyProtocol: HTTP/2
---
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend
  namespace: production
spec:
  targetRef:
    group: policy.linkerd.io
    kind: Server
    name: api-server
  requiredAuthenticationRefs:
  - name: frontend-authn
    kind: MeshTLSAuthentication
    group: policy.linkerd.io
---
apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
  name: frontend-authn
  namespace: production
spec:
  identities:
  - "frontend.production.serviceaccount.identity.linkerd.cluster.local"

Режимы default policy (устанавливаются при установке или через annotation config.linkerd.io/default-inbound-policy):

  • all-unauthenticated — разрешить всё (по умолчанию)
  • all-authenticated — только meshed clients
  • cluster-authenticated — meshed clients в том же кластере
  • deny — запретить всё без явных политик
  • audit — логировать нарушения без блокировки (с Linkerd 2.16)

Для Zero Trust рекомендуется deny или cluster-authenticated + явные AuthorizationPolicy.

Cilium Service Mesh

Cilium (CNCF graduated) использует eBPF для сетевого стека и предлагает service mesh без sidecar. Актуальная версия — 1.19.0 (Feb 4, 2026).

Архитектура

  • L3/L4 — обработка полностью в eBPF (ядро Linux). Нет userspace-прокси.
  • L7 — per-node Envoy. Трафик перенаправляется в Envoy только когда нужна L7-политика (HTTP, gRPC).

Mutual Authentication (beta)

Cilium реализует mutual authentication через SPIFFE/SPIRE:

  1. Cilium Agent получает SPIFFE ID для workloads через DelegatedIdentity API
  2. Аутентификация выполняется out-of-band — cilium-agent на одной ноде устанавливает TLS-рукопожатие с cilium-agent на другой ноде
  3. Шифрование трафика — отдельно через WireGuard или IPsec (node-to-node)

Важное ограничение: mutual authentication в Cilium не равна mTLS в традиционном смысле. Аутентификация (proof of identity) и шифрование (confidentiality) — раздельные механизмы. Без включения WireGuard/IPsec трафик передаётся в plaintext даже после успешной аутентификации. Источник: cilium.io/blog/2024/03/20/improving-mutual-auth-security/

С Cilium 1.19 доступна ztunnel transparent encryption (beta) — per-namespace mTLS-шифрование через ztunnel-прокси. Поддерживается только TCP.

Гибридный подход: Cilium + Istio

Многие организации комбинируют Cilium (CNI + L3/L4 Network Policies) с Istio ambient (mTLS + L7 авторизация). Cilium обеспечивает, что трафик, перенаправленный к ztunnel или sidecar Istio, не прерывается. Документация: docs.cilium.io/en/stable/network/servicemesh/istio/.

Сравнение mesh-решений для Zero Trust

ХарактеристикаIstioLinkerdCilium
Data planeEnvoy (sidecar) / ztunnel+waypoint (ambient)linkerd2-proxy (Rust, sidecar)eBPF + per-node Envoy
mTLSAutomatic, STRICT modeAutomatic, по умолчаниюBeta, WireGuard/ztunnel
СертификатыIstiod CA, TTL 24h, SPIFFEIdentity controller, SPIFFE (2.15+)SPIRE DelegatedIdentity
L7 авторизацияAuthorizationPolicyAuthorizationPolicy + ServerCiliumNetworkPolicy L7
SPIRE интеграцияEnvoy SDS API (с 1.14)SPIRE для VM/non-K8s (с 2.15)Встроенная (mutual auth)
SidecarДа (sidecar) / Нет (ambient)ДаНет
CNCF статусGraduated (Jul 2023)Graduated (Jul 2021)Graduated (Oct 2022)
ЛицензияApache 2.0Apache 2.0 (edge), vendor stableApache 2.0

SPIRE как внешний CA для Istio

В Главе 7 мы развернули SPIRE на kind-кластере. Теперь покажем, зачем и как интегрировать SPIRE с Istio.

Зачем заменять встроенный CA

Встроенный Istio CA (Citadel) — упрощённый: он полагается на Kubernetes ServiceAccount для идентичности, без гранулярной аттестации рабочих нагрузок. SPIRE добавляет:

  • Аттестацию нод и workloads — proof of identity через kernel-level или platform-level attestors (k8s_psat, aws_iid, gcp_iit)
  • Единую идентичность — один домен доверия (trust domain) для Kubernetes, VM, bare-metal, multi-cloud
  • Федерацию — обмен trust bundles между кластерами с разными root CA

Архитектура интеграции

Ключевые компоненты:

  1. SPIFFE CSI Driver (csi.spiffe.io) — монтирует SDS-сокет в pod (вместо hostPath)
  2. SPIRE Controller Manager — автоматически создаёт SPIRE registrations через ClusterSPIFFEID CRD
  3. Envoy SDS API — Envoy запрашивает сертификаты через Unix Domain Socket (/run/secrets/workload-spiffe-uds/socket)

Конфигурация Istio для SPIRE

yaml
# Источник: github.com/istio/istio/blob/master/samples/security/spire/istio-spire-config.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  profile: default
  meshConfig:
    trustDomain: example.org
  values:
    pilot:
      env:
        ENABLE_CA_SERVER: "false"   # отключаем встроенный CA
  components:
    ingressGateways:
    - name: istio-ingressgateway
      enabled: true
      label:
        istio: ingressgateway

ClusterSPIFFEID для автоматической регистрации workloads:

yaml
# Источник: istio.io/docs/ops/integrations/spire/
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
  name: istio-workloads
spec:
  spiffeIDTemplate: >-
    spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}
  podSelector:
    matchLabels:
      spiffe.io/spire-managed-identity: "true"

Ограничение: Istio требует SPIFFE ID в формате spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>. Произвольные path в SPIFFE ID не поддерживаются для Istio workloads.

Порядок развёртывания: ClusterSPIFFEID должен быть создан до установки Istio, иначе ingress gateway не получит сертификат и не выйдет в состояние Ready.

Федерация для multi-cluster

SPIRE Agent может передавать federated trust bundles в Envoy через SDS API, позволяя workloads из разных trust domains устанавливать mTLS. AWS предоставляет reference implementation: aws-samples/istio-on-eks — Istio mesh с SPIRE federation между EKS-кластерами.

Подробнее о федерации SPIRE — в Главе 22.

Lab: Istio mTLS на kind

Развернём Istio в ambient mode на kind-кластере и настроим STRICT mTLS с AuthorizationPolicy.

Предварительные требования

Шаг 1: Создание кластера

bash
cat <<EOF | kind create cluster --name istio-lab --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF

Шаг 2: Установка Istio в ambient mode

bash
# Установка CRDs и control plane
istioctl install --set profile=ambient --skip-confirmation

# Проверка компонентов
kubectl get pods -n istio-system
# istiod, ztunnel (DaemonSet), istio-cni

Шаг 3: Развёртывание тестовых сервисов

bash
# Namespace с ambient mesh
kubectl create namespace demo
kubectl label namespace demo istio.io/dataplane-mode=ambient

# Два сервиса: frontend и backend
kubectl apply -n demo -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: frontend
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      serviceAccountName: backend
      containers:
      - name: backend
        image: hashicorp/http-echo:0.2.3
        args: ["-text=hello from backend"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: backend
  ports:
  - port: 80
    targetPort: 5678
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      serviceAccountName: frontend
      containers:
      - name: frontend
        image: curlimages/curl:8.5.0
        command: ["sleep", "infinity"]
EOF

Шаг 4: Проверка mTLS

bash
# Трафик автоматически шифруется ztunnel
kubectl exec -n demo deploy/frontend -- \
  curl -s http://backend.demo.svc.cluster.local

# Проверка через istioctl
istioctl ztunnel-config workloads -n demo

Шаг 5: STRICT PeerAuthentication

bash
kubectl apply -n demo -f - <<EOF
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: strict-mtls
spec:
  mtls:
    mode: STRICT
EOF

Шаг 6: Deny-all + явное разрешение

bash
# Запретить весь трафик в namespace
kubectl apply -n demo -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-all
spec: {}
EOF

# Проверка — запрос должен быть заблокирован
kubectl exec -n demo deploy/frontend -- \
  curl -s -o /dev/null -w "%{http_code}" http://backend.demo.svc.cluster.local
# Ожидаемый ответ: 403

# Разрешить frontend → backend
kubectl apply -n demo -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend-to-backend
spec:
  selector:
    matchLabels:
      app: backend
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - "cluster.local/ns/demo/sa/frontend"
EOF

# Проверка — запрос должен пройти
kubectl exec -n demo deploy/frontend -- \
  curl -s http://backend.demo.svc.cluster.local
# Ожидаемый ответ: hello from backend

Шаг 7: Очистка

bash
kind delete cluster --name istio-lab

Связь с другими главами

ТемаГлаваСвязь
SPIFFE/SPIREГл. 7Идентичность рабочих нагрузок — фундамент mTLS
МикросегментацияГл. 10Network Policies дополняют L7-авторизацию mesh
ZTNAГл. 11North-south (ZTNA) + east-west (mesh) = полное покрытие
KubernetesГл. 14RBAC + mesh policies для defense-in-depth
Политика как кодГл. 19Mesh-политики в GitOps workflow
МультиоблакоГл. 22SPIRE federation + mesh для cross-cluster

Итоги

  • Service mesh реализует Zero Trust для east-west трафика: автоматический mTLS, L7-авторизация, наблюдаемость
  • Istio — наиболее функциональный; ambient mode снижает overhead за счёт per-node ztunnel
  • Linkerd — минимальный overhead, mTLS по умолчанию; ограниченная экосистема, vendor-стабильные релизы
  • Cilium — eBPF для L3/L4 без прокси, но mTLS в beta; часто комбинируется с Istio
  • SPIRE + Istio — замена встроенного CA для гранулярной аттестации и multi-cluster федерации
  • Начинайте с PeerAuthentication: STRICT + deny-all AuthorizationPolicy, добавляйте явные разрешения

Опубликовано под лицензией CC BY-SA 4.0