기본 TLS 인증서
기본 TLS 인증서 (Default TLS Certificate)
기본 TLS 인증서는 SNI 헤더에 지정된 호스트 이름과 일치하는 다른 인증서가 없거나 SNI가 없을 때 폴백(fallback)으로 동작해요. Gateway API 스펙은 기본 TLS 인증서를 기본적으로 지원하지 않지만, spec.listeners[].hostname 필드가 비어 있는 리스너가 모든 호스트 이름과 일치하면서 해당 프로토콜과 포트에 대한 폴백 역할을 해요.
본문
기본 TLS 인증서는 SNI 헤더에 지정된 호스트 이름과 일치하는 다른 인증서가 없거나 SNI가 없을 때 폴백으로 동작해요. Gateway API 스펙은 기본 TLS 인증서를 기본적으로 지원하지 않아요. 대신 spec.listeners[].hostname 필드가 빈 리스너가 모든 호스트 이름과 일치하면서 해당 프로토콜과 포트에 대한 폴백 역할을 해요.
이 예제에서는 간단한 HTTP 서비스를 배포하고 Cilium Gateway API를 통해 노출해볼게요.
Istio 프로젝트의 bookinfo 샘플 애플리케이션을 사용할 거예요.
데모 앱 배포하기 (Deploy the Demo App)
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.11/samples/bookinfo/platform/kube/bookinfo.yaml
이것은 데모 앱을 배포하는 것뿐이며, Istio 컴포넌트를 추가하는 게 아니에요. Cilium Service Mesh에서는 데모 앱 각 마이크로서비스와 함께 Envoy 사이드카가 생성되지 않는 것을 확인할 수 있어요.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-5498c86cf5-kjzkj 1/1 Running 0 2m39s
productpage-v1-65b75f6885-ff59g 1/1 Running 0 2m39s
ratings-v1-b477cf6cf-kv7bh 1/1 Running 0 2m39s
reviews-v1-79d546878f-r5bjz 1/1 Running 0 2m39s
reviews-v2-548c57f459-pld2f 1/1 Running 0 2m39s
reviews-v3-6dd79655b9-nhrnh 1/1 Running 0 2m39s
참고
사이드카 구현이었다면 출력이 2/2 READY로 보였을 거예요. 하나는 마이크로서비스, 하나는 Envoy 사이드카 때문이에요.
TLS 인증서와 개인 키 생성하기 (Create TLS Certificate and Private Key)
데모 목적으로는 만들어낸 셀프 사이닝 CA(인증 기관)가 서명한 TLS 인증서를 사용해볼게요. 손쉬운 방법 중 하나는 mkcert를 사용하는 거예요. 이 예제에서 사용하는 호스트 이름인 bookinfo.cilium.rocks와 hipstershop.cilium.rocks를 검증할 인증서가 필요해요.
$ mkcert bookinfo.cilium.rocks hipstershop.cilium.rocks
Note: the local CA is not installed in the system trust store.
Run "mkcert -install" for certificates to be trusted automatically ⚠️
Created a new certificate valid for the following names 📜
- "bookinfo.cilium.rocks"
- "hipstershop.cilium.rocks"
The certificate is at "./bookinfo.cilium.rocks+1.pem" and the key at "./bookinfo.cilium.rocks+1-key.pem" ✅
It will expire on 29 November 2026 🗓
이 데모 키와 인증서로 Kubernetes 시크릿을 만들어볼게요:
$ kubectl create secret tls demo-cert --key=bookinfo.cilium.rocks+1-key.pem --cert=bookinfo.cilium.rocks+1.pem
(대안) cert-manager를 설치해볼게요:
$ helm repo add jetstack https://charts.jetstack.io
$ helm install cert-manager jetstack/cert-manager --version v1.16.2 \
--namespace cert-manager \
--set crds.enabled=true \
--create-namespace \
--set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \
--set config.kind="ControllerConfiguration" \
--set config.enableGatewayAPI=true
이제 CA Issuer를 만들어볼게요:
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/servicemesh/ca-issuer.yaml
Gateway와 HTTPRoutes 배포하기 (Deploy the Gateway and HTTPRoutes)
이 예제에서는 단일 인증서가 모든 인입 TLS 연결에 제공되고, 이후 HTTPRoute에 정의된 호스트 이름에 따라 적절한 백엔드로 라우팅돼요.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: default
protocol: HTTPS
port: 443
tls:
certificateRefs:
- kind: Secret
name: demo-cert
mode: Terminate
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bookinfo
spec:
parentRefs:
- name: tls-gateway
sectionName: default
hostnames:
- "bookinfo.cilium.rocks"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: details
port: 9080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: hipstershop
spec:
parentRefs:
- name: tls-gateway
sectionName: default
hostnames:
- "hipstershop.cilium.rocks"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: productpage
port: 9080
구성을 적용해볼게요:
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/kubernetes/gateway/https-default-tls-certificate.yaml
Gateway와 HTTPRoutes가 배포되면 Gateway에서 외부 IP 주소를 확인하고, HTTPRoute의 호스트 이름이 유효한지 확인할 수 있어요.
$ kubectl get gateway tls-gateway
NAME CLASS ADDRESS PROGRAMMED AGE
tls-gateway cilium 172.18.255.200 True 27m
$ kubectl get httproute bookinfo hipstershop
NAME HOSTNAMES AGE
bookinfo ["bookinfo.cilium.rocks"] 27m
hipstershop ["hipstershop.cilium.rocks"] 27m
HTTPS 요청 보내기 (Make HTTPS Requests)
curl 요청에 CA의 인증서를 지정하면 해당 CA가 서명한 인증서를 신뢰한다는 뜻이 돼요.
$ curl --cacert minica.pem -v https://bookinfo.cilium.rocks/details/1 --resolve bookinfo.cilium.rocks:443:172.18.255.200
$ curl --cacert minica.pem -v https://hipstershop.cilium.rocks/ --resolve hipstershop.cilium.rocks:443:172.18.255.200
...
* subjectAltName: "bookinfo.cilium.rocks" matches cert's "bookinfo.cilium.rocks"
* SSL certificate verified via OpenSSL.
TLS 인증서가 요청한 호스트 이름으로 발급됐으므로 경고 메시지가 보이지 않아야 해요.
다음 요청은 IP 주소로 서비스에 접근할 때 클라이언트가 SNI 헤더를 제공하지 않는다는 점을 보여줘요. 이는 보통 Cilium이 IP 주소로 연결을 맺는 로드 밸런서나 프록시의 백엔드로 동작할 때 발생해요. SNI가 없어도 기본 인증서로 TLS 연결은 여전히 수립돼요. 인증서의 호스트 이름이 IP 주소와 일치하지 않으므로, 이 예제에서는 검증을 우회하기 위해 curl에서 -k 플래그를 사용한다는 점을 주목해주세요.
이후 올바른 백엔드로의 라우팅은 Host HTTP 헤더로 결정되는데, 이 예제에서는 이를 명시적으로 제공해요.
$ curl -H "Host: hipstershop.cilium.rocks" -k -v https://172.18.255.200/
...
* SSL certificate verification failed, continuing anyway!
* Established connection to 172.18.255.200 (172.18.255.200 port 443) from 10.244.0.52 port 42458
curl 요청에서 -v를 지정하면 TLS 핸드셰이크가 성공한 것을 볼 수 있어요.
더 알아보기 (Learn more)
- Gateway API (HTTPS) — HTTPS Gateway 예제
- TLS 인증서 생성하기 — TLS 인증서와 개인 키 생성 방법
- Cilium Gateway API — Cilium Gateway API 개요