Istio Gateway에서 HTTPS 인증서 공유할 때 발생하는 HTTP/2 Connection Reuse 문제
여러 Istio Gateway에서 인증서를 공유할 때 간헐적으로 브라우저에서 404 오류가 발생할 수 있다. HTTP/2 Connection Reuse로 인해 발생하는 문제로, Gateway를 합치거나 인증서를 분리하여 해결할 수 있다.
Istio ingress gateway를 L7 reverse proxy로 사용하면 하나의 IP에서 여러 서비스를 운영할 수 있다. 그런데 Gateway에서 TLS termination을 할 때 인증서와 gateway 설정이 잘못되면 문제가 발생할 수 있다.
증상
- 재현 조건: 여러 Istio Gateway resource가 하나의 IP를 공유하고, 하나의 인증서로 TLS termination을 한다.
- 증상: 브라우저에서 각 Gateway의 서비스를 옮겨다닐 때 간헐적으로 일부 서비스에서 404 응답이 온다. 이 응답은 어플리케이션에서 응답하는 게 아닌 istio에서 응답한다. 그러나 curl과 같이 터미널에서 접근하면 성공한다.
설정 예시 manifest
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: a
namespace: default
spec:
selector:
app: gateway-controller
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- a.example.com
tls:
mode: SIMPLE
credentialName: wildcard-cert
---
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: b
namespace: default
spec:
selector:
app: gateway-controller
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- b.example.com
tls:
mode: SIMPLE
credentialName: wildcard-cert # a gateway와 인증서를 공유한다.
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
namespace: default
spec:
secretName: wildcard-cert
dnsNames:
# a, b gateway에서 모두 사용할 수 있도록 wildcard로 발급한다.
- "*.example.com"
issuerRef:
name: letsencrypt
kind: ClusterIssuer원인
HTTP2에서는 TLS connection을 재사용할 수 있는데, TLS 설정이 된 경우 서버 IP와 인증서에 따라 connection을 재사용할지 결정한다. RFC 7540에 따르면 서버 IP가 같고 인증서의 도메인(CN, SAN)이 호환되면 호출을 할 때 이전의 connection을 재사용할 수 있다. 예를 들어 서버 IP가 같고 인증서의 subjectAltName에 *.example.com가 있다면 하나의 connection을 a.example.com과 b.example.com 모두에 사용할 수 있게된다.
For TCP connections without TLS, this depends on the host having resolved to the same IP address. For https resources, connection reuse additionally depends on having a certificate that is valid for the host in the URI. The certificate presented by the server MUST satisfy any checks that the client would perform when forming a new TLS connection for the host in the URI.
ref. RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)
만약 위에 적은 설정 예시처럼 a.example.com과 b.example.com의 Gateway resource는 분리했지만 같은 istio ingressgateway에 설정하여 IP가 같다고 하자. 만약 브라우저에서 a.example.com으로 연결한 후 b.example.com에 호출을 한다면 이전 connection과 IP가 같고, certificate도 *.example.com이므로 호환되기 때문에 이 connection을 재사용하려 한다. 그러나 a.example.com Gateway는 b.example.com route가 없으므로 404 응답을 주게 되는 것이다.
Since both gateways are served by the same workload (i.e., selector
istio: ingressgateway) requests to both services (service1.test.comandservice2.test.com) will resolve to the same IP. Ifservice1.test.comis accessed first, it will return the wildcard certificate (*.test.com) indicating that connections toservice2.test.comcan use the same certificate. Browsers like Chrome and Firefox will consequently reuse the existing connection for requests toservice2.test.com. Since the gateway (gw1) has no route forservice2.test.com, it will then return a 404 (Not Found) response.
반면, curl의 경우 기본적으로 요청마다 connection을 새로 맺어 재현이 안되었던 것이다.
해결 방법
두 Istio Gateway를 하나로 합치거나 두 Gateway에서 지원하는 호스트만 도메인에 지원하도록 인증서를 발급하고 분리하여 해결할 수 있다. 보통 인증서를 발급받아서 교체하는 것보다는 Gateway를 합치는 방법이 더 간단하다.