Ssoon

2주차 : Istio gateways > 클러스터로 트래픽 유입 : gateways 트래픽 보안 본문

Istio Hands-on Study [1기]

2주차 : Istio gateways > 클러스터로 트래픽 유입 : gateways 트래픽 보안

구구달스 2025. 4. 15. 13:32
CloudNet@ 가시다님이 진행하는 Istio Hands-on Study [1기]

Istio Gateway로 안전한 트래픽 관리하기 🚀

클러스터 외부에서 내부로 들어오는 트래픽을 안전하게 관리하는 것은 서비스 운영의 핵심

Istio Gateway는 이를 위한 강력한 도구로, TLS/SSL 종료, 트래픽 암호화, 그리고 상호 TLS 인증 같은 기능을 제공


🔒 트래픽 보안의 중요성

  • 인터넷과 같은 공공 네트워크를 통해 클러스터 내부로 들어오는 트래픽은 보안이 필수입니다.
  • 사용자는 자신이 접속하려는 서비스가 진짜인지 확신하고 싶어 하며, 데이터가 도중에 누군가에게 노출되지 않기를 원합니다.
  • Istio Gateway는 이를 해결하기 위해 트래픽을 암호화하고 신뢰를 보장하는 기능을 제공합니다.

클라이언트가 서비스의 정체성을 신뢰할 수 있도록 보장

트래픽 암호화로 데이터 도청 방지


🛡️ TLS/SSL로 트래픽 보호하기

  • Istio Gateway는 외부에서 들어오는 TLS/SSL 트래픽을 종료(terminate)할 수 있습니다. 이를 통해 클라이언트와 Gateway 간의 통신이 암호화되어 안전하게 보호됩니다.
  • 또한, Gateway는 암호화된 트래픽을 내부 서비스로 안전하게 전달합니다. 이렇게 하면 외부와 내부 모두에서 데이터가 안전하게 오갈 수 있습니다.

TLS/SSL로 외부 트래픽 암호화

Gateway에서 트래픽 종료 후 내부 서비스로 안전 전달

 


🔄 비 TLS 트래픽을 TLS로 리다이렉트

  • 만약 사용자가 실수로 암호화되지 않은 HTTP로 접속하려고 하면 어떻게 될까요?
  • Istio Gateway는 이를 TLS 포트로 자동 리다이렉트할 수 있습니다. 이를 통해 모든 트래픽이 암호화된 경로로만 흐르도록 보장합니다.
  • 사용자는 별도의 설정 없이도 안전한 연결을 사용할 수 있습니다.

HTTP 트래픽을 HTTPS로 자동 리다이렉트

모든 트래픽의 암호화 보장


🤝 상호 TLS로 신뢰 강화

  • 상호 TLS(mTLS)는 클라이언트와 서버가 서로의 신원을 확인하는 강력한 인증 방식입니다.
  • Istio Gateway는 mTLS를 지원해 외부 클라이언트와 내부 서비스 간의 신뢰를 한층 더 강화합니다. 이를 통해 누가 서비스에 접근하는지, 그리고 서비스가 진짜인지 확실히 알 수 있습니다.

mTLS로 클라이언트와 서버 간 상호 신원 확인

서비스 신뢰도 및 보안 수준 향상


🚪 Istio Gateway로 안전한 첫걸음

Istio Gateway는 단순히 트래픽을 관리하는 것을 넘어, 클러스터 외부와 내부를 연결하는 안전한 문 역할을 합니다. TLS/SSL 종료, 비 TLS 트래픽 리다이렉트, 그리고 상호 TLS를 통해 트래픽을 암호화하고 신뢰를 구축할 수 있습니다.
이러한 기능들은 초보자라도 쉽게 설정할 수 있으며, 서비스를 더 안전하고 신뢰할 수 있게 만들어줍니다.🔐

2. Istio Gateway로 HTTP 트래픽 안전하게 보호하기 🔐

인터넷을 통해 서비스로 들어오는 HTTP 트래픽을 안전하게 보호하려면 TLS 설정이 필수

Istio Gateway를 사용하면 HTTPS를 통해 트래픽을 암호화하고, 중간자 공격(MITM)을 방지


🛡️ 중간자 공격(MITM)과 TLS의 중요성

  • 중간자 공격(MITM)은 클라이언트가 원래 접속하려던 서비스 대신 가짜 서비스에 연결되면서 발생합니다. 이 가짜 서비스는 민감한 정보를 탈취할 수 있습니다.
  • TLS는 서비스의 신원을 보장하고 트래픽을 암호화해 이런 공격을 막아줍니다.
  • Istio Gateway는 TLS를 설정해 모든 HTTP 트래픽을 HTTPS로 안전하게 전송하도록 도와줍니다.

MITM 공격: 가짜 서비스가 민감한 정보를 탈취

TLS로 서비스 신원 보장 및 트래픽 암호화


🔑 TLS 설정을 위한 인증서와 키

  • TLS를 활성화하려면 Istio Gateway에 올바른 인증서와 개인 키를 설정해야 합니다.
  • 인증서는 서버의 신원을 증명하는 공개 키로, 신뢰할 수 있는 인증 기관(CA)에서 서명합니다.
  • 클라이언트는 CA의 인증서를 신뢰하며, 이를 통해 서버의 인증서를 검증합니다.
  • 이후 클라이언트는 인증서의 공개 키로 트래픽을 암호화하고, 서버는 개인 키로 이를 해독합니다.

인증서: 서버 신원을 증명하는 CA 서명 공개 키

클라이언트와 서버 간 암호화된 통신 보장


🤝 TLS 핸드셰이크와 세션 키

  • TLS는 단순히 공개/비공개 키로만 작동하지 않습니다.
  • TLS 핸드셰이크 과정에서는 공개/비공개 키(비대칭 암호화)를 사용해 초기 통신을 설정한 뒤, 대칭 암호화를 위한 세션 키를 생성합니다.
  • 이 세션 키는 TLS 세션 동안 트래픽을 효율적으로 암호화하고 해독하는 데 사용됩니다.

