Configure HTTP/3 QUIC Between Client and APISIX
HTTP/3 is the third major version of the Hypertext Transfer Protocol (HTTP). Unlike its predecessors which rely on TCP, HTTP/3 is based on QUIC (Quick UDP Internet Connections) protocol. It brings several benefits that collectively result in reduced latency and improved performance:
- enabling seamless transition between different network connections, such as switching from Wi-Fi to mobile data.
- eliminating head-of-line blocking, so that a lost packet does not block all streams.
- negotiating TLS versions at the same time as the TLS handshakes, allowing for faster connections.
- providing encryption by default, ensuring that all data transmitted over an HTTP/3 connection is protected and confidential.
- providing zero round-trip time (0-RTT) when communicating with servers that clients have already established connections to.
APISIX currently supports HTTP/3 connections between downstream clients and APISIX. HTTP/3 connections with upstream services are not yet supported.
This feature is currently experimental and not recommended for production use.
This guide will show you how to configure APISIX to enable HTTP/3 connections between client and APISIX.
Prerequisite(s)
- Install Docker.
- Install static-curl or other curl that has HTTP/3 support.
- Follow the Getting Started tutorial to start a new APISIX instance in Docker.
Enable HTTP/3 in APISIX
- Docker
- Kubernetes
Enable HTTP/3 on port 9443
by adding the following configurations to APISIX's config.yaml
configuration file:
docker exec apisix-quickstart /bin/sh -c "echo '
apisix:
enable_control: true
control:
ip: 0.0.0.0
port: 9092
ssl:
listen:
- port: 9443
enable_http3: true
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key_required: false
allow_admin:
- 0.0.0.0/0
plugin_attr:
prometheus:
export_addr:
ip: 0.0.0.0
port: 9091
' > /usr/local/apisix/conf/config.yaml"
Reload APISIX for configuration changes to take effect:
docker exec apisix-quickstart apisix reload
Edit the gateway ConfigMap:
kubectl edit cm apisix
Enable SSL and HTTP3:
apisix:
ssl:
enabled: true
listen:
- port: 9443
enable_http3: true
Save the ConfigMap and restart the deployment:
kubectl rollout restart deployment apisix
Generate Certificates and Keys
HTTP/3 requires TLS. You can purchase the certificates or self-generate them, whichever is applicable.
To self-generate, first generate the certificate authority (CA) key and certificate:
openssl genrsa -out ca.key 2048 && \
openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CN=ROOTCA" && \
openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.crt
Next, generate the key and certificate with the common name test.com
for APISIX, and sign with the CA certificate:
openssl genrsa -out server.key 2048 && \
openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=test.com" && \
openssl x509 -req -days 36500 -sha256 -extensions v3_req \
-CA ca.crt -CAkey ca.key -CAserial ca.srl -CAcreateserial \
-in server.csr -out server.crt
Configure HTTPS
- Admin API
- Ingress Controller
You can optionally load the content of server.crt
and server.key
into shell variables:
server_cert=$(cat server.crt)
server_key=$(cat server.key)
Create an SSL certificate object to save the server certificate and its key:
curl -i "http://127.0.0.1:9180/apisix/admin/ssls" -X PUT -d '
{
"id": "quickstart-tls-client-ssl",
"sni": "test.com",
"cert": "'"${server_cert}"'",
"key": "'"${server_key}"'"
}'
❶ Configure the SNI to match the server certificate CN.
❷ Configure the server certificate.
❸ Configure the private key for the server certificate.
Generate a Kubernetes TLS secret named test-tls-secret
using the provided certificate and private key files, and output the manifest to secret.yaml
in the ingress-apisix
namespace:
kubectl create secret tls test-tls-secret \
--cert=server.crt \
--key=server.key \
--namespace=ingress-apisix \
--dry-run=client -o yaml > secret.yaml
Apply the secret to your cluster:
kubectl apply -f secret.yaml
- Gateway API
- APISIX CRD
Update your Gateway manifest file as such:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: apisix
spec:
gatewayClassName: apisix
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
hostname: test.com
tls:
certificateRefs:
- kind: Secret
group: ""
name: test-tls-secret
infrastructure:
parametersRef:
group: apisix.apache.org
kind: GatewayProxy
name: apisix-config
Apply the configuration to your cluster:
kubectl apply -f gateway.yaml
Create a Kubernetes manifest file for TLS configuration:
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
name: test-tls
spec:
ingressClassName: apisix
hosts:
- test.com
secret:
name: test-tls-secret
namespace: ingress-apisix
Apply the configuration to your cluster:
kubectl apply -f tls.yaml
Create a Route in APISIX
Create a sample route to httpbin.org
:
- Admin API
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id":"httpbin-route",
"uri":"/get",
"upstream": {
"type":"roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /get
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
ingressClassName: apisix
http:
- name: httpbin-route
match:
paths:
- /get
upstreams:
- name: httpbin-external-domain
Apply the configuration to your cluster:
kubectl apply -f httpbin-route.yaml
Verify HTTP/3 Connections between Client and APISIX
- Docker
- Kubernetes
Send a request to the route:
curl -kv --http3-only \
-H "Host: test.com" \
--resolve "test.com:9443:127.0.0.1" "https://test.com:9443/get"
kubectl port-forward
only supports TCP, not UDP, and HTTP/3 (QUIC) requires UDP. To test HTTP/3 connection, you can expose the service via NodePort:
kubectl expose deployment apisix \
--name=apisix-gateway-quic \
--port=9443 \
--target-port=9443 \
--protocol=UDP \
--type=NodePort
Find your node IP:
kubectl get svc apisix-gateway-quic
Send a request to the route:
curl -kv --http3-only \
-H "Host: test.com" \
--resolve "test.com:9443:${YOUR_NODE_IP}" "https://test.com:9443/get"
You should receive an HTTP/3 200
response similar to the following:
* Added test.com:9443:127.0.0.1 to DNS cache
* Hostname test.com was found in DNS cache
* Trying 127.0.0.1:9443...
* QUIC cipher selection: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256
* Skipped certificate verification
* Connected to test.com (127.0.0.1) port 9443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://test.com:9443/get
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: test.com]
* [HTTP/3] [0] [:path: /get]
* [HTTP/3] [0] [user-agent: curl/8.7.1]
* [HTTP/3] [0] [accept: */*]
> GET /get HTTP/3
> Host: test.com
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/3 200
...
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Host": "test.com",
"User-Agent": "curl/8.7.1",
"X-Amzn-Trace-Id": "Root=1-6656013a-27da6b6a34d98e3e79baaf5b",
"X-Forwarded-Host": "test.com"
},
"origin": "172.19.0.1, 123.40.79.456",
"url": "http://test.com/get"
}
* Connection #0 to host test.com left intact
Next Steps
You have learned how to configure HTTP/3 between client and APISIX. HTTP/3 connections with upstream services are not yet supported and any contribution is welcome.