제로 트러스트 보안 실전 가이드 — 개발자가 알아야 할 핵심
Never Trust Always Verify 원칙, mTLS, SPIFFE/SPIRE, 서비스 메시(Istio), 마이크로세그멘테이션, ID 기반 접근 제어, 코드 레벨 적용 방법 실전 가이드.
한 줄 요약: 제로 트러스트는 네트워크 위치에 관계없이 모든 요청을 검증하는 보안 모델이다. 개발자는 mTLS, SPIFFE/SPIRE, 서비스 메시를 통해 코드 레벨에서 이를 구현할 수 있다.
- 마이크로서비스 간 인증/인가를 설계하거나 개선해야 하는 백엔드 개발자
- Kubernetes 환경에서 서비스 메시(Istio/Linkerd) 도입을 검토 중인 DevOps/SRE
- 클라우드 인프라 보안 감사를 준비하거나 Zero Trust 아키텍처 전환을 맡은 팀
- mTLS, SPIFFE, SPIRE 개념을 처음 접하는 개발자
제로 트러스트란 무엇인가 — Never Trust, Always Verify
제로 트러스트(Zero Trust)는 2010년 Forrester Research의 John Kindervag가 제안한 보안 모델이다. 핵심 원칙은 세 가지다:
- Never Trust, Always Verify: 내부 네트워크에 있다고 해서 신뢰하지 않는다. 모든 요청은 명시적으로 검증돼야 한다.
- Least Privilege Access: 최소 권한 원칙. 필요한 리소스에만, 필요한 시간 동안만 접근을 허용한다.
- Assume Breach: 이미 침해됐다고 가정하고 설계한다. 측면 이동(lateral movement)을 방지하는 구조를 만든다.
전통적인 경계 보안(Perimeter Security)은 방화벽 안이면 신뢰한다는 모델이다. VPN 접속 후에는 내부 시스템 전체에 접근이 가능한 구조가 대표적이다. 클라우드, 리모트 워크, 마이크로서비스 확산으로 이 모델은 한계를 드러냈다 — 내부자 위협, 공급망 공격, 측면 이동 등에 취약하다.
| 항목 | 경계 보안 | 제로 트러스트 |
|---|---|---|
| 신뢰 기준 | 네트워크 위치 (내부/외부) | ID, 디바이스, 컨텍스트 기반 검증 |
| 측면 이동 방어 | 취약 (내부는 신뢰) | 강함 (마이크로세그멘테이션) |
| 원격 접속 | VPN 의존 | ID 기반 접근 제어 |
| 마이크로서비스 적용 | 어려움 | 서비스 메시로 자연스럽게 통합 |
mTLS — 서비스 간 상호 인증의 기반
TLS는 서버의 신원을 클라이언트가 검증하는 단방향 인증이다. mTLS(mutual TLS)는 서버와 클라이언트가 서로의 인증서를 검증하는 양방향 인증이다. 마이크로서비스 환경에서 서비스 A가 서비스 B를 호출할 때, B는 "이 요청이 진짜 A에서 왔는가"를 mTLS로 검증할 수 있다.
mTLS 동작 흐름:
- 클라이언트(서비스 A)가 자신의 인증서를 서버(서비스 B)에 제시
- 서버는 클라이언트 인증서를 신뢰하는 CA(Certificate Authority)로 검증
- 클라이언트도 서버 인증서를 CA로 검증
- 양방향 검증 완료 후 암호화된 채널 수립
인증서 관리가 mTLS의 핵심 과제다. 서비스 수십~수백 개가 존재하는 환경에서 수동으로 인증서를 발급·갱신하는 것은 불가능하다. SPIFFE/SPIRE가 이 문제를 해결한다.
Go — mTLS 서버 및 클라이언트 설정 예시package main import ( "crypto/tls" "crypto/x509" "net/http" "os" ) // mTLS 서버 설정 func newMTLSServer() *http.Server { caCert, _ := os.ReadFile("ca.crt") caPool := x509.NewCertPool() caPool.AppendCertsFromPEM(caCert) serverCert, _ := tls.LoadX509KeyPair("server.crt", "server.key") tlsConfig := &tls.Config{ ClientCAs: caPool, ClientAuth: tls.RequireAndVerifyClientCert, // 클라이언트 인증서 필수 Certificates: []tls.Certificate{serverCert}, MinVersion: tls.VersionTLS13, } return &http.Server{ Addr: ":8443", TLSConfig: tlsConfig, } } // mTLS 클라이언트 설정 func newMTLSClient() *http.Client { caCert, _ := os.ReadFile("ca.crt") caPool := x509.NewCertPool() caPool.AppendCertsFromPEM(caCert) clientCert, _ := tls.LoadX509KeyPair("client.crt", "client.key") tlsConfig := &tls.Config{ RootCAs: caPool, Certificates: []tls.Certificate{clientCert}, MinVersion: tls.VersionTLS13, } return &http.Client{ Transport: &http.Transport{TLSClientConfig: tlsConfig}, } }
SPIFFE와 SPIRE — 자동화된 워크로드 신원 관리
SPIFFE(Secure Production Identity Framework For Everyone)는 이기종 환경(VM, 컨테이너, 서버리스)에서 워크로드의 신원을 표준화된 방식으로 표현하는 오픈 표준이다. CNCF(Cloud Native Computing Foundation) 프로젝트다.
SPIFFE 핵심 개념:
- SPIFFE ID: URI 형식의 신원 식별자. 예:
spiffe://trust-domain/path/service-name - SVID(SPIFFE Verifiable Identity Document): SPIFFE ID를 담은 X.509 인증서 또는 JWT. 워크로드가 자신의 신원을 증명하는 데 사용한다.
- Trust Domain: 신뢰 경계를 나타내는 도메인. 예:
spiffe://company.internal
SPIRE(SPIFFE Runtime Environment)는 SPIFFE 표준의 레퍼런스 구현체다. Agent(각 노드에 배포)와 Server(중앙 CA 역할)로 구성된다. 워크로드는 SPIRE Agent와 Unix 소켓으로 통신해 자신의 SVID를 발급받는다. 인증서는 자동으로 갱신된다.
SPIRE 아키텍처:
- SPIRE Server: 루트 CA, 워크로드 등록 정보 관리
- SPIRE Agent: 각 노드에 데몬셋으로 실행. 워크로드를 검증(노드 증명)하고 SVID 발급
- 워크로드: SPIRE Agent API(Workload API)로 SVID 요청 및 갱신
SPIRE — Kubernetes 환경 기본 설치 (kubectl)# SPIRE CRD 및 namespace 생성 kubectl apply -f https://spiffe.io/downloads/spire-crds.yaml # SPIRE Server 배포 (StatefulSet) kubectl apply -f https://spiffe.io/downloads/spire-server.yaml # SPIRE Agent 배포 (DaemonSet) kubectl apply -f https://spiffe.io/downloads/spire-agent.yaml # 워크로드 등록 예시 kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create -spiffeID spiffe://example.org/ns/default/sa/myapp -parentID spiffe://example.org/spire/agent/k8s_psat/default/... -selector k8s:ns:default -selector k8s:sa:myapp # 발급된 SVID 확인 kubectl exec -n spire spire-agent-xxxx -- /opt/spire/bin/spire-agent api fetch x509 -socketPath /run/spire/sockets/agent.sock
서비스 메시와 Istio — 코드 변경 없이 mTLS 적용
서비스 메시는 사이드카 프록시(Envoy)를 각 서비스 파드에 주입해 네트워크 트래픽을 투명하게 제어한다. 개발자가 애플리케이션 코드를 수정하지 않아도 mTLS, 트래픽 암호화, 서비스 간 인가 정책을 적용할 수 있다.
Istio는 가장 널리 사용되는 서비스 메시다. 주요 기능:
- 자동 mTLS: 서비스 간 트래픽이 자동으로 mTLS로 암호화된다. PeerAuthentication 정책으로 제어.
- AuthorizationPolicy: 어떤 서비스가 어떤 서비스에 접근할 수 있는지 선언적으로 정의.
- 트래픽 관리: 카나리 배포, 서킷 브레이커, 재시도 정책.
- Observability: 분산 추적(Jaeger), 메트릭(Prometheus), 접근 로그.
Istio — PeerAuthentication (mTLS 강제) 및 AuthorizationPolicy# PeerAuthentication: 네임스페이스 전체에 mTLS STRICT 적용 apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: production spec: mtls: mode: STRICT # mTLS가 아닌 트래픽은 거부 --- # AuthorizationPolicy: 서비스 A만 서비스 B에 접근 허용 apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-service-a-to-b namespace: production spec: selector: matchLabels: app: service-b action: ALLOW rules: - from: - source: principals: - "cluster.local/ns/production/sa/service-a" to: - operation: methods: ["GET", "POST"] paths: ["/api/*"]
마이크로세그멘테이션과 ID 기반 접근 제어
마이크로세그멘테이션은 네트워크를 작은 구역으로 나눠 각 구역 간 트래픽을 명시적으로 허용/차단하는 기술이다. 침해가 발생했을 때 측면 이동을 방지하는 핵심 수단이다.
Kubernetes 환경에서의 구현 방법:
- NetworkPolicy: Pod 간 네트워크 트래픽을 레이블 기반으로 제어. Calico, Cilium 같은 CNI 플러그인이 실제로 적용한다.
- Istio AuthorizationPolicy: 네트워크 레이어를 넘어 서비스 신원(SPIFFE ID) 기반으로 접근 제어.
- OPA(Open Policy Agent): 정책을 코드(Rego)로 관리. API 게이트웨이, Kubernetes admission controller 등에 통합 가능.
ID 기반 접근 제어의 핵심은 "이 IP에서 왔으니 신뢰한다"가 아니라 "이 서비스 계정(신원)이므로 허용한다"는 방식의 전환이다. Kubernetes ServiceAccount와 SPIFFE ID를 결합하면 워크로드 레벨의 강한 신원 보장이 가능하다.
Kubernetes NetworkPolicy — 명시적 화이트리스트 기반 접근 제어# 기본 정책: 모든 인바운드 차단 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: production spec: podSelector: {} # 네임스페이스 내 모든 Pod policyTypes: - Ingress - Egress --- # 허용 정책: service-b는 service-a에서 오는 80 포트만 허용 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-service-a namespace: production spec: podSelector: matchLabels: app: service-b ingress: - from: - podSelector: matchLabels: app: service-a ports: - protocol: TCP port: 8080
개발자가 코드에서 적용하는 제로 트러스트 실천
인프라 수준의 mTLS, 서비스 메시 적용 외에 애플리케이션 코드에서도 제로 트러스트 원칙을 적용해야 한다.
1. JWT 검증을 모든 서비스에서 독립적으로 수행
API 게이트웨이에서만 JWT를 검증하고 내부 서비스는 통과시키는 방식은 경계 보안 사고방식이다. 각 서비스가 직접 토큰을 검증해야 한다. 서비스가 침해됐을 때도 다른 서비스에 대한 무조건 신뢰가 없어야 한다.
2. 서비스 간 호출에 서비스 ID 토큰 포함
사람 사용자의 JWT 외에, 서비스-서비스 간 호출에는 서비스 고유 ID 토큰을 포함해야 한다. Google의 경우 서비스 계정 토큰을 사용하고, SPIRE 환경에서는 JWT-SVID를 사용한다.
3. 시크릿 관리 — 환경변수 직접 사용 금지
DB 비밀번호, API 키 등을 환경변수에 직접 하드코딩하거나 컨테이너에 주입하는 방식은 제로 트러스트에 반한다. HashiCorp Vault, AWS Secrets Manager, Kubernetes External Secrets 등으로 동적 시크릿을 사용해야 한다.
1단계: 모든 서비스 간 통신 TLS 적용 (최소 요건)
2단계: 서비스 인증서 자동화 (cert-manager 또는 SPIRE)
3단계: mTLS STRICT 모드 전환
4단계: AuthorizationPolicy로 명시적 허용 목록 관리
5단계: OPA/Cedar 등으로 정책 코드화 (Policy as Code)