Istio에서는 Ingress Gateway를 활용하여 클라이언트와의 TLS 통신을 처리할 수 있다. 앞선 실습에서는 Gateway에서 TLS를 종료(Terminate)하고 내부 서비스는 HTTP로 처리했지만, 이번 실습에서는 TLS 요청을 그대로 서비스(Pod)까지 전달하는 PASSTHROUGH 모드를 사용한다.
이 실습을 통해 Istio의 Ingress Gateway가 TLS를 해제하지 않고 내부 서비스까지 SNI 기반으로 안전하게 전달할 수 있다는 사실을 확인할 수 있다.
[클라이언트 HTTPS 요청]
↓
[ Istio Ingress Gateway (SNI 라우팅만) ]
↓ (TLS 그대로 유지됨)
[ 내부 서비스 (httpbin) - HTTPS 직접 처리 ]
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 ..
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
아래는 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
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
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)까지 전달되었다는 의미.