Глава 7. Рабочие нагрузки и SPIFFE/SPIRE
В Главе 5 мы построили фундамент идентичности для людей: IdP, OIDC, фишинго-устойчивая MFA. В Главе 6 — защитили привилегированный доступ через PAM и динамические секреты. Но люди — лишь малая часть участников сети. На каждого пользователя приходятся десятки сервисов, контейнеров, CI/CD-пайплайнов и API-интеграций, каждый из которых тоже нуждается в идентичности. Эта глава — о небиологических идентичностях (NHI) и о том, как SPIFFE/SPIRE решает проблему идентификации рабочих нагрузок в мультиоблачной среде.
Зачем: масштаб проблемы NHI
NIST SP 800-207 (Section 2, Tenet 1) определяет: «Все источники данных и вычислительные сервисы считаются ресурсами». Это значит, что не только пользователи, но и сервисы, контейнеры, функции — все участники обмена данными должны иметь проверяемую идентичность.
Источник: NIST SP 800-207, Section 3
Небиологические идентичности (NHI)
NHI (Non-Human Identities) — это все цифровые идентичности, которые работают без непосредственного участия человека:
- Сервисные учётные записи — для приложений, обращающихся к базам данных и API
- API-ключи и токены — AWS Access Keys, GitHub PAT, OAuth client credentials
- SSH-ключи — для автоматизации и Git-операций
- Цифровые сертификаты — X.509/TLS, mTLS, code signing
- CI/CD-токены — GitHub Actions, GitLab CI, Jenkins credentials
- RPA-боты — учётные данные для робототехнической автоматизации
- AI-агенты — новый класс NHI: агенты, обращающиеся к API, базам данных и сервисам
Источники: Microsoft Security: What Are Non-Human Identities, Okta: What Are Non-Human Identities
Масштаб: NHI vs люди
По данным отраслевых исследований, NHI значительно превышают по количеству человеческие идентичности:
| Источник | Соотношение NHI:Human | Год |
|---|---|---|
| CyberArk State of Machine Identity Security | 80:1 | 2025 |
| CSA / Astrix Security Survey | 20:1 (Fortune 500) | 2024 |
| Entro Security NHI Risk Report | 144:1 | H1 2025 |
Источники: CyberArk: Machine Identities Outnumber Humans by More Than 80 to 1, CSA: State of Non-Human Identity Security, Entro Security H1 2025 Report
Почему NHI — растущий вектор атак
NHI отличаются от человеческих идентичностей по трём критическим параметрам:
1. Отсутствие MFA. Сервисная учётная запись не может пройти второй фактор аутентификации. Если её секрет скомпрометирован — доступ получен немедленно.
2. Долгоживущие секреты. По данным GitGuardian State of Secrets Sprawl 2025, 70% секретов, утечённых в публичные GitHub-репозитории в 2022 году, остаются валидными в 2025. В 2024 году обнаружено 23.77 млн новых захардкоженных секретов в публичных репозиториях (+25% YoY).
Источник: GitGuardian State of Secrets Sprawl 2025
3. Отсутствие lifecycle management. По данным CSA / Oasis Security (2025), 79% ИТ-специалистов считают себя не готовыми к предотвращению атак через NHI. Только 15% организаций уверены в полноте инвентаризации NHI.
Источник: CSA: 79% of IT Pros Feel Ill-Equipped to Prevent NHI Attacks
OWASP NHI Top 10 (2025)
OWASP опубликовал первый Top 10 рисков для NHI в 2025 году. Три из десяти рисков напрямую связаны с управлением секретами:
| # | Риск | Суть |
|---|---|---|
| NHI1 | Improper Offboarding | NHI не деактивируются при выводе из эксплуатации |
| NHI2 | Secret Leakage | Утечка ключей в код, логи, чат-платформы |
| NHI5 | Overprivileged NHI | Избыточные привилегии за пределами функциональных требований |
| NHI7 | Long-Lived Secrets | Статические секреты без ротации |
| NHI10 | Human Use of NHI | Разработчики используют сервисные учётные записи для ручных задач |
Полный список: OWASP NHI Top 10
Решение перечисленных проблем требует принципиально другого подхода к идентификации рабочих нагрузок — криптографической идентичности вместо статических секретов. Именно это предлагает стандарт SPIFFE.
Стандарт SPIFFE
SPIFFE (Secure Production Identity Framework for Everyone) — открытый стандарт, определяющий формат идентичности для рабочих нагрузок и API для её получения. Проект под эгидой CNCF (graduated, September 2022).
Источник: CNCF: SPIFFE and SPIRE Graduate
Спецификации
Стандарт включает следующие ключевые документы, поддерживаемые в GitHub-репозитории SPIFFE:
| Документ | Назначение |
|---|---|
| SPIFFE ID | Формат идентификатора рабочей нагрузки |
| X509-SVID | X.509-сертификат как носитель идентичности |
| JWT-SVID | JWT-токен как носитель идентичности |
| Trust Domain and Bundle | Домен доверия и набор корневых ключей |
| Workload API | gRPC API для получения SVID |
| Workload Endpoint | Как найти агент идентичности на узле |
| SPIFFE Federation | Протокол федерации между доменами доверия |
Спецификации поддерживаются как живые документы в GitHub — без традиционного семантического версионирования.
Источник: SPIFFE Specifications
SPIFFE ID
Формат: spiffe://<trust-domain>/<path>
spiffe://production.example.org/ns/payments/sa/api-server
│ │ │
scheme trust domain path (идентифицирует рабочую нагрузку)- Scheme:
spiffe://— RFC 3986-совместимый URI - Trust domain: authority component; регистронезависимый; максимум 255 байт
- Path: регистрозависимый; уникально идентифицирует рабочую нагрузку внутри домена доверия
- Сегменты пути: только
[a-zA-Z0-9._-], без пустых сегментов,..или завершающего/
Источник: SPIFFE-ID Specification
SVID: два формата
SVID (SPIFFE Verifiable Identity Document) — криптографический документ, подтверждающий идентичность рабочей нагрузки. Стандарт определяет два формата:
| X.509-SVID | JWT-SVID | |
|---|---|---|
| Формат | X.509-сертификат | JSON Web Token |
| SPIFFE ID в | Subject Alternative Name (URI SAN) | Claim sub |
| Обязательные поля | Ровно один URI SAN, Key Usage | sub, aud, exp, iat |
| Применение | mTLS между сервисами | L7-прокси, ситуации с разрывом TLS |
| Преимущества | Нет replay-атак, native mTLS | Прозрачность через L7-балансировщики |
| Ограничения | Требует TLS-соединение | Подвержен replay (aud + exp смягчают) |
Спецификация рекомендует X.509-SVID как основной формат. JWT-SVID — для случаев, когда L7-прокси или балансировщик разрывает TLS-соединение.
Источники: X509-SVID Spec, JWT-SVID Spec
Домен доверия (Trust Domain)
Домен доверия соответствует корню доверия системы. Все идентичности внутри одного домена разделяют общий корень доверия — SPIFFE Bundle (набор корневых CA-сертификатов и/или ключей подписи JWT).
Типичная организация имеет несколько доменов доверия:
spiffe://production.example.org/... — production-среда
spiffe://staging.example.org/... — staging
spiffe://ci.example.org/... — CI/CD-инфраструктураФедерация между доменами позволяет рабочим нагрузкам из разных доменов аутентифицировать друг друга.
Источник: SPIFFE Trust Domain and Bundle
Workload API
Стандартизированный gRPC API, через который рабочие нагрузки получают свои SVID и trust bundles.
Ключевые свойства:
- Доступен через Unix Domain Socket (не TCP) — только локальные процессы на узле
- TLS не требуется на сокете — рабочая нагрузка может не иметь корня доверия до bootstrapping
- Клиент находит сокет через переменную окружения
SPIFFE_ENDPOINT_SOCKET - Рабочая нагрузка не хранит никаких секретов — идентичность доставляется через API
Источник: SPIFFE Workload API
SPIRE: эталонная реализация
SPIRE (SPIFFE Runtime Environment) — эталонная реализация стандарта SPIFFE. Проект CNCF (graduated). Используется в production Bloomberg, ByteDance, GitHub, Netflix, Pinterest, Uber и др.
Источник: CNCF SPIRE Project Page, SPIRE GitHub
Архитектура
SPIRE состоит из двух основных компонентов: Server и Agent.
SPIRE Server — центральный компонент:
| Компонент | Функция |
|---|---|
| Certificate Authority (CA) | Подписывает X.509-SVID и JWT-SVID |
| Data Store | Хранит registration entries и аттестованные узлы. Поддерживает SQLite 3 (default), MySQL, PostgreSQL |
| Registration API | Управление записями о рабочих нагрузках и их селекторами |
| Node Attestor (server) | Валидирует аттестационные данные от агентов |
| Upstream Authority | Плагин для делегирования подписи внешнему PKI (Vault, AWS ACM PCA, другой SPIRE Server) |
SPIRE Agent — запускается на каждом узле (DaemonSet в Kubernetes):
| Компонент | Функция |
|---|---|
| Workload API | gRPC API на Unix Domain Socket для локальных рабочих нагрузок |
| Node Attestor (agent) | Предоставляет аттестационные данные серверу для подтверждения идентичности узла |
| Workload Attestor | Идентифицирует рабочую нагрузку по PID (k8s, unix, docker плагины) |
| SVID Cache | Кеширует SVID и trust bundles, обновляет до истечения срока |
Источник: SPIRE Concepts
Аттестация узлов (Node Attestation)
Node Attestation — механизм, которым SPIRE Agent доказывает свою идентичность SPIRE Server при первом подключении. Каждый метод использует платформенно-специфичные доказательства:
| Плагин | Платформа | Как работает |
|---|---|---|
| k8s_psat | Kubernetes | Agent предъявляет Projected Service Account Token; Server валидирует через TokenReview API |
| aws_iid | AWS EC2 | Agent получает Instance Identity Document (подписан AWS); Server проверяет подпись |
| gcp_iit | GCP GCE | Agent получает Instance Identity Token; Server валидирует |
| azure_msi | Azure VM | Agent получает Managed Service Identity token; Server валидирует и извлекает Tenant/Principal ID |
| join_token | Любая | Одноразовый токен, созданный на сервере. Простой, но не автоматизированный |
| x509pop | Любая (PKI) | Agent доказывает владение X.509-сертификатом, выданным вне SPIRE |
Плагин k8s_sat (legacy Service Account Tokens) удалён в SPIRE 1.12.0 — используйте только k8s_psat. Полный список built-in плагинов включает также azure_imds, sshpop, tpm_devid и др. — см. документацию SPIRE.
Источники: k8s_psat Plugin, Configuring SPIRE
Аттестация рабочих нагрузок (Workload Attestation)
Когда рабочая нагрузка обращается к Workload API, SPIRE Agent идентифицирует её по PID процесса, затем преобразует PID в набор селекторов через плагины workload attestor.
Kubernetes Workload Attestor — наиболее распространённый в контейнерных средах:
| Селектор | Пример | Описание |
|---|---|---|
k8s:ns | k8s:ns:production | Namespace |
k8s:sa | k8s:sa:payments-api | Service Account |
k8s:pod-label | k8s:pod-label:app:payments | Pod label (key:value) |
k8s:container-name | k8s:container-name:api | Имя контейнера |
k8s:container-image | k8s:container-image:myregistry/app:v1 | Образ конкретного контейнера |
Важное различие: k8s:container-image сопоставляется только с контейнером, обратившимся к SPIRE, тогда как k8s:pod-image — с любым контейнером в поде.
Unix Workload Attestor — для процессов вне контейнеров:
| Селектор | Пример | Описание |
|---|---|---|
unix:uid | unix:uid:1000 | User ID |
unix:user | unix:user:nginx | Username |
unix:path | unix:path:/usr/bin/nginx | Путь к бинарнику |
unix:sha256 | unix:sha256:3a6eb079... | SHA256 бинарника |
Источники: k8s Workload Attestor, Unix Workload Attestor
Как рабочие нагрузки получают SVID
SPIRE предоставляет несколько способов доставки SVID в рабочую нагрузку:
| Метод | Описание | Модификация приложения |
|---|---|---|
| SPIFFE CSI Driver | Bind mount Unix-сокета через CSI volume | Только volume mount |
| spiffe-helper | Sidecar, записывающий сертификаты на диск | Нет (sidecar пишет файлы) |
| Envoy SDS | SPIRE Agent нативно поддерживает Envoy Secret Discovery Service | Конфигурация Envoy |
| go-spiffe SDK | Программный доступ через библиотеку | Изменение кода (Go) |
SPIFFE CSI Driver (рекомендуемый для Kubernetes) — монтирует Unix Domain Socket Workload API в контейнер рабочей нагрузки. Устраняет необходимость в hostPath volumes в pod-спецификациях рабочих нагрузок (hostPath часто запрещён политиками безопасности). Сам CSI Driver использует hostPath для связи с SPIRE Agent, но это ограничено инфраструктурным компонентом.
# Pod с доступом к Workload API через CSI Driver
apiVersion: v1
kind: Pod
metadata:
name: my-workload
spec:
serviceAccountName: payments-api
containers:
- name: app
image: myregistry/payments-api:v1
volumeMounts:
- name: spiffe-workload-api
mountPath: /spiffe-workload-api
readOnly: true
env:
- name: SPIFFE_ENDPOINT_SOCKET
value: unix:///spiffe-workload-api/spire-agent.sock
volumes:
- name: spiffe-workload-api
csi:
driver: "csi.spiffe.io"
readOnly: trueИсточники: SPIFFE CSI Driver, Working with SVIDs
Идентичность рабочих нагрузок в облаках
Каждый облачный провайдер предлагает собственный механизм идентификации рабочих нагрузок. Все три сходятся к OIDC-федерации, но различаются в деталях.
AWS: IAM Roles и Roles Anywhere
IAM Roles for Service Accounts (IRSA) — для EKS:
- EKS-кластер предоставляет OIDC-провайдер
- Kubernetes ServiceAccount аннотируется ARN IAM-роли
- Pod получает projected service account token и обменивает его через STS на временные AWS-учётные данные
EKS Pod Identity (рекомендуется для новых EKS-кластеров):
- AWS-managed контроллер и агент
- Прямой маппинг pod → IAM role без обновления trust policy
- Только для EKS (не для self-managed Kubernetes)
IAM Roles Anywhere — для рабочих нагрузок вне AWS:
- Регистрация CA как trust anchor
- Рабочая нагрузка предъявляет X.509-сертификат от этого CA
- Вызывает
CreateSessionAPI, подписанный приватным ключом сертификата - Получает временные STS-учётные данные
Источники: AWS IRSA, IAM Roles Anywhere
GCP: Workload Identity Federation
Workload Identity Federation — позволяет внешним рабочим нагрузкам получать доступ к GCP API без статических service account keys:
- Создать Workload Identity Pool и Provider (OIDC или SAML 2.0)
- Настроить attribute mapping (внешние claims → GCP-атрибуты)
- Внешняя рабочая нагрузка обменивает свой токен через GCP Security Token Service (STS)
- STS возвращает краткосрочный OAuth 2.0 access token
Workload Identity Federation for GKE — упрощённая версия для GKE-подов, получающих GCP-идентичность через projected service account tokens.
Источники: GCP Workload Identity Federation, GKE Workload Identity
Azure: Managed Identity и Workload Identity
Managed Identity — для Azure-ресурсов:
- System-assigned: привязана к жизненному циклу ресурса
- User-assigned: независимый жизненный цикл, может использоваться несколькими ресурсами
- Azure управляет получением, ротацией и отзывом токенов автоматически
Workload Identity Federation (Microsoft Entra) — для рабочих нагрузок вне Azure:
- Federated Identity Credentials создают доверие между Entra ID и внешними OIDC-провайдерами
- Поддержка GitHub Actions, Kubernetes, других облаков
- Ожидаемый audience:
api://AzureADTokenExchange
Источники: Azure Managed Identities, Entra Workload Identity Federation
Сравнение: SPIFFE vs облачные механизмы
| Аспект | SPIFFE/SPIRE | AWS IRSA / Pod Identity | GCP WIF | Azure Managed ID / WIF |
|---|---|---|---|---|
| Область | Мультиоблако, on-prem | Только AWS | Только GCP | Только Azure |
| Формат идентичности | X.509-SVID + JWT-SVID | JWT (STS) | JWT (OAuth 2.0) | JWT (Entra token) |
| mTLS | Нативный (X.509-SVID) | Нет | Нет | Нет |
| Не-K8s нагрузки | VM, bare metal, IoT | EC2 Instance Profile | VM + WIF Pools | VM + Managed Identity |
| Открытый стандарт | Да (CNCF graduated) | Проприетарный | Проприетарный | Проприетарный |
| Кросс-облако | Federation API | Требует внешний OIDC | Поддержка внешних OIDC | Поддержка внешних OIDC |
Ключевой вывод: облачные провайдеры сходятся к OIDC для федерации идентичностей. SPIRE включает OIDC Discovery Provider, который транслирует SPIFFE-идентичности в облачные системы — обеспечивая единый слой идентичности для всех платформ.
Источник: SPIRE OIDC Discovery Provider
Федерация между доменами доверия
Федерация позволяет рабочим нагрузкам в разных доменах доверия аутентифицировать друг друга. Каждый домен публикует свой SPIFFE Bundle через bundle endpoint.
Протокол федерации
Два профиля аутентификации bundle endpoint:
| Профиль | Аутентификация | Применение |
|---|---|---|
https_web | Стандартный WebPKI (CA-signed certificate) | Простая настройка |
https_spiffe | SPIFFE-аутентификация, требует endpoint_spiffe_id | Более строгая безопасность |
Конфигурация федерации в SPIRE Server
# server.conf — SPIRE Server domain-a.example
server {
trust_domain = "domain-a.example"
# ...
}
federates_with "domain-b.example" {
bundle_endpoint_url = "https://spire-server-b.example:8443"
bundle_endpoint_profile "https_spiffe" {
endpoint_spiffe_id = "spiffe://domain-b.example/spire/server"
}
}При обмене trust bundles приватные ключи не передаются — только публичные корневые сертификаты CA. Это фундаментальное отличие от VPN или shared secrets.
Источники: SPIFFE Federation Spec, Deploying Federated SPIRE
Lab: SPIRE на Kubernetes
В этой лабораторной работе мы развернём SPIRE на локальном Kubernetes-кластере (kind), зарегистрируем рабочую нагрузку и получим для неё X.509-SVID.
Предварительные требования
Шаг 1: Создание кластера
# Создаём kind-кластер с двумя worker-нодами
cat <<EOF | kind create cluster --name spire-lab --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOFШаг 2: Установка SPIRE через Helm
# Установка CRD (обязательно первым шагом)
helm upgrade --install --create-namespace -n spire-system \
spire-crds spire-crds \
--repo https://spiffe.github.io/helm-charts-hardened/
# Установка SPIRE stack
helm upgrade --install -n spire-system spire spire \
--repo https://spiffe.github.io/helm-charts-hardened/ \
--set global.spire.clusterName=spire-lab \
--set global.spire.trustDomain=lab.example.org \
--waitHelm chart развёртывает: SPIRE Server (StatefulSet), SPIRE Agent (DaemonSet), SPIFFE CSI Driver, Controller Manager, OIDC Discovery Provider.
Источник: SPIRE Helm Charts Installation
Шаг 3: Проверка компонентов
# Все поды SPIRE должны быть Running
kubectl -n spire-system get pods
# Ожидаемый вывод:
# spire-server-0 1/1 Running
# spire-agent-xxxxx 1/1 Running (на каждой ноде)
# spire-spiffe-csi-driver-xxxxx 2/2 Running
# spire-spiffe-oidc-discovery-provider- 1/1 Running
# spire-controller-manager-xxxxx 1/1 Running
# Проверка здоровья SPIRE Server
kubectl exec -n spire-system spire-server-0 -- \
/opt/spire/bin/spire-server healthcheckШаг 4: Автоматическая регистрация через ClusterSPIFFEID
SPIRE Controller Manager позволяет автоматически регистрировать рабочие нагрузки через CRD:
# clusterspiffeid-default.yaml
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
name: lab-workloads
spec:
spiffeIDTemplate: >-
spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}
namespaceSelector:
matchLabels:
spire-enabled: "true"# Применяем CRD
kubectl apply -f clusterspiffeid-default.yaml
# Помечаем namespace для автоматической регистрации
kubectl create namespace workloads
kubectl label namespace workloads spire-enabled=trueИсточник: ClusterSPIFFEID CRD
Шаг 5: Развёртывание тестовой рабочей нагрузки
# test-workload.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: test-workload
namespace: workloads
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-workload
namespace: workloads
spec:
replicas: 1
selector:
matchLabels:
app: test-workload
template:
metadata:
labels:
app: test-workload
spec:
serviceAccountName: test-workload
containers:
- name: client
image: ghcr.io/spiffe/spire-agent:latest
command: ["sleep", "infinity"]
volumeMounts:
- name: spiffe-workload-api
mountPath: /spiffe-workload-api
readOnly: true
env:
- name: SPIFFE_ENDPOINT_SOCKET
value: unix:///spiffe-workload-api/spire-agent.sock
volumes:
- name: spiffe-workload-api
csi:
driver: "csi.spiffe.io"
readOnly: truekubectl apply -f test-workload.yaml
kubectl -n workloads wait --for=condition=ready pod -l app=test-workload --timeout=60sШаг 6: Получение и проверка SVID
# Получение X.509-SVID
kubectl exec -n workloads \
$(kubectl get pod -n workloads -l app=test-workload \
-o jsonpath='{.items[0].metadata.name}') \
-c client -- \
/opt/spire/bin/spire-agent api fetch x509 \
-socketPath /spiffe-workload-api/spire-agent.sock
# Ожидаемый вывод:
# Received 1 svid after X.XXXs
#
# SPIFFE ID: spiffe://lab.example.org/ns/workloads/sa/test-workload
# SVID Valid After: 2026-02-10 12:00:00 +0000 UTC
# SVID Valid Until: 2026-02-10 13:00:00 +0000 UTC ← TTL = 1 час
# Просмотр registration entries
kubectl exec -n spire-system spire-server-0 -- \
/opt/spire/bin/spire-server entry showШаг 7: Очистка
kind delete cluster --name spire-labЧто мы получили
- Автоматическая идентификация — рабочая нагрузка получила SPIFFE ID
spiffe://lab.example.org/ns/workloads/sa/test-workloadбез хранения секретов - Краткосрочные сертификаты — X.509-SVID с TTL 1 час, автоматическая ротация
- Аттестация — SPIRE убедился, что рабочая нагрузка запущена в правильном namespace с правильным ServiceAccount
- CSI Driver — безопасная доставка Workload API без hostPath volumes
В Главе 12 мы покажем, как SPIRE интегрируется с Istio для автоматического mTLS между сервисами. В Главе 22 — как федерация SPIRE объединяет идентичности AWS EKS и GCP GKE.
Итоги
| Проблема NHI | Решение SPIFFE/SPIRE |
|---|---|
| Статические секреты | Криптографическая идентичность (X.509/JWT SVID) — нет паролей и API-ключей |
| Долгоживущие credentials | Краткосрочные SVID (SPIRE defaults: 1 час X.509, 5 минут JWT; настраивается) |
| Нет ротации | Автоматическое обновление через Workload API до истечения срока |
| Нет аттестации | Идентичность подтверждается свойствами платформы (K8s SA, AWS Instance, PID), а не shared secret |
| Vendor lock-in | Открытый стандарт CNCF, федерация между облаками через bundle endpoints |
| Отсутствие governance | Стандартизированный формат SPIFFE ID: spiffe://trust-domain/path — аудируемый и единообразный |