TLS 핸드셰이크: 비대칭 암호화로 세션 키 생성

세션 키: 대칭 암호화로 효율적인 트래픽 암호화


🌐 HTTPS로 안전한 트래픽 전송

  • Istio Gateway는 외부에서 들어오는 트래픽을 HTTPS로 처리하도록 설정할 수 있습니다.
  • 이를 통해 클라이언트와 Gateway 간 트래픽이 암호화되며, Gateway는 TLS 연결을 종료한 후 내부 서비스로 트래픽을 전달합니다.
  • 내부 서비스로의 트래픽은 이후 다른 보안 메커니즘으로 보호될 수 있습니다.

HTTPS로 외부 트래픽 암호화

Gateway에서 TLS 종료 후 내부 서비스로 전달


🔧 인증서 관리와 외부 CA 통합

  • 운영 환경에서는 인증서와 키를 안전하게 관리하는 것이 중요합니다.
  • Kubernetes secrets는 기본적으로 안전하지 않으므로, 외부 CA나 내부 PKI와 통합하는 것이 좋습니다.
  • 예를 들어, cert-manager 같은 도구를 사용하면 인증서 발급과 갱신을 자동화할 수 있습니다.

Kubernetes secrets는 안전하지 않음

cert-manager로 인증서 관리 자동화 권장


🚀 Istio Gateway로 안전한 HTTPS 시작하기

Istio Gateway를 사용하면 HTTP 트래픽을 HTTPS로 안전하게 전송할 수 있습니다. TLS 설정을 통해 중간자 공격을 방지하고, 인증서와 키를 활용해 서비스 신원을 보장하며, 트래픽을 암호화할 수 있습니다. 외부 CA와의 통합까지 고려하면, 더 안전하고 효율적인 보안 환경을 구축할 수 있습니다. 🛠️

Istio Gateway로 HTTP를 HTTPS로 안전하게 리다이렉트하기 🔄

서비스로 들어오는 모든 트래픽을 안전하게 보호하려면 HTTPS를 강제하는 것이 중요

Istio Gateway를 사용하면 HTTP 트래픽을 HTTPS로 자동 리다이렉트하도록 설정


🔒 왜 HTTP를 HTTPS로 리다이렉트해야 할까?

  • HTTP는 암호화되지 않은 상태로 데이터를 전송하기 때문에 보안 위험이 큽니다.
  • 반면, HTTPS는 TLS를 사용해 트래픽을 암호화하여 데이터를 안전하게 보호합니다.
  • Istio Gateway는 HTTP로 들어오는 트래픽을 HTTPS로 리다이렉트하도록 설정할 수 있어, 모든 트래픽이 암호화된 경로로만 흐르도록 보장합니다.

HTTP: 암호화되지 않아 보안 취약

HTTPS: TLS로 트래픽 암호화, 안전한 통신 보장


🚀 HTTP 리다이렉트 설정하기

  • Istio Gateway에서 HTTP 트래픽을 HTTPS로 리다이렉트하려면 Gateway 리소스를 수정하면 됩니다.
  • HTTP 포트(80번)에 httpsRedirect 옵션을 추가해, HTTP로 들어오는 요청을 HTTPS 포트(443번)로 자동 전환하도록 설정합니다.

httpsRedirect: HTTP 트래픽을 HTTPS로 리다이렉트

사용자에게 투명한 보안 강화


🌐 리다이렉트 동작 방식

  • HTTP로 서비스에 접속하면, Istio Gateway는 301 Moved Permanently 응답을 반환합니다.
  • 이 응답은 클라이언트에게 HTTPS URL로 다시 요청하라는 지시를 내립니다.
  • 클라이언트는 이를 따라 HTTPS로 재접속하며, 이후 모든 통신은 암호화된 상태로 이루어집니다.

301 리다이렉트: 클라이언트를 HTTPS로 안내

모든 트래픽이 암호화된 HTTPS로 전환


🔐 HTTPS로 안전한 서비스 구축

Istio Gateway의 HTTP 리다이렉트 기능을 사용하면 서비스로 들어오는 모든 트래픽을 HTTPS로 강제할 수 있습니다. 이를 통해 보안 취약점을 줄이고, 사용자 데이터를 안전하게 보호할 수 있습니다.🛡️

Istio Gateway로 상호 TLS(mTLS)로 안전한 통신 구축하기 🤝

서비스로 들어오는 트래픽을 보호하려면 서버뿐만 아니라 클라이언트의 신원도 확인하는 것이 중요

Istio Gateway의 상호 TLS(mTLS) 기능을 사용하면 클라이언트와 서버가 서로의 신원을 인증하고 트래픽을 암호화


🔐 상호 TLS(mTLS)란 무엇인가요?

  • 일반 TLS에서는 서버가 클라이언트에게 자신의 신원을 증명합니다.
  • 하지만 mTLS는 한 단계 더 나아가, 클라이언트도 서버에게 자신의 신원을 증명합니다.
  • 이를 통해 양쪽 모두가 서로를 신뢰할 수 있는 안전한 통신이 가능해집니다.
  • mTLS는 인증서 교환을 통해 신원을 확인하고 트래픽을 암호화합니다.

mTLS: 클라이언트와 서버가 서로 신원 인증

인증서로 신뢰 구축 및 트래픽 암호화


🛡️ mTLS를 위한 인증서 설정

  • mTLS를 설정하려면 Istio Gateway가 클라이언트의 인증서를 검증할 수 있는 CA 인증서가 필요합니다.
  • 이를 위해 CA 인증서 체인을 Kubernetes secret으로 Gateway에 제공합니다.
  • 또한, 클라이언트는 자신의 인증서와 개인 키를 서버에 제출해 신원을 증명합니다.

CA 인증서: 클라이언트 인증서 검증용

클라이언트 인증서와 키: 신원 증명용


