[Ingress] Ingress Sidecar TLS Termination

y001·2025년 4월 20일

Istio 실전 스터디

목록 보기
11/26

이번 실습에서는 Istio 공식 문서의 시나리오 중 하나인 Ingress Sidecar TLS Termination을 그대로 따라가면서, 사이드카(Envoy)가 TLS 종료를 수행하도록 설정하고 흐름을 검증해보았다. 기존의 Istio Ingress Gateway가 TLS를 종료하는 구조와는 달리, 이번 실습은 사이드카가 직접 TLS 통신을 종료하고 내부 컨테이너로는 HTTP로 전달하는 흐름을 구성하는 데 초점을 둔다.

이번 실습은 다음과 같은 통신 구조를 구성하고 검증하는 것이다:

1. 실습을 위한 사전 설정

Istio 설치 및 기능 활성화

먼저, Istio를 설치할 때 사이드카가 TLS 종료 기능을 사용할 수 있도록 다음과 같이 experimental 기능인 ENABLE_TLS_ON_SIDECAR_INGRESS를 활성화한다.

istioctl install \
  --set profile=default \
  --set values.pilot.env.ENABLE_TLS_ON_SIDECAR_INGRESS=true

이 설정은 Istio가 사이드카에서 TLS 종료를 처리할 수 있도록 내부 기능을 활성화하는 중요한 단계다.


2. 테스트용 네임스페이스 설정

test 네임스페이스 생성 및 사이드카 자동 주입 활성화

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

3. mTLS 정책 설정

전체 메시 네임스페이스에 mTLS 활성화

kubectl -n test apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
spec:
  mtls:
    mode: STRICT
EOF

httpbin의 9080 포트만 mTLS 비활성화

사이드카가 외부에서 들어오는 TLS 요청을 직접 처리할 수 있도록 하기 위해, 외부와 통신할 포트(9080)에 대해서만 mTLS를 끈다.

kubectl -n test apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: disable-peer-auth-for-external-mtls-port
spec:
  selector:
    matchLabels:
      app: httpbin
  mtls:
    mode: STRICT
  portLevelMtls:
    9080:
      mode: DISABLE
EOF

4. TLS 인증서 발급 및 Secret 생성

인증서 발급

# CA 인증서
openssl req -x509 -sha256 -nodes -days 365 \
  -newkey rsa:2048 -subj "/O=example Inc./CN=example.com" \
  -keyout example.com.key -out example.com.crt

# 서버 인증서 (httpbin)
openssl req -out httpbin.test.svc.cluster.local.csr \
  -newkey rsa:2048 -nodes \
  -keyout httpbin.test.svc.cluster.local.key \
  -subj "/CN=httpbin.test.svc.cluster.local/O=httpbin organization"

openssl x509 -req -days 365 \
  -CA example.com.crt -CAkey example.com.key -set_serial 1 \
  -in httpbin.test.svc.cluster.local.csr \
  -out httpbin.test.svc.cluster.local.crt

# 클라이언트 인증서
openssl req -out client.test.svc.cluster.local.csr \
  -newkey rsa:2048 -nodes \
  -keyout client.test.svc.cluster.local.key \
  -subj "/CN=client.test.svc.cluster.local/O=client organization"

openssl x509 -req -days 365 \
  -CA example.com.crt -CAkey example.com.key -set_serial 1 \
  -in client.test.svc.cluster.local.csr \
  -out client.test.svc.cluster.local.crt

Secret 생성

kubectl -n test create secret generic httpbin-mtls-termination-cacert --from-file=ca.crt=./example.com.crt

kubectl -n test create secret tls httpbin-mtls-termination \
  --cert=httpbin.test.svc.cluster.local.crt \
  --key=httpbin.test.svc.cluster.local.key

5. httpbin 배포 및 사이드카 설정

사이드카에 인증서 마운트 설정

