k8s 서비스 간 인증 완전가이드
TL;DR
- 내부망 신뢰 모델은 이미 깨졌고, 서비스 간 통신도 Zero Trust 전제로 설계해야 한다.
- 단일 클러스터 내부는 ServiceAccount token과 mTLS, 대규모 환경은 Service Mesh와 SPIFFE/SPIRE 조합이 표준에 가깝다.
- 외부·크로스트러스트 호출은 OAuth client credentials, DPoP, cert-bound token 같은 별도 메커니즘으로 분리해야 한다.
1. 개념
k8s 서비스 간 인증은 서비스가 누구인지 암호학적으로 식별하고, 요청마다 그 신원을 검증해 통신을 허용하는 구조다. 핵심은 네트워크 위치가 아니라 워크로드 아이덴티티와 짧은 수명의 토큰·인증서다.
2. 배경
과거에는 사설 VPC나 클러스터 내부면 신뢰해도 된다고 봤지만, 공급망 공격·계정 탈취·lateral movement 사례가 누적되면서 내부망 자체를 신뢰하지 않는 Zero Trust 모델이 표준이 됐다.
3. 이유
서비스 간 인증 메커니즘을 모르면 토큰 재사용, audience 검증 누락, 장기 비밀키 남용, 메시 도입 오판 같은 문제가 반복된다. 어떤 경계 안에서 어떤 신원 모델을 써야 하는지 구분하는 기준이 필요하다.
4. 특징
- ServiceAccount projected token, mTLS, Service Mesh, SPIFFE/SPIRE, OAuth의 역할 분리
- short-lived credential과 자동 rotation 중심 설계
- 단일 클러스터·멀티클러스터·외부 파트너 호출을 구분한 인증 전략
- OPA, Cedar, Istio AuthorizationPolicy 같은 인가 계층과의 결합
- JWT 검증, JWKS 캐시, DPoP, cert binding 등 실전 안티패턴 대응
5. 상세 내용
k8s 서비스 간 인증 — 처음부터 깊게 이해하기
작성일: 2026-04-28 (v2 — 심층 보강) 목적: 빅테크/k8s 표준 service-to-service 인증 메커니즘 학습 자료 대상: 플랫폼·보안·SRE 엔지니어, 백엔드 개발자
📚 가장 먼저 — “왜 service간 인증이 필요한가”
옛날 방식 (지금은 위험)
사내 네트워크 (private VPC)
├─ Service A (10.0.1.5)
├─ Service B (10.0.1.6)
└─ Service C (10.0.1.7)
옛날엔 “사내 네트워크 안이면 신뢰” 가정.
- A → B 호출 시 인증 안 함
- “어차피 사내인데 누가 들어오겠어?”
왜 이게 문제인가
- 사내 네트워크 침투 1번 = 모든 서비스 함락
- 해커가 한 서버 뚫으면 → 옆 서비스 전부 자유 호출 (lateral movement)
- 회사 망 안에 있다는 사실만으로 신뢰 — “chewy center(바삭한 겉, 끈적한 내부)” 비유로 불림
- 개발자 실수: dev/staging/prod 잘못 연결되면 방어 없음
- Insider 위협: 내부자가 악의적으로 다른 팀 서비스 호출
- k8s에선 더 심각: pod IP 자주 바뀜, 같은 namespace 안에 여러 팀
- 공급망 공격(supply chain): 침해된 라이브러리/이미지가 사내망 안에 들어오면 즉시 전파
→ “네트워크 위치만으로 신뢰 X” 라는 게 Zero Trust 의 출발점.
실제로 일어난 일 — 회사들이 왜 바뀌었나
- Google Operation Aurora (2010): 중국발 APT(Advanced Persistent Threat) 공격으로 Google 내부 시스템 침해. 이 사건이 BeyondCorp(2011~) 설계의 직접적 계기.
- Uber GitHub 침해 (2016): 프리랜서 개발자 계정에서 AWS IAM 키 탈취 → 5,700만 사용자 정보 유출 → CISO $148M 벌금 + 형사 기소.
- Codecov 공급망 공격 (2021-04): Bash Uploader 변조로 23,000개 고객사 CI 환경 변수(AWS 키, SA 토큰, API 키) 2개월간 탈취.
- SolarWinds (2020): 빌드 시스템 침해 → 18,000개 조직에 백도어 배포.
→ “내부망이 깨끗하다”는 가정은 더 이상 성립하지 않는다.
🎯 Zero Trust란
“Never trust, always verify” — 어디서 오든 매 요청 검증.
어원과 역사적 기원
- John Kindervag (Forrester Research), 2010년 9월: 보고서 “No More Chewy Centers: Introducing the Zero Trust Model of Information Security”에서 “Zero Trust” 용어 공식 제안. 러시아 속담 “신뢰하되 검증하라(Trust but Verify)”를 정면으로 반박하며 만들어진 조어.
- Google BeyondCorp (2011 구현 시작, 2014 공개): Operation Aurora 직후 내부망 신뢰 폐기. USENIX
;login:6편 시리즈로 공개 (Rory Ward, Betsy Beyer 등). - NIST SP 800-207 (2020-08-11): 미국 표준기술연구소가 Zero Trust Architecture를 연방 표준으로 공식화 (Scott Rose 등 저).
- US Executive Order 14028 (2021-05-12): “Improving the Nation’s Cybersecurity” — 미 연방기관 Zero Trust 채택 의무화.
- OMB M-22-09 (2022-01): Federal Zero Trust Strategy — 2024 회계연도까지 ZT 목표 달성 명령.
핵심 원칙 (NIST 800-207 기준)
- 암묵적 신뢰 금지 (No Implicit Trust): 네트워크 위치(내부망/인터넷)나 자산 소유권만으로 신뢰 부여 X
- 분리된 인증·인가 (Discrete AuthN/AuthZ): 리소스 접근 세션 수립 전, 주체와 디바이스에 대한 인증·인가를 별도 함수로 수행
- 리소스 중심 보호: 네트워크 세그먼트가 아닌 개별 리소스를 보호 단위로 삼음
- 암호학적 신원 증명: 단순 IP/header만으론 불가
- 짧게 살고 자동 rotation되는 자격증명: 토큰/인증서
BeyondCorp vs BeyondProd
| 항목 | BeyondCorp | BeyondProd |
|---|---|---|
| 대상 | 사람(직원) → 기업 리소스 접근 | 서비스 → 서비스 통신 |
| 공개 | 2014~2018 USENIX 6편 | 2019-12 화이트페이퍼 |
| 신원 단위 | 사용자 + 디바이스 상태(device posture) | 서비스 ID + 코드 출처(code provenance) |
| 암호화 | TLS + 사용자 토큰 | mTLS / ALTS |
| k8s 매핑 | API Gateway + OIDC | Service Mesh + SPIFFE |
📖 참조:
📖 용어 사전 (Terminology Dictionary)
| 약자 | 풀네임 | 한 줄 설명 |
|---|---|---|
| JWT | JSON Web Token (RFC 7519) | JSON 기반 자기-완결형 토큰. iss/sub/aud/exp 클레임 표준화 |
| JWS | JSON Web Signature (RFC 7515) | JWT의 서명 부분 표준 |
| JWE | JSON Web Encryption (RFC 7516) | JWT의 암호화 변형 |
| JWK | JSON Web Key (RFC 7517) | 공개키를 JSON 형식으로 표현 |
| JWKS | JSON Web Key Set | JWK들의 묶음. 공개키 배포용 endpoint |
| JWA | JSON Web Algorithms (RFC 7518) | JWS/JWE에서 사용하는 알고리즘 목록 |
| JOSE | JSON Object Signing and Encryption | JWS/JWE/JWK/JWA/JWT 통칭. IETF 워킹 그룹 명칭 |
| OIDC | OpenID Connect | OAuth 2.0 위에 인증 레이어를 얹은 것. ID Token (JWT) 발급 |
| OAuth | Open Authorization | 인가 위임 프로토콜. 인증 X, 인가 O |
| PKCE | Proof Key for Code Exchange (RFC 7636) | OAuth 코드 가로채기 방지 |
| DPoP | Demonstrating Proof of Possession (RFC 9449) | Bearer 토큰 탈취 방지 — 매 요청마다 키 소유 증명 |
| TLS | Transport Layer Security | SSL의 후속 표준 |
| mTLS | mutual TLS | 양방향 인증서 검증 |
| PKI | Public Key Infrastructure | 공개키 + CA 체계 |
| CA | Certificate Authority | 인증서 발급 기관 |
| CRL | Certificate Revocation List | 폐기된 인증서 목록 |
| OCSP | Online Certificate Status Protocol (RFC 6960) | 인증서 폐기 상태 실시간 조회 |
| CSR | Certificate Signing Request | 인증서 발급 요청 |
| SAN | Subject Alternative Name | X.509 인증서의 대체 이름 필드. SPIFFE URI도 여기에 들어감 |
| PKIX | Public Key Infrastructure (X.509) | IETF 워킹 그룹 명칭. RFC 5280 |
| SA | ServiceAccount | k8s에서 Pod의 신원 |
| SPIFFE | Secure Production Identity Framework For Everyone | 워크로드 신원 표준 |
| SPIRE | SPIFFE Runtime Environment | SPIFFE의 레퍼런스 구현체 |
| SVID | SPIFFE Verifiable Identity Document | SPIFFE ID를 X.509 또는 JWT로 인코딩한 문서 |
| ALTS | Application Layer Transport Security | Google 내부 mTLS 변형 (2007~) |
| LOAS | Low Overhead Authentication Service | Google 내부 신원 시스템 |
| eBPF | (extended) Berkeley Packet Filter | Linux 커널 내 안전 코드 실행. Cilium의 기반 |
| XFCC | X-Forwarded-Client-Cert | 메시 내부에서 mTLS 클라이언트 정보 전달 헤더 |
| HBONE | HTTP-Based Overlay Network Environment | Istio Ambient의 mTLS 터널 프로토콜 |
| xDS | (eXtensible) Discovery Service | Envoy의 동적 설정 API |
| SigV4 | AWS Signature Version 4 | AWS API 호출 서명 방식 |
| HMAC | Hash-based Message Authentication Code | 공유 비밀키 기반 메시지 인증 |
| MITM | Man-In-The-Middle | 중간자 공격 |
| APT | Advanced Persistent Threat | 지속 표적형 공격 |
| IRSA | IAM Roles for Service Accounts (AWS) | EKS Pod이 IAM 역할 사용 |
| WI | Workload Identity (GCP/Azure) | k8s SA를 클라우드 IAM에 매핑 |
| KEP | Kubernetes Enhancement Proposal | k8s의 RFC 격 |
📜 표준 RFC/문서 빠른 참조 (Standards Quick Reference)
| RFC/문서 | 주제 | 발행 |
|---|---|---|
| RFC 5246 | TLS 1.2 | 2008-08 |
| RFC 5280 | X.509 PKIX 인증서 프로파일 | 2008-05 |
| RFC 5849 | OAuth 1.0 | 2010-04 |
| RFC 6749 | OAuth 2.0 Authorization Framework | 2012-10 |
| RFC 6750 | OAuth 2.0 Bearer Token Usage | 2012-10 |
| RFC 6960 | OCSP | 2013-06 |
| OpenID Connect Core 1.0 | OAuth 2.0 위 인증 레이어 | 2014-11 |
| RFC 7515 | JWS (JSON Web Signature) | 2015-05 |
| RFC 7516 | JWE | 2015-05 |
| RFC 7517 | JWK / JWKS | 2015-05 |
| RFC 7518 | JWA | 2015-05 |
| RFC 7519 | JWT | 2015-05 |
| RFC 7521-7523 | Assertion Framework / SAML / JWT Profile for OAuth | 2015-05 |
| RFC 7636 | PKCE | 2015-09 |
| RFC 8252 | OAuth 2.0 for Native Apps | 2017-10 |
| RFC 8446 | TLS 1.3 | 2018-08 |
| RFC 8693 | OAuth 2.0 Token Exchange | 2020-01 |
| RFC 8705 | OAuth 2.0 mTLS Client Auth + Cert-Bound Tokens | 2020-02 |
| RFC 8725 | JWT Best Current Practices | 2020-02 |
| NIST SP 800-207 | Zero Trust Architecture | 2020-08 |
| RFC 8996 | Deprecating TLS 1.0 / 1.1 | 2021-03 |
| RFC 9449 | DPoP | 2023-09 |
| RFC 9700 | OAuth 2.0 Security Best Current Practice (BCP 240) | 2025-01 |
| KEP-541 | TokenRequest API (Alpha 1.10 / GA 1.20) | — |
| KEP-1205 | Bound Service Account Tokens (GA 1.22) | — |
🔐 5가지 표준 메커니즘 — 하나씩 풀어서 설명
1. ServiceAccount Token (k8s 기본)
가장 단순한 출발점.
개념
k8s에서 모든 Pod는 ServiceAccount (SA) 를 가집니다. SA는 Pod의 신원증.
# 비유: SA = 직원증
Namespace: payments # 부서
└─ ServiceAccount: order-service # 부서 내 역할
└─ Pod: order-service-7b8d # 그 역할을 수행 중인 사람
동작 흐름
- k8s가 자동으로 Pod 안에 token 파일 마운트:
/var/run/secrets/kubernetes.io/serviceaccount/token이 파일은 JWT (JSON Web Token) 형태.
- Service A가 Service B 호출 시 이 token을 헤더에 첨부:
GET /api/orders Authorization: Bearer eyJhbGciOiJSUzI1NiIs... - Service B가 token 검증 — 세 가지 방법:
- TokenReview API: k8s API에 “이 토큰 유효한가?” 물어봄. 5–20ms 레이턴시. 즉시 폐기 지원
- JWKS 로컬 캐시: k8s가 공개한 공개키로 직접 검증. <1ms. TTL까지 유효
- OIDC Discovery: 외부 클러스터/서비스에서 검증할 때
Token 안에 뭐가 들었나
{
"iss": "https://kubernetes.default.svc", // 발급자
"sub": "system:serviceaccount:payments:order-service", // 누구인지
"aud": ["api.example.com"], // 누구에게 (target)
"exp": 1735689600, // 만료시각
"iat": 1735686000, // 발급시각
"jti": "a4c8f2d1-...", // 토큰 고유 ID (재사용 감지용)
"kubernetes.io": {
"namespace": "payments",
"pod": { "name": "order-service-7b8d", "uid": "..." }, // 객체 바인딩!
"serviceaccount": { "name": "order-service", "uid": "..." }
}
}
→ “payments 네임스페이스의 order-service라는 SA가, api.example.com이 받는 사람으로 발급됨”
옛날 방식 vs 지금 — 진화 타임라인
| 시점 | 변화 |
|---|---|
| k8s 1.0 (2015-07) | Legacy Secret-based 토큰. 무기한 유효, audience 없음, etcd 평문 저장 |
| k8s 1.10 (2018-03) | TokenRequest API Alpha (KEP-541) — audience/시간/객체 바인딩 |
| k8s 1.12 (2018-09) | TokenRequest API Beta |
| k8s 1.20 (2020-12) | TokenRequest API GA, BoundSA Volume Alpha |
| k8s 1.21 (2021-04) | BoundSA Volume Beta, Default ON |
| k8s 1.22 (2021-08) | Bound SA Tokens GA (KEP-1205) — 1시간 TTL, kubelet 자동 갱신 |
| k8s 1.24 (2022-05) | Legacy 토큰 자동 생성 중단 |
| k8s 1.28+ | Legacy 토큰에 Warning, 완전 제거 로드맵 |
Projected token 설정 예시
# Pod spec
spec:
serviceAccountName: order-service
volumes:
- name: api-token
projected:
sources:
- serviceAccountToken:
path: api-token
audience: api.example.com # 누가 받을지 명시 (필수!)
expirationSeconds: 3600 # 1시간 후 만료 (최소 600초)
kubelet은 토큰 수명의 80% 시점(48분)에서 자동 갱신. App은 매번 파일을 다시 읽기만 하면 됨.
aud 클레임이 왜 중요한가 — 토큰 혼동 공격
aud 검증 생략하면 token replay/confusion 공격 가능:
- Service A가 Service B용으로 발급받은 토큰을 Service C가 탈취
- Service C가 같은 토큰을 Service B에 그대로 전달 → Service A인 척 인증 성공
- Legacy SA 토큰은
aud자체가 없어 어디든 전달 가능했음
→ 모든 검증 측은 aud, iss, exp, jti 모두 검증해야 함.
💡 비유: 출입증 시스템
- 옛날 방식 = 영구 출입증 (한 번 발급받으면 평생 사용 → 분실/도난 시 위험)
- Projected token = 매시간 갱신되는 디지털 출입증 (1시간 지나면 자동으로 새로 발급, 어느 부서로 입장하는지까지 명시)
📖 참조:
2. mTLS (Mutual TLS) — 양방향 인증서
TLS는 이미 알고 있을 거예요
HTTPS 사이트 접속 시:
Browser ──→ TLS handshake ──→ Server
↑
Server cert 검증 (이 사이트가 진짜 google.com 맞나?)
→ 단방향: 클라이언트가 서버를 검증.
mTLS (mutual TLS) 는?
양방향 인증서 검증. 서버도 클라이언트의 인증서 검증.
Service A ←──→ TLS handshake ←──→ Service B
↑ ↑
A의 cert 검증 B의 cert 검증
→ 양쪽 다 자기가 누군지 인증서로 증명. 둘 다 “이 사람 진짜 맞아?” 확인.
TLS 진화 타임라인 — 30년의 역사
| 버전 | 시기 | 비고 |
|---|---|---|
| SSL 1.0 | 1994 (Netscape 내부) | 결함으로 미공개 |
| SSL 2.0 | 1995-02 | Netscape Navigator 1.1. MD5 의존, 결함 多 |
| SSL 3.0 | 1996-11 | Paul Kocher 등 완전 재작성. DH 키교환 |
| TLS 1.0 | 1999-01 | RFC 2246. SSL 3.1에 해당 |
| TLS 1.1 | 2006-04 | RFC 4346 |
| TLS 1.2 | 2008-08 | RFC 5246. SHA-256 PRF |
| TLS 1.3 | 2018-08 | RFC 8446. 1-RTT 핸드셰이크 |
| TLS 1.0/1.1 | 2021-03 (RFC 8996) | 공식 Deprecated |
TLS 1.3이 mTLS에 가져온 개선
- 핸드셰이크 2-RTT → 1-RTT (세션 재개 시 0-RTT)
- 클라이언트 인증서가 암호화 전송으로 전환 (1.2에서는 평문)
- 취약 스위트 전면 제거 (RSA static, SHA-1, MD5, CBC)
- Perfect Forward Secrecy(PFS) 강제
- Renegotiation 제거 → key update / post-handshake 인증으로 대체
왜 이게 강력한가
- 인증서는 암호학적으로 위조 불가 (signed by trusted CA)
- 헤더와 달리 로깅/덤프에 그대로 노출 안 됨
- TLS 자체가 traffic 암호화도 같이 해줌
- TLS 1.3에서는 클라이언트 신원도 암호화로 보호
비용/성능
| 항목 | 비용 |
|---|---|
| TLS 1.3 mTLS 핸드셰이크 (신규 연결) | ~1–2ms |
| 연결 재사용 (connection pool) | <0.1ms |
| 핸드셰이크 + cert 체인 검증 | 인증서 깊이/OCSP에 비례 |
→ 신규 연결 부담은 있으나 connection pool 잘 구성하면 실질 부담 미미.
단점
- 인증서 발급/배포/rotation을 누군가가 해야 함
- 그래서 수동 mTLS는 운영 부담 ↑
- PKI 운영 노하우 필요 (CA 관리, OCSP, CRL)
→ 그래서 Service Mesh가 등장 (다음 항목)
RFC 8705 — OAuth + mTLS 결합
OAuth 2.0 토큰을 클라이언트 인증서 thumbprint에 바인딩 = certificate-bound access token. 토큰 탈취해도 인증서 없으면 무용지물.
[Client] ──mTLS handshake──> [Auth Server]
(클라이언트 cert 제시)
[AS] → access_token (cnf.x5t#S256 = thumbprint of client cert)
[Client] ──mTLS + Bearer token──> [Resource Server]
[RS] 검증: 토큰의 cnf.x5t#S256 == 실제 사용된 cert의 thumbprint?
FAPI 2.0 (Financial-grade API), OpenBanking 표준에서 채택.
💡 비유: 만남 인증
- TLS = “내가 진짜 회사 정문이야” 한 쪽만 신분증 보여줌
- mTLS = 회사 정문도, 들어오는 사람도 둘 다 신분증 보여줌
- mTLS + Cert-bound token = 신분증 + 본인 봉인 도장 (도장이 신분증과 일치해야 유효)
📖 참조:
3. Service Mesh — mTLS 자동화 + 더
문제: mTLS 좋은데 cert 관리가 너무 어려워
- 모든 service에 cert 발급
- 만료 전에 갱신
- CA(Certificate Authority) 운영
- 유출 시 revoke
→ 이걸 자동으로 해주는 미들웨어가 Service Mesh.
Service Mesh의 어원과 기원
- 용어 등장: Buoyant의 William Morgan(전 Twitter 인프라 엔지니어)이 2017년 블로그 “What’s a Service Mesh? And Why Do I Need One?”에서 공식화. Twitter Finagle 경험에서 영감.
- Sidecar 패턴: Netflix Prana(2014-11)가 패턴을 공식화. 오토바이 사이드카처럼 메인 컨테이너 옆에 부착되는 보조 컨테이너.
진화 타임라인
| 연도 | 이벤트 |
|---|---|
| 2014-11 | Netflix Prana — Sidecar 패턴 공식화 |
| 2016-초 | Linkerd v1 (Buoyant) — 세계 최초 서비스 메시. JVM 프록시 |
| 2016-09 | Envoy 오픈소스 (Lyft, Matt Klein). C++, xDS API |
| 2017-05 | Istio 0.1 (Google + IBM + Lyft) — Envoy 데이터플레인 |
| 2017-09 | Envoy CNCF 인큐베이팅 |
| 2018-02 | Conduit (Buoyant) — Rust 기반 초경량 |
| 2018-07 | Consul Connect (HashiCorp) |
| 2018-09 | Linkerd v2 = Conduit + Linkerd 통합 |
| 2021-12 | Cilium Service Mesh 베타 — eBPF 기반 사이드카리스 |
| 2022-05 | Istio Ambient Mesh 발표 — ztunnel + Waypoint |
| 2022-08 | SPIFFE/SPIRE CNCF Graduated |
| 2024-11 | Istio Ambient GA (v1.24) |
대표 제품 — 깊이 비교
Istio (Sidecar) — 풍부한 기능, 복잡
┌─────────────────────────┐
│ Service A pod │
│ ┌────────┐ ┌──────────┐ │
│ │ App │→│ envoy(SC)│ │ ← sidecar container 자동 주입
│ └────────┘ └──────────┘ │
└─────────────────────────┘
↓ mTLS auto
┌─────────────────────────┐
│ Service B pod │
│ ┌──────────┐ ┌────────┐ │
│ │ envoy(SC)│→│ App │ │
│ └──────────┘ └────────┘ │
└─────────────────────────┘
- 컨트롤 플레인:
istiod(이전의 Pilot + Citadel + Galley 통합) - mTLS 인증서: SPIFFE SVID로 발급
- 인증/인가:
RequestAuthentication(JWT) +AuthorizationPolicy - 메모리: ~155–255 MiB / proxy
- p99 latency 증가: +166% (arxiv 2024 벤치마크 기준)
Istio Ambient — 사이드카 없는 신구조
[Pod A] ──eBPF 리디렉션──> [Ztunnel-NodeA] ─HBONE(mTLS)─> [Ztunnel-NodeB] ──> [Pod B]
L7 정책 필요시:
[Ztunnel-NodeA] ──> [Waypoint Proxy] ──> [Ztunnel-NodeB]
- Ztunnel (Rust, per-node DaemonSet): L4 mTLS만. 워크로드당 ~12 MiB
- Waypoint Proxy (선택): 네임스페이스/서비스 단위 L7 제어
- 메모리: ~12 MiB / ztunnel (사이드카 대비 90% 절감)
- p99 latency 증가: +8% (사이드카 +166% 대비)
Linkerd 2.x — 단순함의 미덕
- 데이터플레인: Rust 마이크로프록시 (
linkerd2-proxy, ~7MB RSS, Tokio 기반) - 컨트롤플레인:
destination+identity+proxy-injector - mTLS: tmpfs에 키 생성(디스크 X), 24시간 인증서, 자동 갱신
- p99 latency 증가: +33%
- 메모리: ~18–62 MiB / proxy
- Tap 기능으로 실시간 요청 관찰:
linkerd tap deploy/frontend - CNCF Graduated (2021)
- 도입: Microsoft, H-E-B, Subspace
- FIPS 140-2/3: Buoyant Enterprise (2025년 기준 검증)
Cilium Service Mesh — eBPF의 힘
[Pod A]
│ eBPF hook (커널)
▼
[Cilium Agent + eBPF maps] ─L7─> [Envoy DaemonSet]
│
└─L3/L4 직접 포워딩─> [Pod B]
- 데이터플레인: 커널 eBPF (L3/L4) + Envoy DaemonSet (L7)
- L7 활성화 시 p99 +99%, L3/L4만 시 매우 낮음
- Hubble: eBPF flow 캡처 기반 관찰 도구
- 커널 요구: ≥4.19 (권장 5.10+)
- 도입: Google Anthos, AWS EKS (CNI 옵션), Adobe, Capital One
Consul Connect — 하이브리드의 강자
- k8s + VM + 베어메탈 + Nomad 동시 메시 가능
- Intentions: 서비스 이름 기반 Allow/Deny 규칙
- 멀티 데이터센터 native, Mesh Gateway가 SNI 라우팅
- HashiCorp 스택(Vault/Nomad/Terraform)과 통합
- 라이선스: BUSL-1.1 (엔터프라이즈)
기타 — Kuma, AWS App Mesh, OSM
- Kuma (Kong, CNCF Sandbox): Global + Zonal CP, KDS 프로토콜
- AWS App Mesh: ⚠️ 2026-09-30 EOS — VPC Lattice / ECS Service Connect로 마이그레이션 권고
- OSM (Open Service Mesh, Microsoft): 2023 CNCF Archived. 신규 도입 X
동작 방식 — Sidecar pattern (Istio 기본 모드)
- Sidecar 컨테이너 (envoy proxy) 가 모든 traffic을 가로챔
- App은 평문 HTTP로 호출
- Sidecar가 자동으로 mTLS 변환
- Cert 발급/rotation도 자동
App 입장에서
// 이전 (mTLS 수동)
val httpClient = HttpClient.newBuilder()
.sslContext(loadKeystore("..."))
.build()
val response = httpClient.send(request, ...)
// Service Mesh 사용
val httpClient = HttpClient.newHttpClient() // 평문 그대로
val response = httpClient.send(request, ...) // sidecar가 알아서 mTLS
→ App 코드 변경 0.
Service Mesh의 다른 이점
- Observability: 모든 통신 trace, Prometheus/Jaeger/Kiali
- Traffic management: canary, A/B, retry, timeout, circuit breaker
- Policy enforcement: “Service A는 Service C 호출 금지” 같은 룰
- Fault injection: 카오스 엔지니어링용 의도적 장애 주입
💡 비유: 회사 보안실
- 직접 mTLS = 각자 자기 회사 출입을 본인이 관리 (출입증 발급도, 갱신도 본인)
- Service Mesh = 보안실(sidecar)이 자동 처리 (직원은 그냥 일하고, 출입증/검증/암호화는 보안실 담당)
- Ambient Mesh = 층마다 한 명의 경비(ztunnel) + 필요시 라운지(waypoint) — 직원마다 보안담당 붙이지 않음
📖 참조:
- Linkerd vs Istio Benchmarks (2021)
- arxiv 2411.02267 — Service Mesh mTLS Performance (2024)
- Istio Ambient GA Blog (2024-11)
- Envoy: 5 Years of OSS — Matt Klein
4. SPIFFE / SPIRE — 표준 service identity
왜 또 다른 표준?
- ServiceAccount = k8s 안에서만 의미 있음
- mTLS cert = 구조 자유로워서 회사마다 다 다름
- → 회사/플랫폼 가로질러 통일된 service identity 표준이 필요
SPIFFE — 어원과 기원
Secure Production Identity Framework For Everyone
- 2016-말 (Los Gatos, CA): 첫 SPIFFE 밋업. Google, Docker, Twilio, Salesforce, Netflix, Twitter 엔지니어 참석.
- Scytale, Inc. (Sunil James, Evan Gilman, Emiliano Berenbaum 창업, 2017): SPIFFE 전담 스타트업. Bessemer로부터 $3M 시드.
- 2017-12: SPIFFE / SPIRE GitHub 공개
- 2018-03-29: CNCF Sandbox
- 2020-06: CNCF Incubating
- 2022-09-20: CNCF Graduated (가장 높은 성숙도)
영감 원천: Google LOAS/ALTS, Netflix Metatron, Pinterest Knox, Meta 내부 시스템들.
SPIFFE ID 형식
spiffe://example.com/payments/order-service
└── trust domain ── path ─────┘
→ 어느 클러스터/클라우드에 있든 같은 형식으로 신원 표현.
SVID — 두 가지 형태
| 종류 | 사용처 |
|---|---|
| X.509-SVID | mTLS 인증서. SPIFFE ID는 SAN URI 확장에 |
| JWT-SVID | HTTP API용. SPIFFE ID는 sub 클레임에 |
SPIRE 아키텍처
[SPIRE Server]
├── Trust Domain 관리
├── Node Attestation: 노드 신원 검증
│ └── 플러그인: AWS IID, GCP GCE, k8s PSAT, TPM, join token
└── SVID 발급 CA
[SPIRE Agent] (각 노드 DaemonSet)
├── Workload Attestation: 프로세스 신원 검증
│ └── 플러그인: k8s(namespace/pod/SA), Unix(PID, UID)
└── Workload API (Unix socket)
└── SVID 제공 (X.509 or JWT)
- Node Attestation: AWS Instance Identity Document, GCP 인스턴스 메타데이터, TPM 등으로 노드 자체를 검증
- Workload Attestation: 프로세스 셀렉터(k8s SA/namespace/pod, Unix UID/PID) 기반 검증
- 자동 rotation: 기본 1시간 TTL, 자동 갱신
- Federation: 다른 trust domain 간 번들 교환으로 크로스 클러스터/클러스터 외부 신뢰
Istio·Envoy·Consul도 SPIFFE 사용
- Istio 1.14+ : SPIRE를 CA 백엔드로 통합 가능
- Envoy: SDS (Secret Discovery Service)가 SPIFFE 호환
- Consul Connect: SPIFFE ID 기반 신원
💡 비유: 국제 여권 표준
- 회사 출입증 (k8s SA) = 사내 직원증 (회사 안에서만 통함)
- mTLS cert (자체 PKI) = 회사마다 다른 사원증 양식 (다른 회사가면 못 알아봄)
- SPIFFE ID = 여권 (국가 가로질러도 동일 표준으로 신원 증명)
실전 도입 사례 — Uber
- 4,500개 마이크로서비스, 250,000+ 노드, 4개 클라우드 (GCP/OCI/AWS/온프렘)
- IRSA/WI 거부한 이유: 4개 클라우드 단일 패브릭 필요, 벤더 lock-in 회피
- LRU 캐시로 SPIRE 에이전트 CPU 40% 절감, 호스트당 워크로드 2.5배
빅테크 영감 사례
- Google ALTS (2007~): mTLS의 Google 변형. AES-128-GCM AEAD, DH 세션 키. LOAS 신원 시스템과 결합. 2017년 화이트페이퍼 공개.
- Netflix Lemur (2015, GitHub): X.509 인증서 오케스트레이션 프레임워크. 다수 CA 브로커.
- Netflix Metatron: Titus 컨테이너 플랫폼에 SPIFFE 컨셉 적용. CD 파이프라인에서 신원 주입.
- Netflix BLESS (2016): AWS Lambda 기반 Ephemeral SSH CA. 단기 SSH 인증서.
- Pinterest Knox (2016, GitHub): 키 관리 서비스. 버전 관리 기반 rotation.
- ByteDance: SPIRE를 100만 노드 규모로 확장 (“Achieving the Second Comma” KubeCon 발표).
📖 참조:
- SPIFFE Overview
- Uber Blog — SPIFFE/SPIRE at Scale
- CNCF SPIFFE Graduation
- Indeed Engineering — Workload Identity with SPIRE + OIDC + Istio
5. OAuth 2.0 (client_credentials grant) — 외부/cross-trust
언제 사용?
다른 회사/시스템이 우리 API 호출할 때, 또는 사람 개입 없는 M2M(Machine-to-Machine) 통신.
예시:
- 외부 SaaS → 사내 API
- 모바일 앱 백엔드 → 인증 서버
- 파트너사 → 우리 API
- 배치 워커 → 외부 클라우드 API
→ k8s SA token은 k8s 안에서만 의미. 외부엔 다른 표준 필요.
OAuth — 어원과 역사
Open Authorization. “Open”=개방형 표준, “Authorization”=인가(인증 X 주의).
| 버전 | RFC | 시기 | 편집자 |
|---|---|---|---|
| OAuth 1.0 community | — | 2007 | Eran Hammer-Lahav 외 |
| OAuth 1.0 공식 | RFC 5849 | 2010-04 | E. Hammer-Lahav |
| OAuth 2.0 | RFC 6749 | 2012-10 | Dick Hardt (Microsoft) |
| PKCE | RFC 7636 | 2015-09 | Sakimura/Bradley/Agarwal |
| Native Apps | RFC 8252 | 2017-10 | Denniss/Bradley |
| Token Exchange | RFC 8693 | 2020-01 | Jones 등 |
| mTLS Auth | RFC 8705 | 2020-02 | Campbell 등 |
| DPoP | RFC 9449 | 2023-09 | Fett 등 |
| Security BCP | RFC 9700 | 2025-01 | Lodderstedt 등 |
| OAuth 2.1 | draft | 진행 중 | Parecki 등 |
4가지 그랜트 타입 (RFC 6749)
| Grant Type | 사용처 | 2026년 상태 |
|---|---|---|
authorization_code |
사용자 있는 웹앱 | ✅ 권장 (PKCE 필수) |
implicit |
(브라우저 SPA 옛날 방식) | ❌ RFC 9700 폐기 권고 |
password |
(사용자 자격증명 직접) | ❌ RFC 9700 폐기 권고 |
client_credentials |
서비스-투-서비스 (M2M) | ✅ 권장 |
동작 흐름
[외부 서비스]
↓ Step 1: OAuth provider에 토큰 요청
POST https://auth.example.com/oauth/token
body: {
grant_type: "client_credentials",
client_id: "external-service-prod",
client_secret: "abc123...", # 또는 mTLS / Private Key JWT
scope: "service-x:invoke"
}
[OAuth Provider (Okta/Auth0/Keycloak)]
↓ Step 2: JWT 발급 (TTL 5–60분 권장)
{ access_token: "eyJ...", expires_in: 900 }
[외부 서비스]
↓ Step 3: API 호출
GET /v1/resource
Authorization: Bearer eyJ...
[Resource Server]
↓ Step 4: token 검증
- JWKS로 signature 검증
- aud, iss, scope, exp, jti 체크
- 통과하면 처리
핵심 컴포넌트
- client_id: 외부 서비스 식별자
- client_secret: 비밀번호 같은 것 (정기 rotation 필수, 단 비대칭 키 권장)
- scope: 권한 범위 (최소 권한 원칙)
- access_token: 짧게 사는 JWT (5–60분)
Client 인증 방식 — 어느 게 안전한가
| 방식 | 보안 레벨 | RFC | 비고 |
|---|---|---|---|
| client_secret_basic | 낮음 | 6749 | 공유 비밀, 유출 위험 |
| client_secret_post | 낮음 | 6749 | body에 secret |
| client_secret_jwt | 중간 | 7523 | HMAC SHA-256 |
| private_key_jwt | 높음 | 7523 | 비대칭 RSA/ECDSA. FAPI 표준 |
| tls_client_auth | 높음 | 8705 | PKI 기반 mTLS |
| self_signed_tls_client_auth | 중간 | 8705 | 자체 서명 cert |
| DPoP | 높음 | 9449 | 매 요청 키 소유 증명 |
DPoP — Bearer 토큰의 본질적 한계 해결
Bearer 토큰의 약점: 토큰 자체만 있으면 누구든 사용 가능 (무기명 채권 같음).
DPoP 해결책:
- 클라이언트가 키 쌍 생성 (non-extractable 권장)
- 토큰 요청 시 DPoP Proof JWT를 헤더로 (htm/htu/iat/jti/jwk 포함)
- AS가 공개키를 토큰에 바인딩 (
cnf.jkt클레임) - 매 API 호출마다 새 DPoP Proof JWT 첨부 (요청 URL/메서드 포함)
- RS가 요청과 DPoP 일치, nonce(선택) 검증
→ 토큰 탈취해도 키 없으면 사용 불가.
채택: FAPI 2.0, OpenBanking, Google OAuth 2.0.
빅 OAuth Provider
- Okta (enterprise)
- Auth0 (developer-friendly, Okta 자회사)
- Keycloak (오픈소스, self-host)
- Cognito (AWS)
- Azure AD / Entra (Microsoft)
- Zitadel (오픈소스, Go)
- Authelia (작은 셀프호스트)
💡 비유: 통행증 발급소
- ServiceAccount = 사내 직원증 (회사 안 통행)
- OAuth client_credentials = 방문객 통행증 (외부에서 회사 들어올 때, 발급소가 짧은 통행증 발급)
- DPoP = 본인만 사용 가능한 봉인된 통행증 (분실해도 도용 불가)
📖 참조:
🌐 추가 메커니즘 — k8s/메시 외 옵션
클라우드 워크로드 아이덴티티 (메시 불필요)
| 솔루션 | 동작 방식 | 특징 |
|---|---|---|
| AWS IRSA | EKS OIDC Provider → sts:AssumeRoleWithWebIdentity |
클러스터별 OIDC 설정 필요 |
| AWS EKS Pod Identity (2023~) | DaemonSet eks-pod-identity-agent + EKS Auth API |
OIDC 불필요, 멀티 클러스터 재사용 |
| GCP Workload Identity | k8s SA → Google SA 매핑, GKE Metadata Server | OIDC 토큰 교환 |
| GCP Workload Identity Federation | 외부 IdP → Google STS | 크로스 클라우드 |
| Azure AD Workload Identity | k8s SA Token Projection + AAD OIDC Federation | 구 AAD Pod Identity 대체 |
| HashiCorp Vault K8s Auth | SA 토큰 → Vault Role 매핑 | PKI/DB 동적 시크릿 통합 |
인가(Authorization) 도구 — 인증과 직교
| 도구 | 모델 | 특징 |
|---|---|---|
| OPA + Rego | 범용 (Datalog) | CNCF Graduated. k8s Admission, Envoy ext_authz |
| Cedar (AWS) | RBAC/ABAC | 형식 검증 가능. 42–60× Rego보다 빠름 |
| AuthZed SpiceDB | ReBAC (Zanzibar) | Google Drive류 권한. p95 5ms |
| Casbin | 다중 모델 (RBAC/ABAC/ReBAC) | 임베디드, 다중 언어 |
| Istio AuthorizationPolicy | L7 메시 정책 | 메시 사용 시 가장 단순 |
API 인증 패턴 (외부/크로스 트러스트)
| 패턴 | 사용처 | 핵심 |
|---|---|---|
| mTLS (RFC 8705) | 강한 보안, 규제 산업 | PKI 인프라 필요 |
| Private Key JWT (RFC 7523) | OAuth client auth | 비대칭 키, 서버 비밀 X |
| DPoP (RFC 9449) | 공개 API 고보안 | 토큰 바인딩 |
| API Key + HMAC | Stripe, AWS SigV4, GitHub Webhook | 단순. 공유 비밀 |
| Webhook Signing | 비동기 콜백 | timestamp + HMAC, replay 방지 |
비-k8s 컨텍스트
- gRPC Channel/Call Credentials: TLS, ALTS, OAuth 통합 API
- AWS IAM SigV4: AWS 서비스 간 / VPC Lattice + IAM Auth Policy
- Cloud Run service identity: 메타데이터 서버 OIDC ID Token + IAM
roles/run.invoker
📊 빅테크가 실제로 쓰는 것 — 사례 깊이
- BeyondCorp (2011~, 6편 USENIX 논문 2014–2018): 사람 → 리소스 Zero Trust
- BeyondProd (2019-12 화이트페이퍼): 서비스 → 서비스 Zero Trust
- ALTS (2007~): TLS 변형, AES-128-GCM AEAD. Borg Prime이 자격증명 주입
- Titan 칩: 부팅 무결성 검증 후 ALTS 자격증명 발급. 하드웨어 root of trust
- LOAS: 내부 신원 시스템. SPIFFE 영감 원천
Netflix
- Lemur (2015): X.509 인증서 오케스트레이션 (오픈소스)
- BLESS (2016): Lambda 기반 Ephemeral SSH CA (오픈소스)
- Metatron: Titus 통합 워크로드 신원 (SPIFFE 컨셉의 내부 구현)
- Wall-E: 인증/인가 게이트웨이
- 모든 internal traffic mTLS, SPIFFE 도입 후 보안 사고 60% 감소 보고
Meta (Facebook)
- 자체 Crypto Auth 시스템
- TUPPERWARE/Twine 워크로드 신원
- SPIFFE 영감 원천 중 하나
Uber
- SPIFFE/SPIRE 풀 도입 (2023-11 블로그 공개)
- 4,500 마이크로서비스, 250K+ 노드, 4개 클라우드
- 멀티클라우드 단일 신원 패브릭
Lyft
- Envoy의 발상지 (Matt Klein, 2015~2016 오픈소스)
- PHP 모놀리스 → 300+ 마이크로서비스 전환에서 출발
- Istio/Contour/AWS App Mesh의 데이터 플레인 표준화
Airbnb
- Istio + 내부 IDP
- Rest.li auth, 내부 SPIFFE-like
Stripe (외부 API 표준)
- Restricted API Keys: 리소스/액션 단위 세분화
- Webhook HMAC 서명: 엔드포인트별 고유 비밀키,
Stripe-Signature헤더, 300초 timestamp 허용
- Knox (2016 오픈소스): 키 관리. 버전 관리 기반 rotation, 접근 감사
Square/Block
- HSM 기반 신원
ByteDance
- SPIRE 100만 노드 규모 (KubeCon “Achieving the Second Comma”)
HashiCorp 내부
- Vault for everything
GitHub
- GitHub Apps OAuth, PAT (Personal Access Token) 생애주기, Webhook HMAC
🎯 단계별 추천 — 회사 성숙도에 맞춰
Tier 1: Startup / 소규모 (~50명)
가장 단순한 것부터:
- App 자체에 JWT 검증 로직 (Spring Security OAuth2 등 표준 라이브러리)
- ServiceAccount token + JWKS 검증 (k8s 안이면)
- 외부엔 Auth0/Cognito/Keycloak 같은 OAuth provider 사용
- mesh 도입 X (인프라 부담)
- cert-manager + Let’s Encrypt로 외부 cert
- 시크릿: Sealed Secrets 또는 SOPS
Tier 2: 성장기 (50~500명)
Service Mesh 도입:
- Linkerd 2 (가벼움, 학습곡선 낮음) 추천
- 자동 mTLS 적용 (PeerAuthentication PERMISSIVE → STRICT 단계적)
- 내부 service간은 mesh가 처리
- 외부는 OAuth 그대로
- HashiCorp Vault PKI + cert-manager
- External Secrets Operator로 클라우드 KMS 통합
Tier 3: 엔터프라이즈 (500명+)
BeyondProd 풀 스택:
- Istio (Sidecar 또는 Ambient) + SPIRE
- Code provenance (binary signing at build, e.g. Sigstore)
- Hardware attestation (TPM)
- 자체 PKI (Vault PKI + HSM)
- OPA Gatekeeper + Istio AuthorizationPolicy 다층
- Phantom token 패턴 (API GW에서 opaque → JWT)
멀티클라우드 / 멀티클러스터
- SPIFFE/SPIRE 단일 신원 패브릭
- Istio 멀티클러스터 + SPIRE Federation
- 각 클라우드 WI(IRSA/GCP WI/Azure WI)는 클라우드 API 접근용으로 병행
📊 성능 벤치마크 (실측값)
Linkerd 공식 (2021, 2,000 RPS, mTLS on)
| 지표 | Baseline | Linkerd 2 | Istio (Envoy) |
|---|---|---|---|
| p50 latency | ~6ms | ~15ms | ~21ms |
| p99 latency | ~25ms | ~90ms | ~200ms |
| 메모리/proxy | — | 17.8 MB | 154.6 MB |
| CPU/proxy | — | 10ms | 88ms |
arxiv 2024 (3,200 RPS, mTLS 강제)
| 메시 | p99 증가율 | CPU 증가 (c/s) | 메모리 증가 (c/s) |
|---|---|---|---|
| Baseline | 0% | 0.08/0.12 cores | 152/71 MiB |
| Istio Sidecar | +166% | +0.81/+0.87 | +255/+169 MiB |
| Istio Ambient | +8% | +0.23/+0.23 | +26/+26 MiB |
| Linkerd 2 | +33% | +0.29/+0.22 | +62/+63 MiB |
| Cilium (L7+enc) | +99% | +0.12/+0.08 | +95/+95 MiB |
토큰 검증 비용
| 항목 | 레이턴시 |
|---|---|
| TLS 1.3 mTLS handshake (신규) | ~1–2ms |
| Connection pool 재사용 | <0.1ms |
| JWT 로컬 JWKS 캐시 검증 | <1ms |
| TokenReview API | 5–20ms (kube-apiserver RTT) |
| OAuth introspection 원격 | 5–50ms |
📋 대안 비교 매트릭스
메시 비교
| 항목 | Istio Sidecar | Istio Ambient | Linkerd 2 | Cilium SM | Consul |
|---|---|---|---|---|---|
| 데이터플레인 | Envoy(C++) | ztunnel+waypoint | Rust micro | eBPF | Envoy |
| p99 증가 | +166% | +8% | +33% | +99%(L7) | ~Istio |
| 사이드카 | 필수 | 불필요 | 필수 | 불필요 | 필수 |
| 메모리/proxy | 155–255 | 12 (ztunnel) | 18–62 | 커널 | 30–50 |
| L7 정책 | 완전 | waypoint 필요 | 제한 | 제한 | 보통 |
| 멀티클러스터 | 지원(복잡) | 지원 | 지원 | 지원 | 네이티브 |
| FIPS | Tetrate/Solo.io | 실험 | Buoyant | 미검증 | HashiCorp |
| 학습곡선 | 가파름 | 중간 | 완만 | 중간 | 중간 |
| CNCF | Graduated | Graduated | Graduated | Graduated | — |
| 라이선스 | Apache 2.0 | Apache 2.0 | Apache 2.0 | Apache 2.0 | BUSL-1.1 |
워크로드 신원 비교
| 항목 | SPIFFE/SPIRE | k8s SA + JWKS | AWS IRSA | GCP WI | Azure WI |
|---|---|---|---|---|---|
| 형식 | SPIFFE URI | JWT | STS 임시 자격증명 | GCP SA token | Azure AD token |
| 표준 | CNCF Graduated | OIDC/JWT | OIDC fed | OIDC fed | OIDC fed |
| 자동 rotation | ✅ | ✅ (1h) | ✅ | ✅ | ✅ |
| 크로스 클라우드 | 네이티브 | 수동 | AWS만 | GCP만 | Azure만 |
| 증명 강도 | 최고 (이중) | 낮음 | 중간 | 중간 | 중간 |
| 복잡도 | 높음 | 낮음 | 낮음 | 낮음 | 낮음 |
외부 인증 비교
| 항목 | OAuth client_creds | mTLS (8705) | Private Key JWT | API Key+HMAC | DPoP |
|---|---|---|---|---|---|
| 보안 | 중간 | 높음 | 높음 | 낮음~중간 | 높음 |
| 리플레이 방지 | ❌ | TLS | jti | timestamp | nonce |
| 토큰 도난 시 | 사용 가능 | 어려움 | 가능 | 가능 | 어려움 |
| 폐기 | 만료 대기 | CRL/OCSP | 만료 대기 | 직접 삭제 | 만료 대기 |
| 키 관리 복잡도 | 낮음 | 높음 | 중간 | 낮음 | 중간 |
| 인프라 요건 | OAuth AS | PKI | OAuth AS+JWKS | 없음 | OAuth AS+클라이언트 |
인가 도구 비교
| 항목 | OPA/Rego | Cedar | Istio AuthZ | Casbin | SpiceDB |
|---|---|---|---|---|---|
| 모델 | 범용(Datalog) | RBAC/ABAC | L7 메시 | 다중 | ReBAC |
| 성능 | 1–5ms | <1ms | <1ms | <1ms (embed) | 5ms p95 |
| 검증가능 | 어려움 | 형식검증 | 낮음 | 중간 | 높음 |
| k8s 적합 | 최고 | 낮음 | 메시필수 | 보통 | 보통 |
🎯 상황별 최적 선택 (Decision Tree)
"단일 클러스터, internal only" → Linkerd 2
"멀티클러스터, 같은 회사" → Istio + SPIFFE/SPIRE
"크로스 클라우드, 같은 회사" → SPIFFE/SPIRE + 클라우드별 WI
"외부 SaaS 호출" → OAuth client_credentials + Private Key JWT
"파트너사 호출" → mTLS (RFC 8705) + cert-bound token
"고성능 (p99 <1ms 추가)" → Cilium L4 / Istio Ambient ztunnel
"FIPS 컴플라이언스" → Buoyant Linkerd / Tetrate Istio / Consul Enterprise
"운영 인력 1명" → Linkerd + cert-manager + JWKS 캐시
"운영 인력 10명+" → Istio + SPIRE + OPA 다층
"공개 API 고보안" → DPoP (RFC 9449)
"AWS 서비스 간" → SigV4 / VPC Lattice + IAM
"GCP Cloud Run 간" → OIDC ID Token + IAM
⚠️ Anti-pattern — 빅테크는 절대 안 하는 것
| ❌ Anti-pattern | 왜 안 됨 | ✅ 올바른 대안 |
|---|---|---|
| 영구 API key | 탈취 시 영구 피해 (Uber 2016, Codecov 2021) | TTL 5–60분 + auto rotation |
| Static service password | 갱신 어렵고 누출 잘 됨 | mTLS cert 또는 OAuth Private Key JWT |
| HTTP header에 raw secret | 로그/덤프에 노출 | TLS + signed JWT, 클레임 부분집합만 로그 |
| Per-app 자체 auth code | 버그 누적, CVE 위험 | 표준 라이브러리 (Spring OAuth2 등) |
| IP whitelist만 | 네트워크 침투 시 무용, NAT 환경에서 부정확 | identity verification 추가 (defense-in-depth) |
| Long-lived k8s SA Secret (예전 방식) | 자동 rotation 안 됨, etcd 평문 | Projected SA Token (1.22+) |
aud 검증 생략 |
Token confusion 공격 | aud, iss, exp, jti 모두 검증 |
alg 헤더 자동 신뢰 (alg=none) |
서명 우회 공격 (Auth0 2015 보고) | algorithms=["RS256"] 명시 |
| RS256 → HS256 알고리즘 혼동 | 공개키를 HMAC 비밀로 사용 (CVE 다수) | 라이브러리에 알고리즘 고정 |
| JWKS 무한 캐시 | 키 로테이션 실패 | TTL 10분, kid 기반 재조회 |
| XFCC 헤더 외부 신뢰 | 클라이언트 신원 위조 | 인그레스에서 SANITIZE |
| Cert pinning 자동화 X | 갱신 시 전체 다운 | CA 루트 또는 SPIFFE ID 핀 |
| Confused Deputy 무대응 | 권한 확장 공격 | RFC 8693 Token Exchange (act 클레임) |
| Refresh token rotation X | 탈취 후 영구 재발급 | RFC 7009 revoke, rotation 강제 |
실제 CVE/사건 사례
- Auth0 Critical JWT Vulnerabilities (2015):
alg=none+ RS256↔HS256 혼동을 광범위하게 공개. 다수 라이브러리 패치. - CVE-2026-22817 (Hono, JS): RS256→HS256 혼동, CVSS 8.2
- CVE-2026-23993 (HarbourJwt, Go): 알 수 없는 알고리즘 bypass
- CVE-2026-28802 (Authlib, Python): JWT 서명 검증 bypass, CVSS 7.7
- Uber 2016: GitHub에 AWS IAM 키 노출 → 5,700만 사용자, $148M 벌금
- Codecov 2021-04: Bash Uploader 변조 → 23,000개 고객사 CI 시크릿 탈취
🔑 베스트 프랙티스 — 프로덕션 체크리스트
토큰 수명(TTL) 권장값
| 토큰 | TTL |
|---|---|
| Access token (M2M) | 5–15분 |
| Access token (사용자) | 15–60분 |
| Refresh token (서비스) | 24h–7일, rotation 필수 |
| SVID X.509 | 1–24시간 |
| SVID JWT | 5–15분 |
| 인증서 (단기) | 1–24시간 |
| 인증서 (장기) | 90일 (Let’s Encrypt) |
| 서명 키 rotation | 90일 |
JWKS 캐싱 전략
Cache-Control: public, max-age=600, must-revalidate
- 기본 TTL: 10분
- Key rotation overlap window:
token_max_TTL + JWKS_cache_TTL + 10분 kid기반 미스 시에만 재조회kid는 키 이력에서 고유 (재사용 금지)
Defense-in-Depth 다층 구조
┌─────────────────────────────────────────────────┐
│ Layer 4: Network Policy (defense-in-depth) │
│ → IP/포트 기반, mTLS의 대체재 X │
├─────────────────────────────────────────────────┤
│ Layer 3: Service Mesh mTLS (STRICT) │
│ → 서비스 신원 + 암호화 │
├─────────────────────────────────────────────────┤
│ Layer 2: App-layer JWT │
│ → 사용자 컨텍스트, 위임 체인 (act claim) │
├─────────────────────────────────────────────────┤
│ Layer 1: Per-call Authorization │
│ → OPA/Cedar 세분화 인가 │
└─────────────────────────────────────────────────┘
감사 로그 — 무엇을 기록하나
✅ 기록:
- 호출자 신원 (SPIFFE URI)
- 대상 서비스, 액션
- 클레임 부분집합 (iss/sub/aud/jti/exp/scope)
- outcome, latency
❌ 절대 기록 금지:
- Raw JWT (Bearer 전체)
- client_secret
- Private key
- Refresh token
→ Raw 토큰을 로그에 남기면 로그 시스템(Splunk/ES) 자체가 비밀 저장소가 됨.
CA Rotation — Graceful Dual-CA Bridging
Phase 1: 신규 CA 생성 (구 CA 병행)
↓
Phase 2: --root-ca-file에 두 CA 모두 등록
↓
Phase 3: 워크로드 롤링 업데이트 (새 cert 취득)
↓
Phase 4: 구 CA 제거 (overlap window 경과 후)
cert-manager는 유효 기간 2/3 시점에서 자동 갱신 (renewBefore 설정).
🔁 마이그레이션 패턴
Legacy API Key → OAuth 2.0
Phase 1 (1–2개월): OAuth AS 구성, 신규 클라이언트 발급
Phase 2 (2–6개월): 양쪽 인증 수락, Deprecation 헤더 추가
Deprecation: true, Sunset: <date>
Phase 3: Legacy 비활성화, 429/403 + 마이그레이션 안내
Workato는 4년 overlap period 운영 사례.
No-Mesh → Service Mesh
1. PERMISSIVE 모드 도입 (mTLS + plaintext 동시)
PeerAuthentication.mtls.mode: PERMISSIVE
2. Grafana로 plaintext 트래픽 식별
3. 네임스페이스 단위 STRICT 전환
4. 전역 STRICT 적용
Sidecar → Ambient (Istio 1.24+)
kubectl label namespace payments istio.io/dataplane-mode=ambient
# 사이드카와 ambient 혼합 운용 가능 (전환 기간)
IRSA → EKS Pod Identity (AWS, 2023~)
| IRSA | Pod Identity | |
|---|---|---|
| OIDC Provider | 클러스터별 필요 | 불필요 |
| Trust Policy | 클러스터 ARN | 범용 |
| Fargate | 지원 | 미지원 (IRSA 유지) |
같은 클러스터에서 혼용 가능. 점진적 전환.
Static SA Secret → Projected Token (k8s 1.24+)
# 레거시 토큰 감사
kubectl get secrets -A -o json | \
jq '.items[] | select(.type=="kubernetes.io/service-account-token") |
{name: .metadata.name, ns: .metadata.namespace}'
# Pod spec에 projected volume 적용 → 레거시 Secret 삭제
🚨 인시던트 대응 플레이북
토큰 침해 (JWT, API Key)
즉각 (0–15분):
1. 침해된 client_secret/API key 즉시 비활성화
2. /revoke 엔드포인트로 refresh token 폐기
3. 침해 범위 감사 (jti, sub 기반)
4. 필요시 client 일시 차단
단기 (15분–1시간):
5. 새 secret 발급, 배포
6. 기존 JWT는 TTL 만료 또는 blocklist
7. 영향 데이터 접근 감사
8. 침해 경로 식별 (CI 환경 변수, GitHub 등)
인증서 침해 (mTLS, SA cert)
1. 침해된 cert serial을 CRL/OCSP에 등록
2. cert-manager 강제 갱신 (cmctl renew)
3. Vault PKI: lease revocation으로 파생 인증서 폐기
4. 영향 서비스 재시작 (새 cert 취득)
5. 중간 CA 침해 시: Dual-CA 절차로 루트부터 교체
Phantom Token 패턴
API Gateway가 opaque token 수신 → introspection으로 실시간 검증 → 내부에는 JWT 전달. JWT의 stateless 폐기 지연 문제 해결.
📝 한 장 요약 — 핵심만
| 메커니즘 | 어디 쓰나 | 핵심 |
|---|---|---|
| ServiceAccount Token | k8s 클러스터 내부 service간 | k8s 자동 발급, projected = TTL + auto rotate, audience 필수 |
| mTLS | 양방향 인증이 필요한 모든 곳 | TLS 양방향 cert 검증, traffic 암호화. RFC 8705로 OAuth 결합 |
| Service Mesh | mTLS를 자동으로 하고 싶을 때 | Sidecar 또는 Ambient/eBPF가 자동 처리. App 코드 변경 0 |
| SPIFFE/SPIRE | service identity 표준화 | 클러스터/클라우드 가로지르는 통일 신원. CNCF Graduated |
| OAuth 2.0 client_credentials | 외부/cross-trust | OAuth provider 발급 short-lived JWT. DPoP/mTLS로 강화 |
| DPoP (RFC 9449) | 공개 API 고보안 | 매 요청 키 소유 증명, 토큰 탈취 방어 |
| 워크로드 아이덴티티 (IRSA/WI) | 클라우드 IAM 통합 | 클라우드 API 접근용. SPIFFE와 병행 |
→ 핵심 원칙: “identity 기반 + 짧게 살고 + 자동 rotation + 암호학적 검증 + 다층 방어”.
🔗 참고 자료
Zero Trust 기반
| 주제 | 링크 | |—|—| | NIST SP 800-207 | https://csrc.nist.gov/pubs/sp/800/207/final | | BeyondCorp Part 1 | https://research.google/pubs/beyondcorp-a-new-approach-to-enterprise-security/ | | BeyondProd | https://cloud.google.com/docs/security/beyondprod | | ALTS Whitepaper | https://cloud.google.com/security/encryption-in-transit/application-layer-transport-security/resources/alts-whitepaper.pdf | | US Executive Order 14028 | https://www.cisa.gov/topics/cybersecurity-best-practices/executive-order-improving-nations-cybersecurity | | Forrester Zero Trust History | https://www.forrester.com/blogs/a-look-back-at-zero-trust-never-trust-always-verify/ |
RFC / 표준
| RFC | 주제 | 링크 | |—|—|—| | RFC 5246 | TLS 1.2 | https://datatracker.ietf.org/doc/html/rfc5246 | | RFC 5280 | X.509 PKIX | https://datatracker.ietf.org/doc/html/rfc5280 | | RFC 6749 | OAuth 2.0 | https://datatracker.ietf.org/doc/html/rfc6749 | | RFC 7519 | JWT | https://datatracker.ietf.org/doc/html/rfc7519 | | RFC 7517 | JWK/JWKS | https://datatracker.ietf.org/doc/html/rfc7517 | | RFC 7523 | Private Key JWT | https://datatracker.ietf.org/doc/html/rfc7523 | | RFC 7636 | PKCE | https://datatracker.ietf.org/doc/html/rfc7636 | | RFC 8446 | TLS 1.3 | https://datatracker.ietf.org/doc/html/rfc8446 | | RFC 8693 | OAuth Token Exchange | https://datatracker.ietf.org/doc/html/rfc8693 | | RFC 8705 | OAuth mTLS | https://datatracker.ietf.org/doc/html/rfc8705 | | RFC 8725 | JWT BCP | https://www.rfc-editor.org/rfc/rfc8725.html | | RFC 8996 | TLS 1.0/1.1 Deprecated | https://datatracker.ietf.org/doc/rfc8996/ | | RFC 9449 | DPoP | https://datatracker.ietf.org/doc/html/rfc9449 | | RFC 9700 | OAuth Security BCP (BCP 240) | https://datatracker.ietf.org/doc/rfc9700/ | | OpenID Connect Core 1.0 | OIDC | https://openid.net/specs/openid-connect-core-1_0.html |
Kubernetes
| 주제 | 링크 | |—|—| | k8s Authenticating | https://kubernetes.io/docs/reference/access-authn-authz/authentication/ | | Service Accounts | https://kubernetes.io/docs/concepts/security/service-accounts/ | | KEP-1205 Bound SA Tokens | https://github.com/kubernetes/enhancements/blob/master/keps/sig-auth/1205-bound-service-account-tokens/README.md | | CA Manual Rotation | https://kubernetes.io/docs/tasks/tls/manual-rotation-of-ca-certificates/ | | Bound SA Tokens (GCP Blog) | https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-bound-service-account-tokens |
서비스 메시
| 주제 | 링크 | |—|—| | Linkerd Benchmarks 2021 | https://linkerd.io/2021/05/27/linkerd-vs-istio-benchmarks/ | | arxiv 2024 mTLS Performance | https://arxiv.org/html/2411.02267v1 | | Istio Performance Docs | https://istio.io/latest/docs/ops/deployment/performance-and-scalability/ | | Istio Ambient GA | https://istio.io/latest/blog/2024/ambient-reaches-ga/ | | Istio mTLS Migration | https://istio.io/latest/docs/tasks/security/authentication/mtls-migration/ | | Linkerd vs Istio | https://www.buoyant.io/linkerd-vs-istio | | Cilium Service Mesh Beta | https://cilium.io/blog/2021/12/01/cilium-service-mesh-beta/ | | The History of Service Mesh | https://thenewstack.io/history-service-mesh/ | | Envoy 5 Years (Matt Klein) | https://mattklein123.dev/2021/09/14/5-years-envoy-oss/ |
SPIFFE
| 주제 | 링크 | |—|—| | SPIFFE Overview | https://spiffe.io/docs/latest/spiffe-about/overview/ | | SPIFFE GitHub | https://github.com/spiffe/spiffe | | CNCF SPIFFE Graduation | https://www.cncf.io/announcements/2022/09/20/spiffe-and-spire-projects-graduate-from-cloud-native-computing-foundation-incubator/ | | Uber SPIFFE/SPIRE Blog | https://www.uber.com/us/en/blog/our-journey-adopting-spiffe-spire/ | | Indeed Engineering (SPIRE+OIDC+Istio) | https://engineering.indeedblog.com/blog/2024/07/workload-identity-with-spire-oidc-for-k8s-istio/ | | SPIFFE Case Studies | https://spiffe.io/docs/latest/spire-about/case-studies/ |
클라우드 워크로드 신원
| 주제 | 링크 | |—|—| | AWS EKS Pod Identity | https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/ | | AWS App Mesh → VPC Lattice | https://aws.amazon.com/blogs/containers/migrating-from-aws-app-mesh-to-amazon-vpc-lattice/ | | GCP Workload Identity Federation | https://docs.cloud.google.com/iam/docs/workload-identity-federation | | Azure AD Workload Identity | https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation | | Cross-cloud auth (DEV.to) | https://dev.to/piyushjajoo/cross-cloud-authentication-in-kubernetes-a-comprehensive-guide-to-irsa-workload-identity-and-40en |
인가 도구
| 주제 | 링크 | |—|—| | OPA k8s Admission | https://www.openpolicyagent.org/docs/kubernetes | | AWS Cedar | https://docs.cedarpolicy.com/ | | AuthZed SpiceDB | https://authzed.com/learn/google-zanzibar | | Casbin | https://casbin.apache.org/ | | Policy Engine 비교 | https://www.permit.io/blog/policy-engine-showdown-opa-vs-openfga-vs-cedar |
빅테크 사례
| 주제 | 링크 | |—|—| | Netflix Lemur | https://github.com/Netflix/lemur | | Netflix BLESS | https://github.com/Netflix/bless | | Netflix Tech Blog (OSS Security) | https://netflixtechblog.com/a-brief-history-of-open-source-from-the-netflix-cloud-security-team-412b5d4f1e0c | | Pinterest Knox | https://github.com/pinterest/knox | | Pinterest Knox Blog | https://medium.com/pinterest-engineering/open-sourcing-knox-a-secret-key-management-service-3ec3a47f5bb | | Stripe API Keys | https://docs.stripe.com/keys | | Lyft Envoy USENIX SREcon | https://www.usenix.org/conference/srecon17americas/program/presentation/klein | | Uber 2016 Breach (Register) | https://www.theregister.com/2018/02/07/uber_quit_github_for_custom_code_after_2016_data_breach/ | | Codecov 2021 Post-Mortem | https://about.codecov.io/apr-2021-post-mortem/ |
보안 / 안티패턴
| 주제 | 링크 | |—|—| | PortSwigger JWT Algorithm Confusion | https://portswigger.net/web-security/jwt/algorithm-confusion | | Auth0 Critical JWT Vulns (2015) | https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ | | WorkOS JWT Algorithm Confusion | https://workos.com/blog/jwt-algorithm-confusion-attacks | | WorkOS aud claim 검증 | https://workos.com/blog/how-to-validate-the-jwt-aud-claim-and-why-it-matters | | WorkOS JWKS 가이드 | https://workos.com/blog/developers-guide-jwks | | ZITADEL Token Exchange | https://zitadel.com/docs/guides/integrate/token-exchange | | OAuth Best Practice (oauth.net) | https://oauth.net/2/oauth-best-practice/ |
PKI / cert-manager
| 주제 | 링크 | |—|—| | cert-manager Docs | https://cert-manager.io/docs/ | | cert-manager Vault Issuer | https://cert-manager.io/docs/configuration/vault/ | | Vault + cert-manager 튜토리얼 | https://developer.hashicorp.com/vault/tutorials/archive/kubernetes-cert-manager | | Buoyant FIPS Service Mesh | https://www.buoyant.io/fips-kubernetes-service-mesh | | Tetrate FIPS Istio | https://docs.tetrate.io/istio-subscription/installation/install-fips |
📖 추가로 공부할 만한 키워드
- OIDC (OpenID Connect): OAuth 2.0 위에 identity layer 얹은 것
- JWKS (JSON Web Key Set): JWT signature 검증용 공개키 셋
- Bound Service Account Token (k8s KEP-1205): projected token
- Workload Identity (GCP/AWS/Azure 명칭): 클라우드 IAM과 k8s SA 연결
- SPIFFE SVID: SPIFFE의 cert 또는 JWT 형태 신원 문서
- HBONE (Istio Ambient): HTTP-Based Overlay Network Environment, mTLS 터널
- xDS (Envoy): 동적 설정 API (LDS/RDS/CDS/EDS/SDS)
- Sigstore / Cosign: 코드 출처(code provenance) 서명
- SLSA (Supply-chain Levels for Software Artifacts): 공급망 보안 프레임워크
- Phantom Token Pattern: API GW에서 opaque ↔ JWT 변환
- Confused Deputy + Token Exchange (RFC 8693): 위임 체인 (
act클레임) - FIPS 140-3: 미 연방 암호 모듈 검증 표준 (2026 신규 검증 필수)
💡 처음부터 다시 — 한 단락 요약
옛날엔 “사내 네트워크 안이면 신뢰” 가 통했지만 지금은 위험. 그래서 Zero Trust = “어디든 신원 검증”. k8s 안에선 ServiceAccount projected token으로 신원 증명, JWT + JWKS로 검증 (audience/iss/exp/jti 모두 확인). 더 안전하게 하려면 mTLS로 양방향 인증서 검증, 인증서 관리는 Service Mesh(Linkerd/Istio Sidecar/Ambient/Cilium)가 자동화. 클러스터·클라우드 가로지르려면 SPIFFE/SPIRE로 통일된 워크로드 신원. 외부 시스템이 호출하면 OAuth 2.0 client_credentials로 short-lived JWT 발급, 더 강하게는 DPoP (RFC 9449) 또는 mTLS-bound token (RFC 8705). 빅테크는 모두 이 길을 따라가고 있고 — Google ALTS, Netflix Metatron/Lemur/BLESS, Uber SPIRE 250K 노드, ByteDance SPIRE 100만 노드 — identity-based + short-lived + auto-rotation + 다층 방어 + 암호학적 검증이 공통 키워드. NIST SP 800-207, RFC 9700(2025-01), KEP-1205가 이 방향의 표준 근거. 안티패턴은 단 하나의 규칙으로 압축됨: “신뢰는 검증으로만 만들어진다”.