Notice:
This is the "latest" release of Envoy Gateway, which contains the most recent commits from the main branch.
This release might not be stable.
Please refer to the /docs documentation for the most current information.

HTTP3

This task will help you get started using HTTP3 using EG. EG supports HTTP/3 between the client and the Gateway, and HTTP/3 between the Gateway and the backend. The two are configured separately and can be used on their own or together. This task uses a self-signed CA, so it should be used for testing and demonstration purposes only.

HTTP/3 to Clients

HTTP/3 to clients is enabled with the http3 field of a ClientTrafficPolicy. The Gateway then listens for QUIC on the HTTPS listener’s port and advertises HTTP/3 to clients with an alt-svc response header.

Prerequisites

  • OpenSSL to generate TLS assets.

Installation

Follow the steps below to install Envoy Gateway and the example manifest. Before proceeding, you should be able to query the example backend using HTTP.

Expand for instructions
  1. Install the Gateway API CRDs and Envoy Gateway using Helm:

    helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace
    
  2. Install the GatewayClass, Gateway, HTTPRoute and example app:

    kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default
    
  3. Verify Connectivity:

    Get the External IP of the Gateway:

    export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get
       

    The above command should succeed with status code 200.

    Get the name of the Envoy service created the by the example Gateway:

    export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Get the deployment of the Envoy service created the by the example Gateway:

    export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Port forward to the Envoy service:

    kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 &
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://localhost:8888/get
       

    The above command should succeed with status code 200.

TLS Certificates

Generate the certificates and keys used by the Gateway to terminate client TLS connections.

Create a root certificate and private key to sign certificates:

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

Create a certificate and a private key for www.example.com:

openssl req -out www.example.com.csr -newkey rsa:2048 -nodes -keyout www.example.com.key -subj "/CN=www.example.com/O=example organization"
openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in www.example.com.csr -out www.example.com.crt

Store the cert/key in a Secret:

kubectl create secret tls example-cert --key=www.example.com.key --cert=www.example.com.crt

Update the Gateway from the Quickstart to include an HTTPS listener that listens on port 443 and references the example-cert Secret:

kubectl patch gateway eg --type=json --patch '
  - op: add
    path: /spec/listeners/-
    value:
      name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
        - kind: Secret
          group: ""
          name: example-cert
  '

Enabling HTTP/3

Apply the following ClientTrafficPolicy to enable HTTP3

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: enable-http3
spec:
  http3: {}
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: eg
EOF

Save and apply the following resource to your cluster:

---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: enable-http3
spec:
  http3: {}
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: eg

By default, Envoy Gateway advertises the Gateway listener port in the HTTP/3 alt-svc response header. If an external load balancer exposes the Gateway on a different port, set advertisedPort to the external port without changing the listener or Service ports:

spec:
  http3:
    advertisedPort: 443

Verify the Gateway status:

kubectl get gateway/eg -o yaml

Testing

Get the External IP of the [Gateway][]:

export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')

Query the example app through the [Gateway][]:

The below example uses a custom docker image with custom curl binary with built-in http3.

docker run --net=host --rm ghcr.io/macbre/curl-http3 curl -kv --http3 -HHost:www.example.com --resolve "www.example.com:443:${GATEWAY_HOST}" https://www.example.com/get

It is not possible at the moment to port-forward UDP protocol in kubernetes service check out https://github.com/kubernetes/kubernetes/issues/47862. Hence we need external loadbalancer to test this feature out.

HTTP/3 to Backends

HTTP/3 to the backend is enabled with the http3 field of a BackendTrafficPolicy.

Prerequisites

QUIC always runs over TLS, so the backend must already be configured with TLS through a BackendTLSPolicy or a Backend resource’s spec.tls. When a BackendTrafficPolicy enables http3 for a route whose backends are not all configured with TLS, or whose backendRefs cannot all be resolved, HTTP/3 is left off for that route, the rest of the policy still applies, and the policy reports a Warning condition naming the route. HTTP/3 comes back on its own once the route’s backends are all resolved and configured with TLS.

HTTP/3 requires a verified backend. A backend with insecureSkipVerify is left off HTTP/3 with a Warning condition on the policy. Configure the backend’s CA instead, through a BackendTLSPolicy or the Backend’s tls.caCertificateRefs.

Enabling HTTP/3

Apply a BackendTrafficPolicy with http3 set to the routes whose backends speak HTTP/3. There are two modes, Auto and Always:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: backend-http3
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: backend
  http3:
    mode: Auto

Auto Mode

Auto is the default. Envoy uses HTTP/3 only for backends that advertise support for it through an alt-svc response header. It caches that advertisement, then races a QUIC connection against a TCP one and uses whichever is established first, so it falls back to HTTP/1.1 or HTTP/2 whenever QUIC is unavailable. This is the safe choice for backends reached over a network where UDP may be blocked.

Envoy only honors an alt-svc entry whose port is the port it dials the backend on, and that names no host. For a Service backend that is the Pod IP and targetPort, not the Service port. A backend that advertises h3=":443" behind a Service that maps port 443 to targetPort 8443 is dialed on 8443, so the advertisement never matches and Auto keeps using TCP. Either have the backend advertise the port it actually listens on, as in h3=":8443", or give the Service the same port and targetPort. The same rule applies to a Backend resource: the advertised port must be the endpoint’s port. The QUIC listener on the backend must also be reachable on that port over UDP.

The backend’s alt-svc header is not forwarded to clients. It names a port on the backend, which a client would read as a port on the Gateway, so Envoy Gateway strips it after recording it and adds the Gateway’s own alt-svc when the listener has HTTP/3 enabled. Do not remove alt-svc with an HTTPRoute ResponseHeaderModifier filter: that runs before the advertisement is recorded, so Auto mode would never discover the backend’s HTTP/3 support.

Always Mode

Always sends every request over HTTP/3 and never falls back to TCP. Use it only where the backend is known to speak HTTP/3 and UDP is known to work, since there is no recovery if QUIC fails. Active health checks are the exception: Envoy sends them over TLS on TCP even in Always mode, so a backend with an active healthCheck must also accept TCP on the same port, or every endpoint is marked unhealthy while QUIC traffic still works. Passive health checks observe the QUIC traffic itself and are unaffected.

Limitations

The EnvoyProxy backendTLS.alpnProtocols setting is not applied to a cluster using HTTP/3. Envoy offers that list on the QUIC handshake too, where only h3 is valid, so the cluster offers no ALPN list of its own: QUIC negotiates h3 and the cluster’s TCP connections fall back to h2 and http/1.1. The policy reports a Warning condition naming the cluster.

http3 cannot be combined with useClientProtocol or proxyProtocol: the first would have Envoy pick the upstream protocol from the downstream request, and PROXY protocol is a TCP preamble with no QUIC equivalent. A policy that sets both is rejected at admission. HTTP/3 is also left off, with the same Warning condition, for backends that declare an HTTP/2 or gRPC appProtocol, since those ask for a protocol HTTP/3 cannot provide.