kubectl -n test apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
  - port: 8443
    name: https
    targetPort: 9080
  - port: 8080
    name: http
    targetPort: 9081
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
      annotations:
        sidecar.istio.io/userVolume: '{"tls-secret":{"secret":{"secretName":"httpbin-mtls-termination","optional":true}},"tls-ca-secret":{"secret":{"secretName":"httpbin-mtls-termination-cacert"}}}'
        sidecar.istio.io/userVolumeMount: '{"tls-secret":{"mountPath":"/etc/istio/tls-certs/","readOnly":true},"tls-ca-secret":{"mountPath":"/etc/istio/tls-ca-certs/","readOnly":true}}'
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        name: httpbin
        ports:
        - containerPort: 80
EOF

6. 사이드카에서 TLS 종료 설정

kubectl -n test apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: ingress-sidecar
spec:
  workloadSelector:
    labels:
      app: httpbin
      version: v1
  ingress:
  - port:
      number: 9080
      protocol: HTTPS
      name: external
    defaultEndpoint: 0.0.0.0:80
    tls:
      mode: MUTUAL
      privateKey: "/etc/istio/tls-certs/tls.key"
      serverCertificate: "/etc/istio/tls-certs/tls.crt"
      caCertificates: "/etc/istio/tls-ca-certs/ca.crt"
  - port:
      number: 9081
      protocol: HTTP
      name: internal
    defaultEndpoint: 0.0.0.0:80
EOF

7. 클라이언트 배포 및 테스트

sleep 클라이언트 배포 (내부 + 외부)

kubectl -n test apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/sleep/sleep.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/sleep/sleep.yaml

내부 클라이언트 테스트 (Plain HTTP)

INTERNAL_CLIENT=$(kubectl -n test get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')
kubectl -n test exec "${INTERNAL_CLIENT}" -c sleep -- \
  curl -IsS "http://httpbin:8080/status/200"

HTTP/1.1 200 OK 반환되면 사이드카 뒤쪽으로 HTTP 통신이 잘 이루어짐을 의미함


외부 클라이언트 인증서 복사 및 mTLS 테스트

EXTERNAL_CLIENT=$(kubectl get pod -l app=sleep -n default -o jsonpath='{.items[0].metadata.name}')

kubectl cp client.test.svc.cluster.local.key default/${EXTERNAL_CLIENT}:/tmp/
kubectl cp client.test.svc.cluster.local.crt default/${EXTERNAL_CLIENT}:/tmp/
kubectl cp example.com.crt default/${EXTERNAL_CLIENT}:/tmp/ca.crt

kubectl exec -n default "${EXTERNAL_CLIENT}" -c sleep -- \
  curl -IsS \
    --cacert /tmp/ca.crt \
    --key /tmp/client.test.svc.cluster.local.key \
    --cert /tmp/client.test.svc.cluster.local.crt \
    -HHost:httpbin.test.svc.cluster.local \
    "https://httpbin.test.svc.cluster.local:8443/status/200"

HTTP/2 200 OK 응답이 오면 mTLS 종료가 사이드카에서 성공한 것.

잘못된 포트 요청 테스트

kubectl exec -n default "${EXTERNAL_CLIENT}" -c sleep -- \
  curl -IsS \
    --cacert /tmp/ca.crt \
    --key /tmp/client.test.svc.cluster.local.key \
    --cert /tmp/client.test.svc.cluster.local.crt \
    -HHost:httpbin.test.svc.cluster.local \
    "http://httpbin.test.svc.cluster.local:8080/status/200"

Connection reset by peer 에러가 발생하면 정상적으로 HTTPS 포트 외의 요청은 거부됨


마무리 정리

이번 실습을 통해, Istio에서 Ingress Gateway를 거치지 않고도 사이드카가 직접 TLS를 종료하는 구조를 성공적으로 구성해보았다. 이 구조는 내부에서 HTTP로 처리되지만 외부 요청에 대해서는 mTLS로 보호되고 있으며, Gateway가 없는 대신 Sidecar 리소스의 ingress 설정을 통해 직접 TLS 종료 처리를 맡는 구조이다. 이는 내부 서비스가 API Gateway처럼 외부에 직접 노출되는 상황에서 유용하게 활용할 수 있다.

0개의 댓글