Взаимная аутентификация сервисов (mTLS)
mTLS между сервисами через SPIRE на базе Cilium.
На этой странице
Введение
Взаимная аутентификация позволяет Cilium проверить идентичность обеих сторон соединения до передачи трафика между подами. Для проверки используется SPIRE (SPIFFE Runtime Environment):
- SPIRE Server управляет корнем доверия и выдаёт удостоверения рабочим нагрузкам;
- SPIRE Agent работает на каждом узле и подтверждает сведения о запущенных на нём подах;
- после аттестации рабочая нагрузка получает X.509-SVID — удостоверение SPIFFE, которое включает сертификат и закрытый ключ;
- агенты Cilium используют SVID, чтобы взаимно подтвердить идентичность источника и получателя трафика.
В платформе «Штурвал» образы spiffe/spire-server и spiffe/spire-agent версии 1.15.3 входят в Модуль управления сетями кластера (shturval-networking, Cilium 1.20.4). Отдельный ShturvalServiceConfig (SSC) для SPIRE не создаётся. Чтобы включить SPIRE, примените ShturvalServicePatch (SSP) к SSC shturval-networking.
Чтобы потребовать взаимную аутентификацию для определённого трафика, задайте authentication.mode: required в правиле CiliumNetworkPolicy или CiliumClusterwideNetworkPolicy. Cilium разрешит соответствующий правилу трафик только после успешной проверки идентичности обеих сторон. Если поле не задано, политика разрешает или блокирует трафик по остальным условиям без взаимной аутентификации.
- В Cilium 1.20 взаимная аутентификация имеет статус beta. Параметры и поведение функции могут измениться в следующих версиях.
- Агенты Cilium выполняют взаимную аутентификацию отдельно от потока данных. Эта функция не шифрует трафик между подами. Для шифрования настройте WireGuard или IPsec.
- Взаимная аутентификация не поддерживается для трафика между кластерами, объединёнными с помощью Cluster Mesh: SPIRE не выдаёт им удостоверения из общего корня доверия.
- Используйте SPIRE, который устанавливает чарт
shturval-networking. Подключить к этой функции собственную реализацию mTLS вместо платформенного SPIRE нельзя.
Взаимная аутентификация сервисов полезна в следующих случаях:
- Проверка идентичности сервисов. Одного совпадения трафика с селекторами и портами сетевой политики недостаточно: перед передачей данных Cilium дополнительно проверяет удостоверения обеих сторон.
- Защита от подключения неподтверждённых рабочих нагрузок. Удостоверение получает под, сведения о котором SPIRE Agent подтвердил по данным узла. Взаимная аутентификация не заменяет RBAC, политики допуска и контроль меток: легитимно созданные поды с одинаковыми метками могут иметь одну Cilium identity.
- Выполнение требований к аутентификации межсервисных соединений. Проверку идентичности можно использовать вместе с шифрованием трафика через WireGuard или IPsec. Эти механизмы настраиваются независимо.
Включение SPIRE
Шаг 1. Применить SSP для включения SPIRE
Создайте SSP со следующими параметрами и загрузите его через импорт манифестов.
apiVersion: ops.shturval.tech/v1beta2
kind: ShturvalServicePatch
metadata:
name: shturval-networking-mtls
spec:
shturvalServiceConfigName: shturval-networking
customvalues:
authentication:
enabled: true
mutual:
spire:
enabled: true
install:
enabled: true
server:
podSecurityContext:
fsGroup: 1000
runAsGroup: 1000
runAsUser: 1000
| Параметр | Описание |
|---|---|
authentication.enabled | Включает подсистему аутентификации Cilium. |
authentication.mutual.spire.enabled | Включает использование SPIRE для взаимной аутентификации Cilium. |
authentication.mutual.spire.install.enabled | Устанавливает SPIRE Server и SPIRE Agent с помощью чарта модуля. |
authentication.mutual.spire.install.server.podSecurityContext.fsGroup | Задаёт идентификатор группы, от имени которой под SPIRE Server получает доступ к подключённым томам. |
authentication.mutual.spire.install.server.podSecurityContext.runAsGroup | Запускает процессы контейнеров SPIRE Server с идентификатором основной группы 1000. |
authentication.mutual.spire.install.server.podSecurityContext.runAsUser | Запускает процессы контейнеров SPIRE Server с идентификатором пользователя 1000. |
По умолчанию SPIRE Server хранит данные на постоянном диске, поэтому они сохраняются после перезапуска пода сервера. Если выделить постоянный диск невозможно, добавьте в этот же SSP параметр authentication.mutual.spire.install.server.dataStorage.enabled: false. В этом режиме данные хранятся только в памяти пода. После его перезапуска SPIRE Server потеряет сохранённое состояние и выдаст удостоверения заново.
Скриншот: импорт SSP для включения SPIRE