🔄 mTLS 인증 과정

  • mTLS에서는 서버와 클라이언트가 각각 인증서를 주고받습니다.
  • 서버는 클라이언트의 인증서를 CA 인증서를 사용해 검증하고, 클라이언트도 서버의 인증서를 검증합니다.
  • 이 상호 인증이 완료되면 트래픽이 암호화된 채로 안전하게 전송됩니다. 이 과정은 양쪽 모두의 신뢰를 보장합니다.

상호 인증: 서버와 클라이언트 간 인증서 검증

암호화된 트래픽으로 안전한 통신


🚀 mTLS로 더욱 안전한 서비스

Istio Gateway의 mTLS를 사용하면 외부 클라이언트의 신원을 확인하고, 모든 트래픽을 암호화할 수 있습니다. 이를 통해 클러스터 내부로 들어오는 트래픽의 보안을 한층 더 강화할 수 있습니다.🔒

Istio Gateway로 다중 가상 호스트를 TLS로 안전하게 서비스하기 🌐

하나의 HTTPS 포트로 여러 서비스를 안전하게 제공하고 싶다면 ?

Istio Gateway는 단일 포트(443번)에서 여러 가상 호스트를 각각의 인증서와 키로 서비스


🚀 다중 가상 호스트와 TLS의 필요성

  • 서비스가 많아질수록 각기 다른 도메인(예: webapp.istioinaction.io, catalog.istioinaction.io)을 하나의 Gateway에서 관리해야 할 때가 있습니다.
  • Istio Gateway는 단일 HTTPS 포트에서 여러 가상 호스트를 지원하며, 각 호스트에 고유한 인증서와 키를 설정해 안전하게 서비스할 수 있습니다.

단일 포트(443)에서 여러 가상 호스트 지원

각 호스트에 고유한 TLS 인증서와 키 설정


🔑 가상 호스트별 인증서 설정

  • 각 가상 호스트는 고유한 인증서와 개인 키를 필요로 합니다.
  • 예를 들어, webapp.istioinaction.io와 catalog.istioinaction.io는 각각 다른 인증서를 사용합니다.
  • Istio Gateway는 이러한 인증서들을 Kubernetes secret으로 관리하며, Gateway 설정에서 각 호스트에 맞는 인증서를 지정합니다.

각 가상 호스트에 고유 인증서와 키 할당

Kubernetes secret으로 인증서 안전 관리


🤝 SNI로 올바른 인증서 선택

  • Istio Gateway가 단일 포트에서 여러 인증서를 어떻게 구분할까요?
  • 비결은 TLS 확장 기능인 Server Name Indication(SNI) 에 있습니다.
  • 클라이언트가 HTTPS 연결을 시작할 때, TLS 핸드셰이크의 ClientHello 단계에서 접속하려는 서비스의 도메인을 알려줍니다. Istio Gateway는 이를 바탕으로 올바른 인증서를 선택해 응답합니다.

SNI: 클라이언트가 요청한 도메인 식별

단일 포트에서 올바른 인증서 동적 선택


🔐 다중 호스트로 안전한 서비스 운영

Istio Gateway를 사용하면 단일 HTTPS 포트에서 여러 가상 호스트를 안전하게 서비스할 수 있습니다. SNI를 활용해 각 호스트에 맞는 인증서를 제공하며, 모든 트래픽을 TLS로 암호화합니다.🛡️

 

 

 

 


 

 

 

 

 


🔒 1. Istio Gateway에서 TLS로 안전한 HTTPS 트래픽 설정하기

서비스 메시로 들어오는 트래픽을 안전하게 보호하려면 TLS(Transport Layer Security)를 설정해 HTTPS를 사용하는 것이 필수입니다. 이를 통해 MITM(Man-in-the-Middle) 공격을 방지하고 모든 트래픽을 암호화할 수 있습니다. 이번 가이드에서는 Istio의 Gateway 리소스를 활용해 HTTPS를 설정하고, 트래픽을 안전하게 처리하는 방법


🛡️ TLS로 MITM 공격 막기

MITM 공격은 클라이언트가 의도한 서비스 대신 가짜 서비스에 연결되어 민감한 정보를 노출시키는 공격입니다. 예를 들어, 사용자가 webapp.istioinaction.io에 접속하려 했지만, 공격자가 가짜 서버를 만들어 데이터를 가로채는 상황이죠. 이를 방지하려면 TLS를 사용해 트래픽을 암호화하고 서버의 신원을 확인해야 합니다.

TLS는 서버가 **인증서(certificate)**를 제시해 자신의 신원을 증명하도록 합니다. 이 인증서는 **CA(Certificate Authority)**라는 신뢰할 수 있는 기관에 의해 서명된 서버의 공개 키입니다. 클라이언트는 CA의 인증서를 신뢰하며, 이를 통해 서버의 인증서가 유효한지 확인합니다. 이후 공개 키를 사용해 트래픽을 암호화하고, 서버는 비밀 키로 이를 복호화합니다.

TLS는 MITM 공격을 방지하고 트래픽을 암호화해 클라이언트와 서버 간 안전한 통신을 보장합니다.
(그림 4.8: 클라이언트와 서버 간 TLS 설정 과정)

TLS 핸드셰이크는 비대칭 암호화(공개/비밀 키)를 사용해 초기 통신을 설정한 뒤, 대칭 암호화(세션 키)를 통해 실제 트래픽을 암호화합니다. 자세한 내용은 부록 C에서 확인하세요!


🔐 Kubernetes Secret으로 인증서와 키 준비하기

Istio Gateway에서 TLS를 활성화하려면 서버의 인증서비밀 키를 제공해야 합니다. 이를 위해 Kubernetes의 Secret 리소스에 인증서와 키를 저장합니다.

Kubernetes Secret은 기본적으로 평문으로 저장되므로 보안에 취약합니다. 실제 프로덕션 환경에서는 더 안전한 저장 방식을 고려하세요(예: Vault 또는 외부 Secret 관리 도구).

  • 다음 명령어를 실행해 webapp-credential라는 Secret을 생성합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl create -n istio-system secret tls webapp-credential \
