[Ingress] Ingress Gateway without TLS Termination

y001·2025년 4월 19일

Istio 실전 스터디

목록 보기
10/26

Istio에서는 Ingress Gateway를 활용하여 클라이언트와의 TLS 통신을 처리할 수 있다. 앞선 실습에서는 Gateway에서 TLS를 종료(Terminate)하고 내부 서비스는 HTTP로 처리했지만, 이번 실습에서는 TLS 요청을 그대로 서비스(Pod)까지 전달하는 PASSTHROUGH 모드를 사용한다.

이 실습을 통해 Istio의 Ingress Gateway가 TLS를 해제하지 않고 내부 서비스까지 SNI 기반으로 안전하게 전달할 수 있다는 사실을 확인할 수 있다.


🧭 실습 목표

  • HTTPS 클라이언트 요청을 Istio Ingress Gateway에서 PASSTHROUGH 방식으로 처리
  • Gateway에서 TLS를 Terminate하지 않고 요청을 그대로 내부 서비스로 전달
  • 내부 서비스(Pod)는 자체적으로 TLS 인증서를 가지고 있으며, 직접 HTTPS 응답을 처리
[클라이언트 HTTPS 요청]
        ↓
[ Istio Ingress Gateway (SNI 라우팅만) ]
        ↓ (TLS 그대로 유지됨)
[ 내부 서비스 (httpbin) - HTTPS 직접 처리 ]

1️⃣ 자체 서명 인증서 생성

mkdir -p certs && cd certs

# CN은 반드시 SNI와 동일하게 설정해야 함
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=httpbin.example.com/O=myorg"

cd ..

2️⃣ Kubernetes Secret 및 네임스페이스 설정

kubectl create namespace passthrough-test
kubectl label namespace passthrough-test istio-injection=enabled

kubectl create -n passthrough-test secret tls httpbin-credential \
  --key=certs/tls.key \
  --cert=certs/tls.crt

3️⃣ HTTPS 서버 배포 (TLS 직접 처리)

아래는 TLS 인증서를 직접 사용하는 httpbin 서비스이다. gunicorn을 활용하여 직접 8443 포트를 열고 HTTPS 요청을 처리한다.

kubectl apply -n passthrough-test -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: httpbin
spec:
  selector:
    app: httpbin
  ports:
  - port: 443
    targetPort: 8443
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
      - name: httpbin
        image: docker.io/kennethreitz/httpbin
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: certs
          mountPath: /etc/certs
          readOnly: true
        args:
        - gunicorn
        - --certfile=/etc/certs/tls.crt
        - --keyfile=/etc/certs/tls.key
        - -b
        - 0.0.0.0:8443
        - httpbin:app
      volumes:
      - name: certs
        secret:
          secretName: httpbin-credential
EOF

4️⃣ Gateway 및 VirtualService 구성 (PASSTHROUGH 모드)

Istio Gateway에서는 TLS를 직접 해제하지 않고 SNI 기반으로 전달만 한다.

kubectl apply -n passthrough-test -f - <<EOF
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:
    - httpbin.example.com
EOF
kubectl apply -n passthrough-test -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - httpbin.example.com
  gateways:
  - passthrough-gateway
  tls:
  - match:
    - port: 443
      sniHosts:
      - httpbin.example.com
    route:
    - destination:
        host: httpbin.passthrough-test.svc.cluster.local
        port:
          number: 443
EOF

5️⃣ 동작 확인

kubectl get svc -n istio-system istio-ingressgateway

EXTERNAL-IP가 127.0.0.1이라면 다음 명령어로 요청을 보낸다:

curl -v --resolve httpbin.example.com:443:127.0.0.1 \
  https://httpbin.example.com/status/418 \
  --insecure

✅ 기대 결과

curl을 통한 HTTPS 접속 성공

* Connected to httpbin.example.com (127.0.0.1) port 443

→ 클라이언트가 Ingress Gateway로 HTTPS 요청을 성공적으로 보냈고,

TLS 핸드셰이크가 Gateway에서 해제되지 않았음 (PASSTHROUGH)

* TLS handshake ...
* SSL connection using TLSv1.2 ...
* Server certificate:
*  subject: CN=httpbin.example.com; O=myorg

→ 이 로그는 TLS 종료가 Gateway에서 되지 않았고, 그대로 httpbin 서비스(Pod)로 전달되었음을 보여줌.

즉, Istio Ingress Gateway는 이 요청을 해석하지 않았고, SNI 정보(httpbin.example.com)만 보고 목적지로 라우팅만 해준 것이다. (이게 바로 PASSTHROUGH의 핵심)

실제 응답이 httpbin에서 내려온 것

< HTTP/1.1 418 I'M A TEAPOT
< Server: gunicorn/19.9.0

→ gunicorn에서 직접 응답을 내리고 있다는 건, 이 요청이 내부 Pod의 HTTPS 포트(8443)까지 전달되었다는 의미.


✨ PASSTHROUGH의 의미

  • Gateway가 TLS 요청을 해제하지 않고 그대로 내부 Pod로 전달함
  • Pod 내부에서 직접 TLS를 처리함 (자체 인증서 보유)
  • TLS 종료 지점을 유연하게 설정 가능
    • 일부 서비스는 Gateway에서 TLS 종료 (SIMPLE)
    • 일부 서비스는 서비스 자체에서 종료 (PASSTHROUGH)

0개의 댓글