Шаг 2. Проверить применение SSP и работу SPIRE
Откройте Сервисы и репозитории → Установленные сервисы, найдите Модуль управления сетями кластера (shturval-networking) и перейдите к управлению сервисом. Убедитесь, что на вкладке Примененные ShturvalServicePatch отображается shturval-networking-mtls, а на вкладке Сервис указан статус Patched.
Скриншоты: применённый SSP


Проверьте, что в неймспейсе cilium-spire запущены под StatefulSet spire-server и поды DaemonSet spire-agent. DaemonSet создаёт по одному агенту на каждом подходящем узле.
kubectl get pods -n cilium-spire
kubectl exec -n cilium-spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server healthcheck
Ответ Server is healthy. подтверждает, что SPIRE Server работает и готов принимать запросы агентов.
Скриншот: проверка подов и состояния SPIRE Server

Проверьте список аттестованных SPIRE Agent:
kubectl exec -n cilium-spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server agent list
В результате должен отображаться отдельный агент для каждого узла, на котором работает DaemonSet spire-agent. Затем проверьте, что в SPIRE зарегистрированы делегированные удостоверения Cilium Agent и Cilium Operator:
kubectl exec -n cilium-spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server entry show -parentID spiffe://spiffe.cilium/ns/cilium-spire/sa/spire-agent
В выводе должны присутствовать SPIFFE ID spiffe://spiffe.cilium/cilium-agent и spiffe://spiffe.cilium/cilium-operator.
SPIFFE ID в сертификате пода имеет вид spiffe://spiffe.cilium/identity/<идентификатор-Cilium>. Число в конце соответствует имени объекта CiliumIdentity, назначенного поду.
Шаг 3. Потребовать взаимную аутентификацию в сетевой политике
Добавьте authentication.mode: required в правило ingress или egress, для которого требуется взаимная аутентификация.
Применяйте взаимную аутентификацию к фактическому межсервисному соединению.
В следующем примере сервис orders обращается к сервису payments в том же неймспейсе. Политика разрешает входящий трафик к подам с меткой app: payments только от подов с меткой app: orders, только на TCP-порт 8080 и только после взаимной аутентификации.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: payments-mutual-auth
namespace: app
spec:
description: "Входящий трафик к payments только от orders и только после взаимной аутентификации"
endpointSelector:
matchLabels:
app: payments
ingress:
- fromEndpoints:
- matchLabels:
app: orders
io.kubernetes.pod.namespace: app
authentication:
mode: required
toPorts:
- ports:
- port: "8080"
protocol: TCP
Наличие секции ingress включает для выбранных подов модель default deny для входящего трафика: Cilium отбрасывает любой поток, который не соответствует разрешающему правилу. Подробнее см. в разделе Поведение по умолчанию.
Пример проверки взаимной аутентификации
HTTP-код ответа не показывает, выполнялась ли взаимная аутентификация: разрешённый запрос успешно завершается как с authentication.mode: required, так и без этой настройки. Результат проверки можно дополнительно просмотреть в Hubble UI.
В примере далее создаются две рабочие нагрузки в неймспейсе mtls-test: под pod-worker обращается к сервису echo. Сначала политика разрешает только запрос GET /headers без взаимной аутентификации. Затем в то же правило добавляется authentication.mode: required.
Перед добавлением authentication.mode: required убедитесь, что SPIRE Server возвращает Server is healthy., как описано в разделе Проверка.
Образы echoserver и netshoot загружаются из публичных реестров. Для проверки в закрытом контуре замените их доступными образами: сервер должен принимать HTTP-запросы на порту 3000, а образ клиента — содержать команду curl.
Шаг 1. Создать тестовые рабочие нагрузки
Создайте неймспейс и поды:
apiVersion: v1
kind: Namespace
metadata:
name: mtls-test
---
apiVersion: v1
kind: Service
metadata:
name: echo
namespace: mtls-test
labels:
app: echo
spec:
selector:
app: echo
ports:
- name: http
port: 3000
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
namespace: mtls-test
labels:
app: echo
spec:
replicas: 1
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Pod
metadata:
name: pod-worker
namespace: mtls-test
labels:
app: pod-worker
spec:
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "infinite"]
Дождитесь Running у обоих подов:
kubectl -n mtls-test get pods
Шаг 2. Проверить удостоверение SPIFFE
Проверьте, что для рабочей нагрузки echo создано удостоверение SPIFFE. Следующие команды предназначены для Bash:
IDENTITY_ID=$(kubectl -n mtls-test get cep -l app=echo -o=jsonpath='{.items[0].status.identity.id}')
echo $IDENTITY_ID
kubectl exec -n cilium-spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server entry show -spiffeID spiffe://spiffe.cilium/identity/$IDENTITY_ID
Команда echo должна вывести числовой идентификатор. В результате entry show должен отображаться SPIFFE ID с тем же числом и селектор cilium:mutual-auth.
Чтобы просмотреть все удостоверения рабочих нагрузок, зарегистрированные Cilium Operator в SPIRE, и сопоставить их с объектами CiliumIdentity, выполните:
kubectl exec -n cilium-spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server entry show -selector cilium:mutual-auth
kubectl get ciliumidentities
Числа в конце SPIFFE ID должны соответствовать значениям в столбце NAME объектов CiliumIdentity.
Шаг 3. Применить политику без взаимной аутентификации
Примените политику без взаимной аутентификации. Она разрешает поду pod-worker отправлять только запрос GET /headers на порт 3000 сервиса echo:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: mutual-auth-echo
namespace: mtls-test
spec:
endpointSelector:
matchLabels:
app: echo
ingress:
- fromEndpoints:
- matchLabels:
app: pod-worker
toPorts:
- ports:
- port: "3000"
protocol: TCP
rules:
http:
- method: GET
path: /headers
Скриншот: импорт политики без взаимной аутентификации