--key ch4/certs/3_application/private/webapp.istioinaction.io.key.pem \
--cert ch4/certs/3_application/certs/webapp.istioinaction.io.cert.pem
secret/webapp-credential created
  • 이 명령어는 istio-system 네임스페이스에 Secret을 생성합니다. Istio 1.13.0 기준으로, TLS에 사용되는 Secret은 istio-ingressgateway와 동일한 네임스페이스에 있어야 합니다. 기본적으로 istio-ingressgateway는 istio-system 네임스페이스에서 실행되므로 Secret도 여기에 저장합니다.

Secret에 인증서와 비밀 키를 저장해 Gateway가 TLS를 처리할 수 있도록 준비합니다.
프로덕션에서는 Gateway를 별도의 네임스페이스에서 실행하는 것이 좋습니다.


🌐 Gateway에서 HTTPS 설정하기

  • 이제 인증서와 키가 준비되었으니, Gateway 리소스를 수정해 HTTPS를 활성화합니다.
    아래는 HTTP(포트 80)와 HTTPS(포트 443)를 모두 지원하도록 설정한 Gateway 예제입니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat ch4/coolstore-gw-tls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"
  • 이 설정은 다음과 같은 작업을 수행합니다:
    • 포트 80에서 HTTP 트래픽을 수신합니다.
    • 포트 443에서 HTTPS 트래픽을 수신하며, tls 섹션에서 webapp-credential Secret을 사용해 TLS를 설정합니다.
    • mode: SIMPLE은 기본 TLS 모드로, 클라이언트와 서버 간 단방향 TLS를 의미합니다.
  • 이제 이 Gateway 리소스를 적용합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl apply -f ch4/coolstore-gw-tls.yaml -n istioinaction
gateway.networking.istio.io/coolstore-gateway configured

 

Gateway에서 포트 443을 열고 TLS 설정을 추가해 HTTPS를 활성화했습니다.
credentialName으로 Secret을 지정해 인증서와 키를 사용합니다.


🔍 HTTPS 트래픽 테스트하기

