A few months ago I contributed experimental HTTP/3 support at the Istio ingress gateway (PR). For the HTTPS gateway servers which terminate the TLS at the gateway, it automatically adds a mirror HTTP/3 listener over QUIC provided that there is an open UDP port. If there is an HTTPS server on TCP port 443, then an HTTP/3 server is automatically created on UDP port 443 as illustrated in the diagram below. All you need to do is to flip a switch on pilot and open UDP ports.
Contents
Prerequistes
HTTP/3 over QUIC is so new as of writing this. QUIC was standardized this May end and HTTP/3 is not yet standardized (There is an IETF draft here). So creating custom builds, turning on alpha features are expected. I’m running Linux.
Kubernetes
This feature requires the supporting both TCP and UDP on the same port at the gateway. By
default for Kubernetes services of type LoadBalancer
this is not allowed. Since Kubernetes
1.20 there is alpha support. As documented here, it can be turned on with MixedProtocolLBSupport
feature gate.
For this demo, I use Kind to provision a cluster with the following config
|
|
For the load balancer support, I’m using MetalLB with the following configuration. For setup and usage instructions, please refer to their documentation.
cURL
Testing the setup requires support for --http3
flag. If it is not supported out of the box,
then you have to build it with Cloudflare Quiche support as documented here.
Istio
As of writing this Istio 1.12 is not yet released. So the pilot and proxy images should be built from the master branch.
Setup
Demo setup consists of
- Setting up Istio
- Deploying httpbin application
- Setting up gateway TLS certificates
- Configuring ingress gateway
Here is the demo setup
Setting up Istio
There are two important things
- Set
PILOT_ENABLE_QUIC_LISTENERS
environment variable totrue
on Istiod to turn on generating HTTP/3 mirror listener on the gateway - Exposing both 443/UDP and 443/TCP - same port, different transport protocol
Here is an example IstioOperator
spec.
|
|
Then install Istio
|
|
Deploying httpbin
I am using the sample httpbin provided in the main Istio repository (Link). Deploy it with good old kubectl
. Make sure that Istio sidecars are injected.
You can turn on istio sidecar injection at the namespace level by labeling it istio-injection=enabled
or if you are using revisions (please use it to make upgrades smoother), then it is istio.io/rev=<revision-name>
.
|
|
Setting up TLS certificates for the gateway
HTTP/3 is over TLS only. In fact, using TLS is baked into QUIC, the underlying transport protocol as described in RFC-9000 and RFC-9001. So we need to generate and install certificates. For the demo, I’m using self-signed certificates.
- Parameters for the certificate (Like SAN). Save it as
httpbin.cfg
|
|
- Generate TLS certificates and keys with the following script. Here, I’m using
openssl
. You can use other tools likecfssl
as well.
|
|
- Install the certificates
|
|
Istio configuration
Remember, HTTP/3 listener is generated automatically for TLS-terminated HTTPS listener. Currently,
specifying that a port is HTTP/3-only is not yet supported. This is because HTTP/3 support is not widespread
yet and the most common use case is for the clients like Google Chrome to become aware of HTTP/3 support
with alt-svc
HTTP header in the response when they first use HTTP/1.1 or HTTP/2 over TCP.
Configure the gateway. Notice that there is no explicit HTTP/3 related configuration
|
|
Configure the route
|
|
Once these routes are applied, check if two listeners are created
|
|
Where did the other inbound listener come from? Let us dive deeper into the config. Let us start with checking listener names
|
|
UDP?? Remember that QUIC uses UDP. So could this be related to QUIC? Let us dive deeper.
The output is huge and only the relevant part is shown here. So pipe it to a pager like less
|
|
Well…The one with udp
prefix is a QUIC listener!
Demo time
First note down the address of the Ingress gateway
|
|
I have a custom build of curl called qcurl
which supports sending HTTP/3 request with
--http3
flag. curl
here is the standard curl available in the software repositories.
For demo purposes, I’m skipping TLS certificate verification. Don’t this if it is not a demo.
Let us first send HTTP/2 request
$ curl -svk --http2 --resolve httpbin.quic-corp.com:443:$INGRESS_IP https://httpbin.quic-corp.com/headers
[ Output truncated ]
...
> GET /headers HTTP/2
> Host: httpbin.quic-corp.com
> user-agent: curl/7.76.1
> accept: */*
...
< HTTP/2 200
< server: istio-envoy
< date: Wed, 27 Oct 2021 05:05:59 GMT
< content-type: application/json
< content-length: 601
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 1
< alt-svc: h3=":443"; ma=86400
<
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.quic-corp.com",
"User-Agent": "curl/7.76.1",
"X-B3-Parentspanid": "819c2b2cab59bbe8",
"X-B3-Sampled": "0",
"X-B3-Spanid": "0462ac631afb5f84",
"X-B3-Traceid": "3fa44e3bbbcd21a3819c2b2cab59bbe8",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin/sa/httpbin;Hash=97bf9c90d4a5b9bb8f5da3e825dfa34f04631400420649394741807a320aa0a1;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
}
}
Yayyy! It is working. In particular notice this header
alt-svc: h3=":443"; ma=86400
It indicates that HTTP/3 is supported. h3
is the ALPN sent with the TLS handshake.
Clients supporting HTTP/3 can use this information to connect with QUIC for the future
connections. Now, let us send an HTTP/3 request
$ qcurl -svk --http3 --resolve httpbin.quic-corp.com:443:$INGRESS_IP https://httpbin.quic-corp.com/headers
* Added httpbin.quic-corp.com:443:172.18.200.1 to DNS cache
* Hostname httpbin.quic-corp.com was found in DNS cache
* Trying 172.18.200.1:443...
* Connect socket 5 over QUIC to 172.18.200.1:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* Connected to httpbin.quic-corp.com () port 443 (#0)
* h3 [:method: GET]
* h3 [:path: /headers]
* h3 [:scheme: https]
* h3 [:authority: httpbin.quic-corp.com]
* h3 [user-agent: curl/7.78.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0xa64260)
> GET /headers HTTP/3
> Host: httpbin.quic-corp.com
> user-agent: curl/7.78.0-DEV
> accept: */*
>
< HTTP/3 200
< server: istio-envoy
< date: Wed, 27 Oct 2021 05:08:46 GMT
< content-type: application/json
< content-length: 642
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 2
< alt-svc: h3=":443"; ma=86400
<
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.quic-corp.com",
"Transfer-Encoding": "chunked",
"User-Agent": "curl/7.78.0-DEV",
"X-B3-Parentspanid": "d9f3d78e3f6ddc5c",
"X-B3-Sampled": "0",
"X-B3-Spanid": "3f6c920b02a92b73",
"X-B3-Traceid": "d5c22cd31ddec119d9f3d78e3f6ddc5c",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin/sa/httpbin;Hash=97bf9c90d4a5b9bb8f5da3e825dfa34f04631400420649394741807a320aa0a1;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
}
}
And….. IT WORKS!