kubectl -n mtls-test exec pod-worker -- curl -s -o /dev/null -w "%{http_code}\n" --max-time 10 http://echo:3000/headers
kubectl -n mtls-test exec pod-worker -- curl -s --max-time 10 http://echo:3000/headers-1
Скриншот: проверка L7-правила политики

Первая команда должна вывести код 200. Второй запрос не соответствует L7-правилу, поэтому Cilium блокирует обращение к пути /headers-1.
Шаг 4. Применить политику с обязательной взаимной аутентификацией
Замените политику вариантом с обязательной взаимной аутентификацией:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: mutual-auth-echo
namespace: mtls-test
spec:
endpointSelector:
matchLabels:
app: echo
ingress:
- fromEndpoints:
- matchLabels:
app: pod-worker
authentication:
mode: required
toPorts:
- ports:
- port: "3000"
protocol: TCP
rules:
http:
- method: GET
path: /headers
Скриншот: состояние политики с обязательной взаимной аутентификацией

Шаг 5. Проверить взаимную аутентификацию
Повторите две команды проверки. Ожидаемый результат не изменится: запрос к /headers вернёт 200, а запрос к /headers-1 будет заблокирован L7-правилом. При первом обращении Cilium может временно отбрасывать пакеты, пока агенты выполняют взаимную аутентификацию. Если первый запрос завершился по тайм-ауту, выполните его повторно.
Откройте Hubble UI и отфильтруйте потоки по неймспейсу mtls-test. Сначала для соединения отобразится событие отбрасывания пакета с причиной Authentication required, затем — событие Authentication со значением Spire. Код ответа 200 вместе с событием Spire подтверждает, что взаимная аутентификация прошла успешно.
Если для Cilium включён уровень журналирования debug, успешную взаимную аутентификацию можно дополнительно проверить по журналам агентов:
kubectl -n kube-system -c cilium-agent logs -l k8s-app=cilium --timestamps=true | grep "Policy is requiring authentication\|Validating Server SNI\|Validated certificate\|Successfully authenticated"
Скриншот: успешная взаимная аутентификация в журналах Cilium

Сообщение Policy is requiring authentication означает, что политика потребовала аутентификацию. Сообщения Validated certificate и Successfully authenticated подтверждают успешную проверку сертификата и завершение взаимной аутентификации. Если журналирование debug выключено, эти сообщения могут отсутствовать; в этом случае проверяйте результат в Hubble UI.
Шаг 6. Удалить тестовые ресурсы
Удалите тестовый неймспейс вместе с созданными в нём подами, сервисом и политикой. SSP shturval-networking-mtls останется в кластере:
kubectl delete namespace mtls-test
Поле authentication поддерживается только в политиках Cilium. В стандартном Kubernetes NetworkPolicy это поле недоступно.