SSL Termination vs SSL Passthrough
로드 밸런서나 리버스 프록시에서 HTTPS 트래픽을 처리하는 방식에는 크게 두 가지가 있습니다.
SSL Termination (SSL 종료)
트래픽 흐름:
Client ←[HTTPS]→ Ingress Controller ←[HTTP/HTTPS]→ PodIngress Controller에서 SSL/TLS를 복호화하고, 백엔드 Pod로는 평문 또는 재암호화된 트래픽을 전송합니다.
장점:
- Pod의 CPU 부담 감소 (암호화/복호화 오프로딩)
- 트래픽 검사, 로깅, 라우팅 등 L7 기능 활용 가능
- 인증서를 Ingress Controller에서 중앙 관리
- 헤더 추가/수정, 압축 등 추가 처리 가능
단점:
- Ingress와 Pod 간 트래픽이 평문일 경우 내부 네트워크 보안 위험
- End-to-End 암호화 불가
SSL Passthrough (SSL 통과)
트래픽 흐름:
Client ←[HTTPS]→ Ingress Controller ←[HTTPS]→ PodIngress Controller가 암호화된 트래픽을 복호화하지 않고 그대로 Pod로 전달합니다.
장점:
- 완전한 End-to-End 암호화
- 민감한 데이터 처리 시 더 안전
- 규정 준수(Compliance) 요구사항 충족
단점:
- 각 Pod가 TLS 처리 부담
- L7 라우팅 불가 (SNI 기반 L4 라우팅만 가능)
- 각 Pod에 인증서 배포 필요
- 트래픽 내용 검사 불가
Kubernetes에서의 SSL/TLS 처리
Kubernetes에서는 Ingress Controller가 외부 트래픽의 진입점 역할을 하며, SSL/TLS 처리를 담당합니다.
전체 트래픽 흐름
| 단계 | 설명 |
|---|---|
| ① | 클라이언트가 HTTPS로 요청 (예: https://api.example.com) |
| ② | Cloud Load Balancer가 Ingress Controller Service로 전달 |
| ③ | SSL Termination: HTTP로 전달 / SSL Passthrough: HTTPS 그대로 전달 |
| ④ | Service가 Pod로 로드밸런싱 |
Ingress Controller vs Service Mesh (Istio)
Kubernetes에서 TLS를 처리하는 방법은 크게 두 가지 아키텍처로 나뉩니다.
아키텍처 비교
| 항목 | Ingress Controller | Istio Service Mesh |
|---|---|---|
| 처리 계층 | Edge (클러스터 진입점) | Edge + Mesh (모든 트래픽) |
| TLS 범위 | 외부 → 내부 진입점 | 외부 → 내부 + Pod 간 통신 |
| 설치 복잡도 | 낮음 | 높음 |
| 리소스 사용량 | 낮음 (~50-200MB) | 높음 (Pod당 ~100MB+ sidecar) |
| 기능 | Ingress + TLS | Full Service Mesh |
| mTLS | 수동 설정 | 자동 (Zero Trust) |
| 관찰성 | 제한적 | 풍부 (분산 추적, 메트릭) |
| 트래픽 관리 | 기본 라우팅 | 고급 (카나리, A/B, 트래픽 미러링) |
Ingress Controller 아키텍처
특징:
- TLS는 Edge(Ingress Controller)에서만 처리
- Pod 간 통신은 평문 (내부 네트워크 신뢰)
- 단순하고 가볍지만, 내부 트래픽은 암호화되지 않음
Istio Service Mesh 아키텍처
특징:
- Edge와 Pod 간 통신 모두 TLS 처리
- Pod 간 자동 mTLS (Zero Trust 네트워크)
- 각 Pod에 Envoy sidecar 주입 (리소스 오버헤드)
- istiod가 인증서 자동 발급/갱신
Istio에서의 TLS 처리
1. Istio Gateway (Edge Termination)
Istio Gateway는 Ingress Controller의 역할을 하며, 외부 트래픽의 진입점입니다.
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: myapp-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE # SSL Termination
credentialName: myapp-tls-secret # Kubernetes Secret 참조
hosts:
- api.example.comTLS 모드 종류:
| 모드 | 설명 | 사용 사례 |
|---|---|---|
SIMPLE |
SSL Termination (단방향 TLS) | 일반 HTTPS 웹사이트 |
MUTUAL |
mTLS (양방향 TLS, 클라이언트 인증서 필요) | B2B API, 내부 서비스 |
PASSTHROUGH |
SSL Passthrough (암호화 유지) | End-to-End 암호화 필요 시 |
AUTO_PASSTHROUGH |
SNI 기반 자동 라우팅 | 멀티 테넌트 |
2. VirtualService로 라우팅
Gateway를 통과한 트래픽을 실제 서비스로 라우팅합니다.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp-vs
spec:
hosts:
- api.example.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /api/v1
route:
- destination:
host: myapp-service
port:
number: 80803. Istio에서 SSL Passthrough
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: passthrough-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
tls:
mode: PASSTHROUGH
hosts:
- secure-api.example.com
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: passthrough-vs
spec:
hosts:
- secure-api.example.com
gateways:
- passthrough-gateway
tls:
- match:
- port: 443
sniHosts:
- secure-api.example.com
route:
- destination:
host: secure-service
port:
number: 4434. 자동 mTLS (Service Mesh 내부 통신)
Istio의 가장 강력한 기능은 Pod 간 자동 mTLS입니다.
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # 모든 트래픽 mTLS 강제mTLS 모드:
| 모드 | 설명 |
|---|---|
STRICT |
mTLS만 허용 (평문 거부) |
PERMISSIVE |
mTLS와 평문 모두 허용 (마이그레이션용) |
DISABLE |
mTLS 비활성화 |
자동으로 처리되는 것들:
- 각 Pod에 고유한 인증서 발급 (SPIFFE 기반)
- 인증서 자동 갱신 (기본 24시간마다)
- Pod 간 통신 자동 암호화
- 서비스 간 인증 및 권한 부여
5. Istio + cert-manager 통합
외부 트래픽은 cert-manager로, 내부 트래픽은 Istio mTLS로 처리할 수 있습니다.
# cert-manager로 외부 인증서 발급
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: istio-gateway-cert
namespace: istio-system
spec:
secretName: istio-gateway-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com
---
# Istio Gateway에서 사용
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: istio-gateway-tls # cert-manager가 생성한 Secret
hosts:
- api.example.comIngress Controller vs Istio: 선택 가이드
Ingress Controller를 선택해야 할 때
적합한 경우:
- 간단한 웹 애플리케이션
- 외부 트래픽만 HTTPS 필요
- 리소스가 제한적 (작은 클러스터)
- 빠른 구축이 필요
- 팀의 Kubernetes 경험이 적음
추천 구성:
NGINX/Traefik Ingress Controller + cert-manager비용 (예시: 3개 Pod 애플리케이션):
- Ingress Controller: ~200MB
- 총 메모리: ~200MB
Istio를 선택해야 할 때
적합한 경우:
- 마이크로서비스 아키텍처 (10개 이상 서비스)
- Pod 간 통신도 암호화 필요 (Zero Trust)
- 고급 트래픽 관리 (카나리, A/B 테스팅)
- 풍부한 관찰성 필요 (분산 추적)
- mTLS 기반 서비스 인증 필요
- 규정 준수 (금융, 의료 등)
추천 구성:
Istio Gateway (Edge) + Istio mTLS (Mesh) + cert-manager비용 (예시: 3개 Pod 애플리케이션):
- Istio Control Plane (istiod): ~200MB
- Envoy Sidecar (Pod당): ~100MB
- 총 메모리: ~500MB (3 Pod × 100MB + 200MB)
하이브리드 접근: 점진적 도입
Istio는 점진적으로 도입할 수 있습니다.
Phase 1: Ingress Gateway만 사용
# Istio Gateway로 외부 트래픽만 처리
# Pod에는 sidecar 주입 안 함
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: phase1-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: app-tls
hosts:
- app.example.comPhase 2: 중요 서비스만 Mesh에 포함
# Namespace 레이블로 sidecar 자동 주입
apiVersion: v1
kind: Namespace
metadata:
name: critical-services
labels:
istio-injection: enabled # 이 Namespace의 Pod만 sidecar 주입Phase 3: PERMISSIVE 모드로 마이그레이션
# mTLS와 평문 모두 허용 (점진적 마이그레이션)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: PERMISSIVEPhase 4: STRICT 모드로 전환
# 모든 트래픽 mTLS 강제
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT실무 비교: NGINX vs Traefik vs Istio
같은 애플리케이션의 TLS 설정 비교
요구사항:
- 도메인: api.example.com
- Let’s Encrypt 인증서
- HTTPS → HTTP 백엔드
NGINX Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: myapp-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 8080장점:
- 가장 간단하고 직관적
- 광범위한 커뮤니티 지원
- 풍부한 어노테이션
Traefik
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
ingressClassName: traefik
tls:
- hosts:
- api.example.com
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 8080장점:
- 자체 Let’s Encrypt 통합 (cert-manager 불필요)
- 동적 설정 변경
- 현대적인 아키텍처
Istio
# Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: myapp-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: myapp-tls
hosts:
- api.example.com
---
# VirtualService
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp-vs
spec:
hosts:
- api.example.com
gateways:
- myapp-gateway
http:
- route:
- destination:
host: myapp.default.svc.cluster.local
port:
number: 8080
---
# Certificate (cert-manager)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
namespace: istio-system
spec:
secretName: myapp-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com장점:
- Edge + Mesh 통합 보안
- 자동 mTLS (Pod 간 통신)
- 고급 트래픽 관리
단점:
- 설정이 복잡 (3개 리소스 필요)
- 더 많은 리소스 사용
SSL Termination 구성
1. Ingress 리소스 설정
가장 일반적인 방식으로, Kubernetes Ingress 리소스에서 TLS를 정의합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
# NGINX Ingress의 경우
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# Traefik의 경우
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: nginx # 또는 traefik
tls:
- hosts:
- api.example.com
secretName: myapp-tls-secret # TLS 인증서가 저장된 Secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80802. TLS Secret 생성
인증서와 개인키를 Secret으로 저장합니다.
# 기존 인증서가 있는 경우
kubectl create secret tls myapp-tls-secret \
--cert=tls.crt \
--key=tls.key \
-n default
# Secret 확인
kubectl get secret myapp-tls-secret -o yamlSecret 구조:
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: myapp-tls-secret
data:
tls.crt: LS0tLS1CRUdJTi... # Base64 encoded
tls.key: LS0tLS1CRUdJTi... # Base64 encoded3. cert-manager를 통한 자동 인증서 발급
Let’s Encrypt를 사용한 자동 인증서 발급이 가능합니다.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com
- www.example.comClusterIssuer 설정:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx4. Traefik에서의 자동 인증서 발급
Traefik은 자체적으로 Let’s Encrypt 통합을 지원합니다.
# Traefik values.yaml
additionalArguments:
- "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"# Ingress 설정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
ingressClassName: traefik
tls:
- hosts:
- api.example.com
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 8080SSL Passthrough 구성
1. NGINX Ingress에서 SSL Passthrough
NGINX Ingress Controller는 --enable-ssl-passthrough 플래그를 활성화해야 합니다.
Deployment 설정:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
spec:
template:
spec:
containers:
- name: nginx-ingress-controller
args:
- /nginx-ingress-controller
- --enable-ssl-passthrough # 추가Helm으로 설치 시:
# values.yaml
controller:
extraArgs:
enable-ssl-passthrough: trueIngress 설정:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-passthrough
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: secure-api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-secure-service
port:
number: 443 # Pod는 443 포트에서 HTTPS 리스닝2. Traefik에서 SSL Passthrough
Traefik에서는 IngressRouteTCP CRD를 사용합니다.
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: myapp-passthrough
spec:
entryPoints:
- websecure
routes:
- match: HostSNI(`secure-api.example.com`)
services:
- name: myapp-secure-service
port: 443
tls:
passthrough: true # SSL Passthrough 활성화주요 차이점:
IngressRoute가 아닌IngressRouteTCP사용 (L4 레벨)HostSNI()매처 사용 (SNI 기반 라우팅)tls.passthrough: true설정
3. Pod에서 TLS 처리
SSL Passthrough를 사용하면 Pod가 직접 TLS를 처리해야 합니다.
apiVersion: v1
kind: Pod
metadata:
name: myapp-secure
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 443
name: https
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: myapp-tls-secret애플리케이션 코드 예시 (Go):
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Secure response"))
})
// TLS 인증서로 HTTPS 서버 시작
log.Fatal(http.ListenAndServeTLS(
":443",
"/etc/tls/tls.crt",
"/etc/tls/tls.key",
nil,
))
}인증서 관리 Best Practices
cert-manager 설치
cert-manager는 Kubernetes에서 인증서를 자동으로 발급하고 갱신하는 표준 도구입니다.
# Helm으로 설치
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.13.0 \
--set installCRDs=true인증서 자동 갱신
cert-manager는 인증서 만료 30일 전에 자동으로 갱신을 시도합니다.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
spec:
secretName: myapp-tls-secret
duration: 2160h # 90일
renewBefore: 720h # 만료 30일 전 갱신
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- api.example.com인증서 상태 확인:
# Certificate 리소스 확인
kubectl get certificate
# 상세 정보
kubectl describe certificate myapp-cert
# cert-manager 로그
kubectl logs -n cert-manager -l app=cert-managerWildcard 인증서
DNS-01 Challenge를 사용하면 와일드카드 인증서를 발급할 수 있습니다.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-dns-key
solvers:
- dns01:
cloudflare:
email: admin@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-tokenapiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
spec:
secretName: wildcard-tls-secret
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- "*.example.com"
- example.com실무 사용 시나리오
시나리오 1: 일반 웹 애플리케이션
요구사항:
- 공개 웹 서비스
- 자동 HTTPS 인증서
- 내부 네트워크 신뢰 가능
권장 방식: SSL Termination
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- www.example.com
secretName: webapp-tls
rules:
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp
port:
number: 80시나리오 2: 금융 API 서비스
요구사항:
- End-to-End 암호화 필수
- 규정 준수 (PCI-DSS 등)
- 트래픽 검사 불필요
권장 방식: SSL Passthrough
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: banking-api
spec:
entryPoints:
- websecure
routes:
- match: HostSNI(`api.bank.example.com`)
services:
- name: banking-api-service
port: 443
tls:
passthrough: true시나리오 3: mTLS가 필요한 마이크로서비스
요구사항:
- 클라이언트 인증서 검증
- 서비스 간 상호 TLS 인증
권장 방식: SSL Passthrough + Service Mesh
# Istio Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: mtls-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: PASSTHROUGH
hosts:
- "api.internal.example.com"시나리오 4: 멀티 테넌트 SaaS
요구사항:
- 테넌트별 커스텀 도메인
- 각 테넌트별 인증서
- 중앙화된 관리
권장 방식: SSL Termination + 동적 인증서
# 테넌트별 Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: tenant-a-cert
spec:
secretName: tenant-a-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- tenant-a.example.com
---
# Ingress에서 여러 호스트 처리
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: saas-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- tenant-a.example.com
secretName: tenant-a-tls
- hosts:
- tenant-b.example.com
secretName: tenant-b-tls
rules:
- host: tenant-a.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: saas-app
port:
number: 80
- host: tenant-b.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: saas-app
port:
number: 80Re-encryption: 하이브리드 접근
내부 네트워크도 암호화하면서 L7 기능을 유지하고 싶다면 Re-encryption을 사용할 수 있습니다.
NGINX Ingress에서 Re-encryption
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: reencrypt-ingress
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-ssl-verify: "true"
nginx.ingress.kubernetes.io/proxy-ssl-secret: "default/backend-ca-cert"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: frontend-tls # Ingress의 인증서
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-backend
port:
number: 443 # Backend도 HTTPS트래픽 흐름:
Client ←[TLS: frontend-tls]→ Ingress ←[TLS: backend-cert]→ Pod
(복호화) (재암호화)Service Mesh (Istio)를 통한 자동 mTLS
Istio를 사용하면 Pod 간 트래픽이 자동으로 mTLS로 암호화됩니다.
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: default
spec:
mtls:
mode: STRICT # 모든 트래픽 mTLS 필수성능 및 보안 고려사항
SSL Termination 최적화
NGINX Ingress 설정:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configuration
data:
# SSL 세션 캐시
ssl-session-cache: "shared:SSL:10m"
ssl-session-timeout: "10m"
# 최신 TLS 프로토콜만 허용
ssl-protocols: "TLSv1.2 TLSv1.3"
# 강력한 암호화 스위트
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
# HTTP/2 활성화
use-http2: "true"
# HSTS 활성화
hsts: "true"
hsts-max-age: "31536000"보안 점검 체크리스트
- TLS 1.2 이상만 허용
- 강력한 암호화 스위트 사용
- HSTS 헤더 설정
- 인증서 자동 갱신 설정
- 인증서 만료 모니터링 알림
- Secret 암호화 (Sealed Secrets, SOPS 등)
- RBAC으로 Secret 접근 제한
모니터링
인증서 만료 알림 (Prometheus + Alertmanager):
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cert-expiry-alert
spec:
groups:
- name: certificates
rules:
- alert: CertificateExpiringSoon
expr: |
certmanager_certificate_expiration_timestamp_seconds - time() < 604800
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate {{ $labels.name }} expiring in 7 days"결론 및 실무 팁
빠른 선택 가이드
| 상황 | 추천 솔루션 | 비용 | 복잡도 |
|---|---|---|---|
| 블로그, 소규모 웹 | NGINX + SSL Termination | 낮음 | 낮음 |
| E-Commerce, SaaS | Traefik + cert-manager | 중간 | 낮음 |
| MSA (10+ 서비스) | Istio + mTLS | 높음 | 높음 |
| 금융, 의료 | Istio STRICT mTLS | 높음 | 높음 |
| Legacy 앱 통합 | SSL Passthrough | 중간 | 중간 |
실무에서 자주 만나는 상황
상황 1: “Let’s Encrypt 인증서가 자동 갱신 안 돼요”
원인:
- HTTP-01 Challenge 실패 (80 포트 미개방)
- DNS-01 Challenge 실패 (API 토큰 만료)
- cert-manager Pod 리소스 부족
해결:
# 인증서 상태 확인
kubectl describe certificate myapp-cert
# cert-manager 로그 확인
kubectl logs -n cert-manager -l app=cert-manager --tail=100
# Challenge 상태 확인
kubectl get challenges
# 수동 재발급
kubectl delete certificate myapp-cert
kubectl apply -f certificate.yaml상황 2: “Istio 설치 후 기존 앱이 안 돼요”
원인:
- sidecar injection으로 인한 네트워크 정책 충돌
- mTLS STRICT 모드로 인한 외부 연결 차단
해결:
# 특정 Namespace만 sidecar 주입
apiVersion: v1
kind: Namespace
metadata:
name: legacy-app
labels:
istio-injection: disabled # sidecar 비활성화
---
# 외부 서비스 허용 (ServiceEntry)
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: external-db
spec:
hosts:
- db.external.com
ports:
- number: 5432
name: postgres
protocol: TCP
location: MESH_EXTERNAL상황 3: “멀티 도메인 인증서 관리가 복잡해요”
팁: Wildcard 인증서 사용
# *.example.com 와일드카드 인증서
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
spec:
secretName: wildcard-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- "*.example.com"
- example.com모든 Ingress에서 재사용:
spec:
tls:
- hosts:
- app1.example.com
- app2.example.com
secretName: wildcard-tls # 같은 인증서 재사용상황 4: “내부 서비스도 HTTPS가 필요해요”
방법 1: Re-encryption (NGINX)
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-ssl-verify: "false" # 자체 서명 인증서 허용방법 2: Istio mTLS (자동)
# 네임스페이스 전체에 mTLS 활성화
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT성능 최적화 팁
Tip 1: SSL Session Resumption
NGINX Ingress 설정:
data:
ssl-session-cache: "shared:SSL:10m"
ssl-session-timeout: "10m"
ssl-session-tickets: "true"효과: TLS 핸드셰이크 오버헤드 90% 감소
Tip 2: HTTP/2 활성화
data:
use-http2: "true"효과: 멀티플렉싱으로 동시 요청 처리
Tip 3: OCSP Stapling
data:
ssl-stapling: "on"
ssl-stapling-verify: "on"효과: 인증서 검증 속도 향상
보안 강화 체크리스트
필수 설정:
# TLS 1.2+ 만 허용
ssl-protocols: "TLSv1.2 TLSv1.3"
# 강력한 암호화 스위트
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
# HSTS 헤더 (브라우저 강제 HTTPS)
hsts: "true"
hsts-max-age: "31536000"
hsts-include-subdomains: "true"
# 보안 헤더 추가
add-headers: "default/custom-headers"custom-headers ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-headers
data:
X-Frame-Options: "DENY"
X-Content-Type-Options: "nosniff"
X-XSS-Protection: "1; mode=block"
Referrer-Policy: "strict-origin-when-cross-origin"디버깅 커맨드 모음
# 1. 인증서 정보 확인
kubectl get certificate
kubectl describe certificate myapp-cert
# 2. Secret 내용 확인
kubectl get secret myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
# 3. Ingress에서 사용 중인 인증서 확인
kubectl get ingress myapp -o yaml | grep secretName
# 4. cert-manager 로그
kubectl logs -n cert-manager -l app=cert-manager -f
# 5. Istio 인증서 확인
istioctl proxy-config secret <pod-name> -n <namespace>
# 6. mTLS 상태 확인
istioctl authn tls-check <pod-name>.<namespace>
# 7. 외부에서 TLS 테스트
curl -vI https://api.example.com
# 8. TLS 핸드셰이크 확인
openssl s_client -connect api.example.com:443 -servername api.example.com비용 최적화
작은 클러스터 (1-3 노드):
- NGINX Ingress만 사용
- Shared 인증서 활용
- 예상 비용: ~$50/월
중형 클러스터 (5-10 노드):
- Traefik + cert-manager
- 서비스별 인증서
- 예상 비용: ~$200/월
대형 클러스터 (10+ 노드):
- Istio 고려 (이미 리소스 충분)
- 예상 추가 비용: ~$300/월 (sidecar 오버헤드)