Глава 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 mTLS | Istio ambient, Cilium |
| Waypoint (L7 on demand) | Namespace: waypoint proxy для L7 политик | Добавляется только когда нужен L7 | Istio 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
Плоскость данных — два режима:
Sidecar mode — Envoy-прокси в каждом pod. Полная L7-функциональность: маршрутизация, retry, circuit breaking, L7 авторизация.
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-соединения:
# Источник: 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 принцип "запретить всё, разрешить явно":
# Источник: 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 — это заблокирует весь трафик, кроме явно разрешённого.
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 для авторизации:
# Источник: 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 clientscluster-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:
- Cilium Agent получает SPIFFE ID для workloads через DelegatedIdentity API
- Аутентификация выполняется out-of-band — cilium-agent на одной ноде устанавливает TLS-рукопожатие с cilium-agent на другой ноде
- Шифрование трафика — отдельно через 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
| Характеристика | Istio | Linkerd | Cilium |
|---|---|---|---|
| Data plane | Envoy (sidecar) / ztunnel+waypoint (ambient) | linkerd2-proxy (Rust, sidecar) | eBPF + per-node Envoy |
| mTLS | Automatic, STRICT mode | Automatic, по умолчанию | Beta, WireGuard/ztunnel |
| Сертификаты | Istiod CA, TTL 24h, SPIFFE | Identity controller, SPIFFE (2.15+) | SPIRE DelegatedIdentity |
| L7 авторизация | AuthorizationPolicy | AuthorizationPolicy + Server | CiliumNetworkPolicy 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.0 | Apache 2.0 (edge), vendor stable | Apache 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
Архитектура интеграции
Ключевые компоненты:
- SPIFFE CSI Driver (
csi.spiffe.io) — монтирует SDS-сокет в pod (вместо hostPath) - SPIRE Controller Manager — автоматически создаёт SPIRE registrations через
ClusterSPIFFEIDCRD - Envoy SDS API — Envoy запрашивает сертификаты через Unix Domain Socket (
/run/secrets/workload-spiffe-uds/socket)
Конфигурация Istio для SPIRE
# Источник: 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: ingressgatewayClusterSPIFFEID для автоматической регистрации workloads:
# Источник: 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.
Предварительные требования
- Docker, kind, kubectl, Helm v3
istioctl— istio.io/latest/docs/setup/getting-started/
Шаг 1: Создание кластера
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
# Установка CRDs и control plane
istioctl install --set profile=ambient --skip-confirmation
# Проверка компонентов
kubectl get pods -n istio-system
# istiod, ztunnel (DaemonSet), istio-cniШаг 3: Развёртывание тестовых сервисов
# 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
# Трафик автоматически шифруется 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
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 + явное разрешение
# Запретить весь трафик в 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: Очистка
kind delete cluster --name istio-labСвязь с другими главами
| Тема | Глава | Связь |
|---|---|---|
| SPIFFE/SPIRE | Гл. 7 | Идентичность рабочих нагрузок — фундамент mTLS |
| Микросегментация | Гл. 10 | Network Policies дополняют L7-авторизацию mesh |
| ZTNA | Гл. 11 | North-south (ZTNA) + east-west (mesh) = полное покрытие |
| Kubernetes | Гл. 14 | RBAC + mesh policies для defense-in-depth |
| Политика как код | Гл. 19 | Mesh-политики в GitOps workflow |
| Мультиоблако | Гл. 22 | SPIRE 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-allAuthorizationPolicy, добавляйте явные разрешения