Kubernetes에서 SSL Termination과 SSL Passthrough 처리하기

2025년 12월 09일

devops

# Kubernetes# SSL# TLS# Ingress# Security

SSL Termination vs SSL Passthrough

로드 밸런서나 리버스 프록시에서 HTTPS 트래픽을 처리하는 방식에는 크게 두 가지가 있습니다.

SSL Termination (SSL 종료)

트래픽 흐름:

Client ←[HTTPS]→ Ingress Controller ←[HTTP/HTTPS]→ Pod

Ingress Controller에서 SSL/TLS를 복호화하고, 백엔드 Pod로는 평문 또는 재암호화된 트래픽을 전송합니다.

장점:

  • Pod의 CPU 부담 감소 (암호화/복호화 오프로딩)
  • 트래픽 검사, 로깅, 라우팅 등 L7 기능 활용 가능
  • 인증서를 Ingress Controller에서 중앙 관리
  • 헤더 추가/수정, 압축 등 추가 처리 가능

단점:

  • Ingress와 Pod 간 트래픽이 평문일 경우 내부 네트워크 보안 위험
  • End-to-End 암호화 불가

SSL Passthrough (SSL 통과)

트래픽 흐름:

Client ←[HTTPS]→ Ingress Controller ←[HTTPS]→ Pod

Ingress Controller가 암호화된 트래픽을 복호화하지 않고 그대로 Pod로 전달합니다.

장점:

  • 완전한 End-to-End 암호화
  • 민감한 데이터 처리 시 더 안전
  • 규정 준수(Compliance) 요구사항 충족

단점:

  • 각 Pod가 TLS 처리 부담
  • L7 라우팅 불가 (SNI 기반 L4 라우팅만 가능)
  • 각 Pod에 인증서 배포 필요
  • 트래픽 내용 검사 불가

Kubernetes에서의 SSL/TLS 처리

Kubernetes에서는 Ingress Controller가 외부 트래픽의 진입점 역할을 하며, SSL/TLS 처리를 담당합니다.

전체 트래픽 흐름

Kubernetes Cluster

① HTTPS

② HTTPS

③ HTTP/HTTPS

Client

LoadBalancer

Ingress Controller

Service

Pod

단계 설명
클라이언트가 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 아키텍처

Kubernetes Cluster

HTTPS

HTTPS

HTTP

HTTP

HTTP

Client

LoadBalancer

Ingress

Controller

Pod A

Pod B

특징:

  • TLS는 Edge(Ingress Controller)에서만 처리
  • Pod 간 통신은 평문 (내부 네트워크 신뢰)
  • 단순하고 가볍지만, 내부 트래픽은 암호화되지 않음

Istio Service Mesh 아키텍처

Istio Control Plane

Kubernetes Cluster

HTTPS

HTTPS

mTLS

mTLS

인증서 발급/갱신

인증서 발급/갱신

Client

LoadBalancer

Istio

Gateway

Pod A

+ Envoy

Pod B

+ Envoy

istiod

특징:

  • 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.com

TLS 모드 종류:

모드 설명 사용 사례
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: 8080

3. 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: 443

4. 자동 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.com

Ingress 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.com

Phase 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: PERMISSIVE

Phase 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: 8080

2. 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 yaml

Secret 구조:

apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
  name: myapp-tls-secret
data:
  tls.crt: LS0tLS1CRUdJTi...  # Base64 encoded
  tls.key: LS0tLS1CRUdJTi...  # Base64 encoded

3. 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.com

ClusterIssuer 설정:

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: nginx

4. 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: 8080

SSL 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: true

Ingress 설정:

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-manager

Wildcard 인증서

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-token
apiVersion: 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: 80

Re-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 오버헤드)

참고 자료

© 2025, 미나리와 함께 만들었음