Istio + SPIRE 통합 실전 가이드: SVID 자동 발급부터 멀티 클러스터 페더레이션까지
이 글은 3부작 시리즈의 1편입니다.
- 1편 (현재): Istio + SPIRE 통합 및 멀티 클러스터 페더레이션
- 2편 예정: JWT-SVID와 AWS IAM OIDC 연동으로 서비스 계정 키 없애기
- 3편 예정: Istio Ambient Mode + SPIRE로 사이드카 없는 Zero Trust 구현
SPIFFE/SPIRE는 "이 워크로드가 실제로 내가 기대하는 환경에서 실행되고 있는가"라는 질문에 암호학적으로 답합니다. 워크로드가 어떤 노드에서, 어떤 서비스어카운트로, 어떤 이미지로 실행되는지를 다중 요소로 증명한 뒤 SVID(암호화 신원 문서)를 발급하고, Envoy 사이드카가 이를 자동으로 수령해 mTLS를 수립합니다.
Kubernetes의 기본 서비스어카운트 토큰이나 네임스페이스 기반 mTLS만으로는 이 증명이 불완전합니다. 서비스어카운트 토큰이 탈취되면 신원 자체가 위조될 수 있고, 멀티 클러스터 환경에서는 서로 다른 PKI 체계 간 신뢰 연결이 더욱 복잡해집니다. 금융·헬스케어처럼 NIST SP 800-207(모든 워크로드에 명시적 신원 검증을 요구하는 Zero Trust 원칙) 준수가 필요한 환경, 그리고 AWS·GCP·Azure를 동시에 사용하는 멀티 클라우드 환경에서 SPIFFE/SPIRE는 벤더 중립 표준으로 빠르게 자리잡고 있습니다.
이 글에서는 Istio와 SPIRE를 실제로 통합하는 전체 과정, 그리고 멀티 클러스터 환경에서 서로 다른 트러스트 도메인 간 페더레이션을 설정하는 방법을 단계별로 살펴봅니다.
핵심 개념
SPIFFE, SPIRE, SVID — 세 가지 핵심 용어와 두 가지 신원 형태
SPIFFE (Secure Production Identity Framework For Everyone): 클라우드 네이티브 환경의 워크로드에 표준화된 암호화 신원을 부여하는 오픈 명세입니다. SPIRE는 CNCF 졸업 프로젝트로 이 명세를 구현한 런타임이며, **SVID (SPIFFE Verifiable Identity Document)**가 그 결과물입니다.
SVID는 두 가지 형태로 발급됩니다.
| 타입 | 형태 | 주요 사용 시나리오 |
|---|---|---|
| X.509 SVID | X.509 인증서 | Envoy mTLS — 서비스 간 암호화 통신 |
| JWT-SVID | JWT 토큰 | AWS IAM, GCP Workload Identity 등 OIDC 연동 |
X.509 SVID는 인증서의 SAN(Subject Alternative Name) 필드에 SPIFFE URI가 포함됩니다. 발급된 SVID를 직접 확인하려면 다음 명령어를 사용할 수 있습니다.
# SVID 인증서의 SAN 필드를 직접 확인
openssl x509 -in svid.pem -text -noout | grep -A2 "Subject Alternative Name"
# 출력 예시:
# X509v3 Subject Alternative Name:
# URI:spiffe://cluster-a.example.com/ns/prod/sa/payment-serviceSPIFFE URI는 spiffe://<trust-domain>/<path> 형태를 따릅니다. 예를 들어 spiffe://cluster-a.example.com/ns/prod/sa/payment-service는 "cluster-a.example.com 트러스트 도메인 내 prod 네임스페이스의 payment-service 서비스어카운트"를 의미하며, 이 신원이 mTLS 핸드셰이크에서 검증됩니다.
Istio + SPIRE 통합 아키텍처
Istio의 기본 SDS(Secret Discovery Service) 구현을 SPIRE Agent로 교체하는 방식입니다. Envoy 사이드카는 UNIX Domain Socket을 통해 SPIRE Agent의 Workload API에 접근해 SVID를 수령하고, TTL의 50% 시점에 자동으로 갱신합니다.
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Pod │
│ ┌──────────────┐ SDS/UNIX Socket ┌──────────────────┐ │
│ │ Envoy Proxy │◄──────────────── │ SPIRE Agent │ │
│ │ (Sidecar) │ SVID X.509 cert │ (DaemonSet) │ │
│ └──────────────┘ └──────┬───────────┘ │
└─────────────────────────────────────────────│───────────────┘
│ gRPC (Node Attestation
│ + SVID 서명 요청)
┌──────────▼──────────┐
│ SPIRE Server │
│ (StatefulSet) │
│ ClusterSPIFFEID │
│ (Controller Mgr) │
└─────────────────────┘SPIRE Agent는 노드마다 하나씩 실행되는 DaemonSet으로, 워크로드의 실행 환경(노드, 서비스어카운트, 네임스페이스)을 직접 확인(Attestation)합니다. SPIRE Agent와 SPIRE Server는 gRPC로 연결되어 노드 인증 및 SVID 서명을 처리합니다. 단순히 "이 Pod의 서비스어카운트 토큰이 유효한가"가 아니라, "이 Pod가 실제로 이 노드에서 이 조건으로 실행 중인가"를 증명하는 것이 핵심 차이입니다.
멀티 클러스터 트러스트 도메인 페더레이션
각 클러스터를 독립된 트러스트 도메인으로 운영하면서, SPIRE Server 간 Bundle Endpoint를 통해 상호 루트 CA를 교환합니다.
cluster-a cluster-b
SPIRE Server ──── Bundle Exchange ──── SPIRE Server
(port 8443) (루트 CA 자동 교환) (port 8443)
│ │
▼ ▼
Workload A Workload B
spiffe://cluster-a.../sa/frontend spiffe://cluster-b.../sa/payment
└──────── mTLS 수립 가능 ──────────┘실전 적용
실전 구성: 단일 클러스터 — Istio + SPIRE 기본 통합
1단계: SPIRE 전체 스택을 Helm으로 설치합니다.
helm install spire spire/spire \
--namespace spire-system \
--set global.trustDomain="cluster.local" \
--set spire-controller-manager.enabled=true \
--set spiffe-csi-driver.enabled=true| 옵션 | 역할 |
|---|---|
global.trustDomain |
Istio의 trust domain과 반드시 일치시켜야 합니다 |
spire-controller-manager.enabled |
ClusterSPIFFEID CRD 처리기 활성화 |
spiffe-csi-driver.enabled |
보안 소켓 마운트 방식 활성화 (hostMount 대신 권장) |
설치 확인:
kubectl get pods -n spire-system
# 기대 출력: spire-server, spire-agent(DaemonSet), spiffe-csi-driver 모두 Running2단계: Istio 설치 시 SPIRE SDS 소켓을 지정합니다.
# istio-operator.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
defaultConfig:
proxyMetadata:
# Envoy가 SPIRE Agent 소켓을 SDS 소스로 인식하는 핵심 설정
SPIFFE_ENDPOINT_SOCKET: "unix:///run/secrets/workload-spiffe-credentials/socket"
PROXY_CONFIG_XDS_AGENT: "true"
values:
global:
pilotCertProvider: "spire" # Istiod가 SPIRE를 인증서 제공자로 인식
sds:
token:
aud: istio-ca
components:
pilot:
k8s:
env:
- name: ENABLE_CA_SERVER
value: "false"ENABLE_CA_SERVER: "false": Istiod 자체 CA 서버를 비활성화해 SVID 발급을 SPIRE에 완전히 위임합니다.
SPIFFE_ENDPOINT_SOCKET은 Envoy가 실제로 SPIRE Agent 소켓을 찾을 경로를 알려주는 핵심 설정으로, 이 값이 없으면 Envoy가 SVID를 수령하지 못합니다.
3단계: 사이드카 인젝션 템플릿에 SPIFFE CSI 볼륨 마운트를 추가합니다.
# ConfigMap: istio-sidecar-injector
spec:
containers:
- name: istio-proxy
env:
- name: SPIFFE_ENDPOINT_SOCKET
value: "unix:///run/secrets/workload-spiffe-credentials/socket"
volumeMounts:
- name: workload-socket
mountPath: /run/secrets/workload-spiffe-credentials
volumes:
- name: workload-socket
csi:
driver: "csi.spiffe.io"
readOnly: true
SPIFFE_ENDPOINT_SOCKET환경 변수가 없으면 Envoy가 소켓 경로를 찾지 못해 SVID를 수령할 수 없습니다. 마운트 경로와 환경 변수의 디렉토리 부분이 일치해야 합니다.
4단계: ClusterSPIFFEID로 워크로드를 자동 등록합니다.
2023년 이후 기존의 k8s-workload-registrar는 deprecated되었고, SPIRE Controller Manager와 ClusterSPIFFEID CRD 방식이 권장 표준이 되었습니다. label selector와 federatesWith 필드를 선언하면 워크로드 등록과 페더레이션 관계가 자동으로 처리됩니다.
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
name: istio-workloads
spec:
spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
podSelector:
matchLabels:
security.istio.io/tlsMode: "istio"
workloadSelectorTemplates:
- "k8s:ns:{{ .PodMeta.Namespace }}"
- "k8s:sa:{{ .PodSpec.ServiceAccountName }}"| 필드 | 설명 |
|---|---|
spiffeIDTemplate |
Go 템플릿 문법으로 SPIFFE URI를 동적 생성 |
podSelector |
Istio 사이드카 인젝션이 활성화된 Pod만 대상 |
workloadSelectorTemplates |
Attestation 시 Pod 소속 네임스페이스·서비스어카운트 검증 |
워크로드 자동 등록 확인:
# SPIRE Server Pod 이름 조회 후 등록된 entry 확인
SPIRE_POD=$(kubectl get pod -n spire-system -l app=spire-server -o name | head -1)
kubectl exec -n spire-system $SPIRE_POD -- \
/opt/spire/bin/spire-server entry show
# 출력: spiffe://cluster.local/ns/prod/sa/payment-service 등 자동 등록 확인멀티 클러스터 구성: 트러스트 도메인 페더레이션
클러스터 A의 SPIRE Server 설정 (HCL 형식)
SPIRE Server는 HCL(HashiCorp Configuration Language) 형식의 네이티브 설정 파일을 사용합니다. Kubernetes YAML에 익숙하다면 중괄호 블록 기반 구조로 이해하면 됩니다.
# spire-server.conf (cluster-a)
# 단순화된 예시 — 운영 환경에서는 datastore, key_manager, notifier 블록 추가 필요
server {
trust_domain = "cluster-a.example.com"
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "cluster-b.example.com" {
bundle_endpoint_url = "https://spire-server.cluster-b.svc:8443"
bundle_endpoint_profile "https_spiffe" {
endpoint_spiffe_id = "spiffe://cluster-b.example.com/spire/server"
}
}
}
}페더레이션 상태 확인:
SPIRE_POD=$(kubectl get pod -n spire-system -l app=spire-server -o name | head -1)
kubectl exec -n spire-system $SPIRE_POD -- \
/opt/spire/bin/spire-server bundle list
# 출력: cluster-b.example.com 번들이 정상 교환되었는지 확인클러스터 A에서 ClusterFederatedTrustDomain 리소스로 페더레이션을 선언합니다.
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterFederatedTrustDomain
metadata:
name: cluster-b-federation
spec:
trustDomain: "cluster-b.example.com"
bundleEndpointURL: "https://spire-server.cluster-b.svc:8443"
bundleEndpointProfile:
type: HTTPSSPIFFEAuthentication
endpointSPIFFEID: "spiffe://cluster-b.example.com/spire/server"페더레이션 대상을 포함한 워크로드 등록
apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
name: cross-cluster-workload
spec:
spiffeIDTemplate: "spiffe://cluster-a.example.com/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
federatesWith:
- "cluster-b.example.com"Istio AuthorizationPolicy로 상대 클러스터의 워크로드를 인가합니다.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-cluster-b-workloads
spec:
rules:
- from:
- source:
principals:
- "spiffe://cluster-b.example.com/ns/prod/sa/payment-service"페더레이션의 핵심 원리: 양쪽 클러스터의 SPIRE Server가 서로의 Bundle Endpoint에서 루트 CA를 주기적으로 가져옵니다. 한쪽 클러스터의 루트 CA가 교체되어도 자동으로 반영되므로, 수동으로 인증서를 배포할 필요가 없습니다.
대규모 운영 사례: Indeed의 Nested SPIRE 토폴로지
대규모 멀티 클러스터에서 단일 SPIRE Server에 모든 워크로드를 등록하면 가용성과 확장성에 한계가 생깁니다. Indeed는 Nested SPIRE 토폴로지를 채택하여 이 문제를 해결했습니다.
Root SPIRE Server (Global)
│ X.509 발급 (Intermediate CA)
├── Intermediate SPIRE Server (cluster-a)
│ └── Workload SVIDs (X.509 + JWT)
│
└── Intermediate SPIRE Server (cluster-b)
└── Workload SVIDs (X.509 + JWT)Intermediate SPIRE Server는 Root SPIRE Server를 UpstreamAuthority로 지정합니다.
# intermediate-spire-server.conf (cluster-a)
# 단순화된 예시 — 운영 환경에서는 datastore, key_manager 블록 추가 필요
UpstreamAuthority "spire" {
plugin_data {
server_address = "root-spire-server.spire-root.svc"
server_port = 8081
workload_api_socket_path = "/tmp/spire-agent/public/api.sock"
}
}| 레이어 | 역할 |
|---|---|
| Root SPIRE Server | 각 클러스터 중간 서버에 X.509 인증서 발급, 전사 신뢰 앵커 역할 |
| Intermediate SPIRE Server | 클러스터 내 워크로드 Attestation 및 SVID 서명 |
| Workload SVID | Istio mTLS용 X.509 SVID, AWS IAM·Confluent 연동용 JWT-SVID 동시 발급 |
JWT-SVID → OIDC 토큰 변환 패턴: SPIRE가 발급한 JWT-SVID를 AWS IAM이나 GCP Workload Identity Federation의 OIDC 토큰으로 변환하면, 서비스 계정 키 없이 클라우드 리소스에 접근할 수 있습니다. Indeed가 실제로 사용 중인 이 패턴은 X.509 SVID와 JWT-SVID의 역할 분리에서 출발합니다 — X.509는 mTLS, JWT는 외부 서비스 인증을 각각 담당합니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 세밀한 Attestation | 노드, 이미지 서명, 프로세스 목록 등 다중 요소를 결합해 기본 Istio보다 훨씬 세분화된 신원 증명이 가능합니다 |
| 자동 SVID 갱신 | TTL 50% 시점에 Envoy가 무중단으로 자동 갱신하며, 운영자 개입이 불필요합니다 |
| 규정 준수 | 보통 1시간 이하의 단기 수명 인증서로 유출 영향이 최소화되어 NIST SP 800-207 준수에 유리합니다 |
| 멀티 클라우드 표준 | AWS, GCP, Azure, 온프레미스를 단일 벤더 중립 표준으로 통합할 수 있습니다 |
| 강화된 워크로드 신원 | 서비스어카운트 토큰이 탈취되더라도 실행 환경 전체를 증명하지 못하면 신원이 발급되지 않습니다 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 사전 등록 필수 | SPIRE에 등록되지 않은 워크로드는 READY 상태가 되지 않아 배포 순서 관리가 필요합니다 | ClusterSPIFFEID CRD로 자동 등록을 구성하면 됩니다 |
| SPIRE Agent 단일 장애점 | DaemonSet인 SPIRE Agent가 비정상이면 해당 노드의 모든 워크로드가 신원을 받지 못합니다 | Agent의 PodDisruptionBudget과 liveness probe를 설정하는 것을 권장합니다 |
| trust domain 불일치 | Istio와 SPIRE의 trust domain이 다르면 인증·인가 오류가 발생합니다 | 설치 시 global.trustDomain을 Istio 설정과 반드시 맞춰야 합니다 |
| 운영 복잡도 증가 | 멀티 클러스터 페더레이션은 번들 엔드포인트 관리, TLS 연결 유지 등 추가 운영 부담이 있습니다 | ClusterFederatedTrustDomain CRD로 선언적 관리가 가능합니다 |
| HostMount 보안 위험 | SPIRE Agent 소켓을 hostMount로 공유하면 노드 전체 권한 위험이 있습니다 | SPIFFE CSI Driver 사용을 권장합니다 |
| 초기 학습 곡선 | CSI Driver, Controller Manager, Server/Agent 전체 스택 구성이 필요합니다 | Helm Chart spire/spire 하나로 전체 스택을 설치할 수 있으며, SPIRE 공식 문서와 Istio 통합 가이드를 단계별로 따라가면서 숙달도를 높여가는 것을 권장합니다 |
SDS (Secret Discovery Service): Envoy가 인증서와 키를 동적으로 가져오는 API입니다. Istio는 기본적으로 자체 SDS 구현을 사용하지만, SPIRE 통합 시 이 역할을 SPIRE Agent가 대신합니다. Envoy는
SPIFFE_ENDPOINT_SOCKET으로 지정된 소켓 경로만 알면 발급 주체가 바뀌어도 동일하게 동작합니다.
실무에서 가장 흔한 실수
-
Istio와 SPIRE의 trust domain을 다르게 설정하는 경우 — Helm 설치 시
global.trustDomain을 Istio의meshConfig.trustDomain과 동일하게 맞춰야 합니다. 다를 경우 SVID는 발급되지만 Istio가 이를 신뢰하지 않아 mTLS 연결이 실패합니다. -
SPIFFE_ENDPOINT_SOCKET환경 변수 없이 볼륨만 마운트하는 경우 — 소켓 경로를 마운트해도 Envoy가 해당 경로를 SDS 소스로 인식하려면SPIFFE_ENDPOINT_SOCKET환경 변수가 함께 설정되어 있어야 합니다. 볼륨 마운트만 있고 환경 변수가 없으면 Envoy는 기본 Istio CA를 계속 사용합니다. -
멀티 클러스터 페더레이션에서 번들 엔드포인트 방화벽을 열지 않는 경우 — SPIRE Server의 8443 포트가 상대 클러스터에서 접근 가능해야 합니다. NetworkPolicy와 인그레스 규칙을 미리 확인하지 않으면 번들 교환이 자동으로 실패합니다.
마치며
Istio + SPIRE 통합은 "서비스어카운트 토큰 탈취만으로는 신원을 위조할 수 없다"는 원칙을 실제로 구현합니다. 워크로드의 실행 환경 전체를 암호학적으로 증명하는 이 구조가 Zero Trust 아키텍처의 핵심 기반이 됩니다.
지금 바로 시작해볼 수 있는 3단계:
-
단일 클러스터 실습 환경 구성 —
kind또는minikube로 로컬 클러스터를 만든 뒤 아래 명령으로 SPIRE 전체 스택을 설치해볼 수 있습니다.bashhelm install spire spire/spire \ --set global.trustDomain="cluster.local" \ --set spire-controller-manager.enabled=true \ --set spiffe-csi-driver.enabled=true \ --namespace spire-system --create-namespace -
ClusterSPIFFEID CRD 적용 확인 — 샘플 워크로드를 배포한 뒤 아래 명령으로 워크로드가 자동 등록되었는지 확인해볼 수 있습니다.
bashSPIRE_POD=$(kubectl get pod -n spire-system -l app=spire-server -o name | head -1) kubectl exec -n spire-system $SPIRE_POD -- \ /opt/spire/bin/spire-server entry show -
멀티 클러스터 페더레이션 실습 — AWS Samples의
istio-on-eks레포지토리에 EKS 기반 SPIRE 페더레이션 참조 아키텍처가 제공되어 있어, 실제 멀티 클러스터 페더레이션을 단계별로 따라해볼 수 있습니다.
다음 글: SPIRE의 JWT-SVID를 AWS IAM OIDC와 연동하여 서비스 계정 키 없이 S3, DynamoDB에 접근하는 Workload Identity 패턴 — "Goodbye Service API Keys"를 살펴봅니다.
참고 자료
공식 문서
- Istio 공식 문서 — SPIRE 통합
- SPIFFE 공식 사이트 — SPIRE 개념
- SPIFFE 공식 문서 — SPIRE 확장 설계
- SPIRE Controller Manager — ClusterSPIFFEID CRD 문서
실전 구성 가이드
- AWS Samples: Istio on EKS with SPIRE Federation
- imesh.ai — How to Integrate Istio and SPIRE for Secure Workload Identity
- Jimmy Song — Managing Certificates in Istio with cert-manager and SPIRE
- HPE Developer — Service Mesh Security Hardening with SPIRE and Istio
심화 학습 및 사례 연구