HTTPS 설정이 완료되었으니, 트래픽을 테스트해봅시다. 다음 명령어로 webapp.istioinaction.io에 HTTPS 요청을 보냅니다:

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v -H "Host: webapp.istioinaction.io" https://localhost:30005/api/catalog
* Host localhost:30005 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:30005...
* connect to ::1 port 30005 from ::1 port 35894 failed: Connection refused
*   Trying 127.0.0.1:30005...
* Connected to localhost (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
* Closing connection
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
  • 하지만 이 요청은 실패합니다:
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
* Closing connection
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
  • 문제는 클라이언트가 서버의 인증서를 검증할 수 없다는 점입니다. 서버가 제시한 인증서는 webapp.istioinaction.io용으로 발급되었지만, 기본 CA 인증서로는 이를 신뢰할 수 없습니다. 이를 해결하려면 올바른 CA 인증서를 지정해야 합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v -H "Host: webapp.istioinaction.io" https://localhost:30005/api/catalog \
--cacert ch4/certs/2_intermediate/certs/ca-chain.cert.pem
* Host localhost:30005 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:30005...
* connect to ::1 port 30005 from ::1 port 54182 failed: Connection refused
*   Trying 127.0.0.1:30005...
* Connected to localhost (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
* Closing connection
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:30005
  • 그런데 여전히 실패합니다! 이유는 요청이 localhost로 보내졌기 때문입니다. 서버 인증서는 webapp.istioinaction.io를 위해 발급된 것이므로, 호스트 이름이 일치하지 않습니다. (localhost로 호출함)
  • 도메인 질의를 위한 임시 설정
    webapp.istioinaction.io로 접속하면 내 로컬 머신(127.0.0.1) 으로 연결되도록 설정
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ echo "127.0.0.1       webapp.istioinaction.io" | sudo tee -a /etc/hosts
[sudo] password for ssoon:
127.0.0.1       webapp.istioinaction.io

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat /etc/hosts | tail -n 1
127.0.0.1       webapp.istioinaction.io
  • JSON 데이터를 성공적으로 받을 수 있습니다!
    클라이언트는 CA 인증서를 통해 서버의 신원을 확인하고, 트래픽을 암호화해 안전하게 통신했습니다.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v https://webapp.istioinaction.io:30005/api/catalog \
--cacert ch4/certs/2_intermediate/certs/ca-chain.cert.pem
* Host webapp.istioinaction.io:30005 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30005...
* Connected to webapp.istioinaction.io (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=webapp.istioinaction.io
*  start date: Jul  4 12:49:32 2021 GMT
*  expire date: Jun 29 12:49:32 2041 GMT
*  common name: webapp.istioinaction.io (matched)
*  issuer: C=US; ST=Denial; O=Dis; CN=webapp.istioinaction.io
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://webapp.istioinaction.io:30005/api/catalog
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: webapp.istioinaction.io:30005]
* [HTTP/2] [1] [:path: /api/catalog]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/catalog HTTP/2
> Host: webapp.istioinaction.io:30005
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
< content-length: 357
< content-type: application/json; charset=utf-8
< date: Sat, 19 Apr 2025 06:45:40 GMT
< x-envoy-upstream-service-time: 14
< server: istio-envoy
<
* Connection #0 to host webapp.istioinaction.io left intact
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]

HTTPS 요청이 성공하려면 올바른 CA 인증서와 호스트 이름 매핑이 필요합니다.


🔐 트래픽 암호화의 전체 그림

이제 우리는 외부 클라이언트부터 istio-ingressgateway까지 트래픽을 암호화했습니다. Gateway는 TLS 연결을 종료하고, 이후 webapp 서비스로 트래픽을 전달합니다. 현재 Gateway와 webapp 서비스 간 트래픽은 암호화되지 않았지만, 서비스 메시 내부의 암호화는 나중에 챕터 9에서 다룰 예정입니다.

외부 클라이언트 → Istio Gateway (TLS 종료) → webapp 서비스 (암호화되지 않음)

TLS를 설정해 외부 트래픽을 암호화했으며, Gateway에서 TLS를 종료해 안전한 통신을 구현했습니다.
내부 트래픽 암호화는 다음 단계에서 탐구합니다.

프로덕션 환경에서는 cert-manager 같은 도구를 사용해 외부 CA나 내부 PKI와 인증서 워크플로우를 통합하는 것이 좋습니다.


🎯 다음 단계는?

이제 Istio Gateway에서 TLS를 설정해 HTTPS 트래픽을 안전하게 처리하는 방법을 배웠습니다. 인증서와 비밀 키를 Secret에 저장하고, Gateway를 설정하며, curl로 HTTPS 요청을 테스트하는 과정을 통해 트래픽 암호화의 중요성을 이해했죠. 다음 단계에서는 서비스 메시 내부 트래픽의 암호화나, 비 HTTP 트래픽 처리 방법 등 Istio의 고급 보안 기능을 탐구해보세요.


🔐 2. Istio Gateway에서 HTTP 트래픽을 HTTPS로 강제 리다이렉트하기

이전 가이드에서 Istio Gateway에 TLS를 설정해 HTTPS 트래픽을 안전하게 처리하는 방법을 배웠습니다. 하지만 사용자가 실수로 HTTP(http://)로 접속한다면? 보안을 강화하기 위해 모든 트래픽을 HTTPS(https://)로 강제하는 방법을 알아보겠습니다! 


🔄 HTTP를 HTTPS로 리다이렉트 설정하기

기본적으로 Istio Gateway는 HTTP와 HTTPS를 모두 지원하도록 설정할 수 있습니다. 하지만 보안을 위해 모든 트래픽을 HTTPS로 강제하려면 Gateway 리소스에 httpsRedirect 옵션을 추가해야 합니다. 아래는 HTTP 트래픽을 HTTPS로 리다이렉트하도록 수정한 Gateway 설정 예제입니다:

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat ch4/coolstore-gw-tls-redirect.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
    tls:
      httpsRedirect: true
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"
  • 이 설정은 다음과 같은 작업을 수행합니다:
    • 포트 80에서 HTTP 트래픽을 수신하지만, tls.httpsRedirect: true 를 통해 HTTP 요청을 HTTPS로 리다이렉트합니다.
    • 포트 443에서는 HTTPS 트래픽을 처리하며, webapp-credential Secret을 사용해 TLS를 설정합니다.

httpsRedirect: true는 HTTP 포트(80)로 들어오는 모든 트래픽을
HTTPS 포트(443)로 리다이렉트하도록 Gateway에 지시합니다.


🛠️ Gateway 리소스 업데이트하기

  • 이제 수정된 Gateway 설정을 클러스터에 적용합니다. 다음 명령어를 실행해 리다이렉트 설정을 반영합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl apply -f ch4/coolstore-gw-tls-redirect.yaml
gateway.networking.istio.io/coolstore-gateway created
  • 이 명령어는 coolstore-gateway를 새로운 설정으로 업데이트합니다. 적용이 완료되면 모든 HTTP 트래픽이 HTTPS로 리다이렉트됩니다.

kubectl apply를 사용해 Gateway 리소스를 업데이트하면, Istio가 새로운 리다이렉트 설정을 즉시 반영합니다.


🔍 리다이렉트 동작 테스트하기

  • 리다이렉트 설정이 제대로 작동하는지 확인하기 위해 HTTP로 요청을 보내보겠습니다. 
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v http://webapp.istioinaction.io:30000/api/catalog
* Host webapp.istioinaction.io:30000 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30000...
* Connected to webapp.istioinaction.io (127.0.0.1) port 30000
> GET /api/catalog HTTP/1.1
> Host: webapp.istioinaction.io:30000
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< location: https://webapp.istioinaction.io:30000/api/catalog
< date: Sat, 19 Apr 2025 07:08:44 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host webapp.istioinaction.io left intact
  • 이 출력은 HTTP 요청이 301 Moved Permanently 응답과 함께
    HTTPS URL(https://webapp.istioinaction.io/api/catalog)로 리다이렉트되었음을 보여줍니다.
    이제 클라이언트는 HTTPS로 요청을 다시 보내야 합니다.

HTTP 요청이 HTTPS로 리다이렉트되며, 모든 트래픽이 암호화된 채로 Gateway를 통해 처리됩니다.


🎯 트래픽 보안 강화 완료!

이제 Istio Gateway는 모든 트래픽을 HTTPS로 강제해, HTTP를 통한 비보안 접속을 차단합니다. httpsRedirect 설정 덕분에 사용자가 실수로 HTTP를 사용하더라도 자동으로 HTTPS로 전환되므로 서비스 메시로 들어오는 모든 트래픽이 안전하게 암호화됩니다.


🔐 3. Istio Gateway에서 상호 TLS(mTLS)로 클라이언트와 서버 인증하기

이전 가이드에서는 TLS를 설정해 서버의 신원을 클라이언트에게 증명하는 방법을 배웠습니다. 하지만 클라이언트의 신원도 확인하고 싶다면 어떨까요? **상호 TLS(mTLS)**를 사용하면 서버와 클라이언트가 서로의 인증서를 검증해 더 강력한 보안을 구현할 수 있습니다. 이번 가이드에서는 Istio Gateway에서 mTLS를 설정해 외부 트래픽의 클라이언트를 인증하는 과정을 설명합니다. 


🛡️ 상호 TLS(mTLS)로 양방향 인증 구현하기

일반 TLS에서는 서버가 자신의 인증서를 클라이언트에게 제시해 신원을 증명합니다. 하지만 mTLS는 한 단계 더 나아가, 클라이언트도 자신의 인증서를 서버에 제시해 신원을 증명합니다. 서버는 클라이언트의 인증서를 신뢰할 수 있는 **CA(Certificate Authority)**가 서명했는지 확인합니다. 이 과정에서 양쪽 모두 인증서를 교환하고 트래픽을 암호화해 안전한 통신을 보장합니다.

클라이언트 ↔ 서버: 서로 인증서를 교환하고 CA로 검증한 뒤, 암호화된 트래픽 교환

mTLS는 클라이언트와 서버가 서로를 인증해 보안을 강화합니다.
양쪽 모두 신뢰할 수 있는 CA가 서명한 인증서를 사용해야 합니다.


🔑 Kubernetes Secret으로 CA 인증서 준비하기

  • mTLS를 설정하려면 istio-ingressgateway가 클라이언트의 인증서를 검증할 수 있도록 CA 인증서(또는 인증서 체인)를 제공해야 합니다. 이를 위해 Kubernetes Secret에 서버 인증서, 비밀 키, 그리고 CA 인증서를 저장합니다.
  • 다음 명령어를 실행해 webapp-credential-mtls 라는 Secret을 생성합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl create -n istio-system secret \
generic webapp-credential-mtls --from-file=tls.key=\
ch4/certs/3_application/private/webapp.istioinaction.io.key.pem \
--from-file=tls.crt=\
ch4/certs/3_application/certs/webapp.istioinaction.io.cert.pem \
--from-file=ca.crt=\
ch4/certs/2_intermediate/certs/ca-chain.cert.pem
secret/webapp-credential-mtls created
  • 이 명령어는 istio-system 네임스페이스에 Secret을 생성하며, 다음과 같은 파일을 포함합니다:
    • tls.key: 서버의 비밀 키
    • tls.crt: 서버의 인증서
    • ca.crt: 클라이언트 인증서를 검증할 CA 인증서 체인

mTLS를 위해 Secret에 서버 인증서, 비밀 키, 그리고 클라이언트 검증용 CA 인증서를 저장합니다.
이 Secret은 istio-ingressgateway와 동일한 네임스페이스에 있어야 합니다.


🌐 Gateway에서 mTLS 설정하기

  • 이제 CA 인증서가 준비되었으니, Gateway 리소스를 수정해 mTLS를 활성화합니다.
    아래는 HTTP(포트 80)와 HTTPS(포트 443)에 mTLS를 적용한 Gateway 설정 예제입니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat ch4/coolstore-gw-mtls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      credentialName: webapp-credential-mtls
    hosts:
    - "webapp.istioinaction.io"
  • 이 설정은 다음과 같은 작업을 수행합니다:
    • 포트 80에서는 HTTP 트래픽을 수신합니다(비암호화).
    • 포트 443에서는 HTTPS 트래픽을 처리하며, tls.mode: MUTUAL을 통해 mTLS를 활성화합니다.
    • credentialName: webapp-credential-mtls는 서버 인증서와 CA 인증서를 포함한 Secret을 지정합니다.
  • 이제 이 Gateway 리소스를 적용합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl apply -f ch4/coolstore-gw-mtls.yaml -n istioinaction
gateway.networking.istio.io/coolstore-gateway configured

 

tls.mode: MUTUAL은 mTLS를 활성화해 클라이언트와 서버가 서로 인증서를 검증하도록 설정합니다.

 

참고: Istio Gateway SDS

Istio Gateway는 **SDS(Secret Discovery Service)**를 통해 인증서를 동적으로 가져옵니다.
SDS는 istio-agent 프로세스에 내장되어 있으며, Secret 업데이트를 자동으로 반영합니다. 

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ docker exec -it myk8s-control-plane istioctl proxy-config secret deploy/istio-ingressgateway.istio-system
RESOURCE NAME                                  TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
default                                        Cert Chain     ACTIVE     true           270634571266688350616141726413897844282     2025-04-20T04:40:19Z     2025-04-19T04:38:19Z
kubernetes://webapp-credential-mtls            Cert Chain     ACTIVE     true           1049106                                     2041-06-29T12:49:32Z     2021-07-04T12:49:32Z
kubernetes://webapp-credential-mtls-cacert     CA             ACTIVE     true           1049106                                     2041-06-29T12:49:29Z     2021-07-04T12:49:29Z
ROOTCA                                         CA             ACTIVE     true           59577252112357099295490927856197578583      2035-04-17T04:40:09Z     2025-04-19T04:40:09Z

 


🔍 mTLS 트래픽 테스트하기

  • mTLS 설정이 완료되었으니, HTTPS 요청을 보내 테스트합니다.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v https://webapp.istioinaction.io:30005/api/catalog \
--cacert ch4/certs/2_intermediate/certs/ca-chain.cert.pem
* Host webapp.istioinaction.io:30005 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30005...
* Connected to webapp.istioinaction.io (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=webapp.istioinaction.io
*  start date: Jul  4 12:49:32 2021 GMT
*  expire date: Jun 29 12:49:32 2041 GMT
*  common name: webapp.istioinaction.io (matched)
*  issuer: C=US; ST=Denial; O=Dis; CN=webapp.istioinaction.io
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://webapp.istioinaction.io:30005/api/catalog
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: webapp.istioinaction.io:30005]
* [HTTP/2] [1] [:path: /api/catalog]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/catalog HTTP/2
> Host: webapp.istioinaction.io:30005
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS alert, unknown (628):
* OpenSSL SSL_read: OpenSSL/3.0.13: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0
* Failed receiving HTTP2 data: 56(Failure when receiving data from the peer)
* Connection #0 to host webapp.istioinaction.io left intact
curl: (56) OpenSSL SSL_read: OpenSSL/3.0.13: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0
  • 이 요청은 실패합니다:
* Connection #0 to host webapp.istioinaction.io left intact
curl: (56) OpenSSL SSL_read: OpenSSL/3.0.13: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0
  • 실패 원인은 클라이언트가 자신의 인증서를 제공하지 않았기 때문입니다.
    mTLS에서는 클라이언트도 서버에 인증서를 제시해야 하므로, curl에 클라이언트 인증서와 비밀 키를 추가해야 합니다.
  • 다음 명령어를 실행합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v https://webapp.istioinaction.io:30005/api/catalog \
--cacert ch4/certs/2_intermediate/certs/ca-chain.cert.pem \
--cert ch4/certs/4_client/certs/webapp.istioinaction.io.cert.pem \
--key ch4/certs/4_client/private/webapp.istioinaction.io.key.pem
* Host webapp.istioinaction.io:30005 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30005...
* Connected to webapp.istioinaction.io (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, CERT verify (15):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=webapp.istioinaction.io
*  start date: Jul  4 12:49:32 2021 GMT
*  expire date: Jun 29 12:49:32 2041 GMT
*  common name: webapp.istioinaction.io (matched)
*  issuer: C=US; ST=Denial; O=Dis; CN=webapp.istioinaction.io
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://webapp.istioinaction.io:30005/api/catalog
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: webapp.istioinaction.io:30005]
* [HTTP/2] [1] [:path: /api/catalog]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/catalog HTTP/2
> Host: webapp.istioinaction.io:30005
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
< content-length: 357
< content-type: application/json; charset=utf-8
< date: Sat, 19 Apr 2025 07:24:02 GMT
< x-envoy-upstream-service-time: 15
< server: istio-envoy
<
* Connection #0 to host webapp.istioinaction.io left intact
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
  • JSON 데이터(제품 목록)를 성공적으로 받을 수 있습니다!
    클라이언트는 서버의 인증서를 검증하고, 서버는 클라이언트의 인증서를 검증해 mTLS를 완성했습니다.

mTLS 요청이 성공하려면 클라이언트가 자신의 인증서(--cert)와 비밀 키(--key)를 제공해야 합니다.
이를 통해 양방향 인증이 완료됩니다.


🎯 mTLS로 보안 강화 완료!

이제 Istio Gateway에서 mTLS를 설정해 클라이언트와 서버가 서로를 인증하도록 만들었습니다. 이를 통해 외부 트래픽의 출처를 확인하고, 모든 통신을 암호화해 보안을 한층 강화했습니다. mTLS는 특히 민감한 데이터를 다루는 서비스에서 강력한 보안 메커니즘으로 활용됩니다.


🌐 4. Istio Gateway로 여러 Virtual Host를 TLS로 안전하게 제공하기

Istio의 Gateway는 단일 HTTPS 포트(443)를 통해 여러 virtual host를 처리할 수 있는 강력한 기능을 제공합니다. 이를 통해 서로 다른 서비스(예: webapp.istioinaction.io와 catalog.istioinaction.io)를 각각의 인증서와 비밀 키로 안전하게 제공할 수 있습니다. 이번 가이드에서는 Istio Gateway에서 다중 virtual host를 TLS로 설정하는 방법을 설명합니다.


🔒 단일 포트로 다중 Virtual Host 처리하기

  • Istio Gateway는 포트 443에서 여러 virtual host를 처리할 수 있도록 설계되었습니다.
    이를 위해 동일한 포트와 프로토콜(HTTPS)을 사용하되, 각 virtual host마다 고유한 인증서와 비밀 키를 지정합니다.
  • 아래는 webapp.istioinaction.io와 catalog.istioinaction.io를 각각의 인증서로 제공하는 Gateway 설정 예제입니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat ch4/coolstore-gw-multi-tls.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https-webapp
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https-catalog
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: catalog-credential
    hosts:
    - "catalog.istioinaction.io"
  • 이 설정은 다음과 같은 작업을 수행합니다:
    • 동일한 포트 443에서 HTTPS 프로토콜로 두 virtual host를 처리합니다.
    • https-webappwebapp-credential Secret을 사용해 webapp.istioinaction.io를 제공합니다.
    • https-catalog catalog-credential Secret을 사용해 catalog.istioinaction.io를 제공합니다.

단일 포트(443)에서 여러 virtual host를 처리하려면 각 host마다 고유한 인증서와 비밀 키를 지정해야 합니다.
이름(https-webapp, https-catalog)으로 구분합니다.


🔐 Catalog 서비스용 인증서 Secret 생성하기

  • catalog.istioinaction.io를 위한 인증서와 비밀 키를 Kubernetes Secret에 저장합니다.
  • 다음 명령어를 실행해 catalog-credential Secret을 생성합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl create -n istio-system secret tls catalog-credential \
--key ch4/certs2/3_application/private/catalog.istioinaction.io.key.pem \
--cert ch4/certs2/3_application/certs/catalog.istioinaction.io.cert.pem
secret/catalog-credential created
  • 이 명령어는 istio-system 네임스페이스에 catalog-credential Secret을 생성하며, catalog.istioinaction.io용 비밀 키와 인증서를 포함합니다.

각 virtual host는 고유한 Secret을 사용해 인증서와 비밀 키를 제공합니다.
Secret은 istio-ingressgateway와 동일한 네임스페이스에 저장해야 합니다.


🛠️ Gateway와 VirtualService 업데이트하기

  • 이제 Gateway 설정을 적용합니다. 다음 명령어를 실행해 다중 virtual host를 지원하는 Gateway를 업데이트합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl apply -f ch4/coolstore-gw-multi-tls.yaml -n istioinaction
gateway.networking.istio.io/coolstore-gateway configured
  • 또한, catalog.istioinaction.io를 위한 VirtualService 리소스를 추가해 트래픽을 올바른 서비스로 라우팅합니다:
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat ch4/catalog-vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog-vs-from-gw
spec:
  hosts:
  - "catalog.istioinaction.io"
  gateways:
  - coolstore-gateway
  http:
  - route:
    - destination:
        host: catalog
        port:
          number: 80(

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ kubectl apply -f ch4/catalog-vs.yaml -n istioinaction
virtualservice.networking.istio.io/catalog-vs-from-gw created

 

  • 도메인 질의를 위한 임시 설정
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ echo "127.0.0.1       catalog.istioinaction.io" | sudo tee -a /etc/hosts
[sudo] password for ssoon:
127.0.0.1       catalog.istioinaction.io

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ cat /etc/hosts | tail -n 2
127.0.0.1       webapp.istioinaction.io
127.0.0.1       catalog.istioinaction.io

Gateway는 virtual host를 정의하고, VirtualService는 각 host의 트래픽을 특정 서비스로 라우팅합니다.
두 리소스가 함께 작동해 트래픽 흐름을 완성합니다.


🔍 다중 Virtual Host 테스트하기

  • 두 virtual host(webapp.istioinaction.io와 catalog.istioinaction.io)로 HTTPS 요청을 보내 테스트합니다.

1. webapp.istioinaction.io 테스트

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v https://webapp.istioinaction.io:30005/api/catalog \
--cacert ch4/certs/2_intermediate/certs/ca-chain.cert.pem
* Host webapp.istioinaction.io:30005 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30005...
* Connected to webapp.istioinaction.io (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=webapp.istioinaction.io
*  start date: Jul  4 12:49:32 2021 GMT
*  expire date: Jun 29 12:49:32 2041 GMT
*  common name: webapp.istioinaction.io (matched)
*  issuer: C=US; ST=Denial; O=Dis; CN=webapp.istioinaction.io
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://webapp.istioinaction.io:30005/api/catalog
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: webapp.istioinaction.io:30005]
* [HTTP/2] [1] [:path: /api/catalog]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/catalog HTTP/2
> Host: webapp.istioinaction.io:30005
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
< content-length: 357
< content-type: application/json; charset=utf-8
< date: Sat, 19 Apr 2025 07:35:03 GMT
< x-envoy-upstream-service-time: 5
< server: istio-envoy
<
* Connection #0 to host webapp.istioinaction.io left intact
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
  • HTTPS 연결이 잘 되었고, 정상적인 API 응답 JSON 데이터를 반환합니다.

2. catalog.istioinaction.io 테스트

(⎈|kind-myk8s:N/A) ssoon@DESKTOP-UQRJB87:~/istio-in-action/book-source-code-master$ curl -v https://catalog.istioinaction.io:30005/items \
--cacert ch4/certs2/2_intermediate/certs/ca-chain.cert.pem
* Host catalog.istioinaction.io:30005 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:30005...
* Connected to catalog.istioinaction.io (127.0.0.1) port 30005
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: ch4/certs2/2_intermediate/certs/ca-chain.cert.pem
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
*  subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=catalog.istioinaction.io
*  start date: Jul  4 13:30:38 2021 GMT
*  expire date: Jun 29 13:30:38 2041 GMT
*  common name: catalog.istioinaction.io (matched)
*  issuer: C=US; ST=Denial; O=Dis; CN=catalog.istioinaction.io
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://catalog.istioinaction.io:30005/items
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: catalog.istioinaction.io:30005]
* [HTTP/2] [1] [:path: /items]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET /items HTTP/2
> Host: catalog.istioinaction.io:30005
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
< x-powered-by: Express
< vary: Origin, Accept-Encoding
< access-control-allow-credentials: true
< cache-control: no-cache
< pragma: no-cache
< expires: -1
< content-type: application/json; charset=utf-8
< content-length: 502
< etag: W/"1f6-ih2h+hDQ0yLLcKIlBvwkWbyQGK4"
< date: Sat, 19 Apr 2025 07:37:22 GMT
< x-envoy-upstream-service-time: 14
< server: istio-envoy
<
[
  {
    "id": 1,
    "color": "amber",
    "department": "Eyewear",
    "name": "Elinor Glasses",
    "price": "282.00"
  },
  {
    "id": 2,
    "color": "cyan",
    "department": "Clothing",
    "name": "Atlas Shirt",
    "price": "127.00"
  },
  {
    "id": 3,
    "color": "teal",
    "department": "Clothing",
    "name": "Small Metal Shoes",
    "price": "232.00"
  },
  {
    "id": 4,
    "color": "red",
    "department": "Watches",
    "name": "Red Dragon Watch",
    "price": "232.00"
  }
* Connection #0 to host catalog.istioinaction.io left intact
]
  • HTTPS 연결이 잘 되었고, 정상적인 API 응답 JSON 데이터를 반환합니다.
  • 두 요청 모두 동일한 포트(443)를 사용하지만, 서로 다른 인증서로 처리됩니다.

두 virtual host는 동일한 포트(443)를 공유하지만, 고유한 인증서와 VirtualService로 독립적으로 처리됩니다.


🧠 SNI(Server Name Indication)로 인증서 구분하기

단일 포트에서 여러 virtual host를 어떻게 구분할까요? 비밀은 **SNI(Server Name Indication)**라는 TLS 확장에 있습니다. SNI를 통해 클라이언트는 TLS 핸드셰이크의 ClientHello 단계에서 접속하려는 서비스의 호스트 이름(예: webapp.istioinaction.io)을 명시합니다. Istio Gateway(정확히는 Envoy)는 이 정보를 바탕으로 올바른 인증서를 선택하고 트래픽을 해당 서비스로 라우팅합니다.

SNI는 클라이언트가 요청한 호스트 이름을 TLS 핸드셰이크에서 전달해,
Gateway가 올바른 인증서와 서비스를 선택하도록 합니다.


🎯 다중 Virtual Host로 유연한 TLS 구현 완료!

이제 Istio Gateway에서 단일 포트(443)를 사용해 여러 virtual host를 TLS로 안전하게 제공하는 방법을 배웠습니다. webapp.istioinaction.io와 catalog.istioinaction.io를 각각의 인증서로 처리하며, SNI를 활용해 트래픽을 정확히 라우팅했습니다. 이를 통해 복잡한 멀티 도메인 환경에서도 효율적이고 안전한 트래픽 관리가 가능해졌습니다.

Comments