Backend TLS

Backend TLS

Configure TLS to terminate for a specific backend workload.

⚠️
This feature is experimental in the upstream Kubernetes Gateway API and subject to change.

When you configure an HTTPS listener, the Gateway terminates the TLS connection and decrypts the traffic. The Gateway then routes the decrypted traffic to the backend service.

However, you might have a specific backend workload that uses its own TLS certificate. In this case, you can configure the Gateway to originate a TLS connection that terminates at the backend service by using the Kubernetes Gateway API BackendTLSPolicy. For more information, see the Kubernetes Gateway API docs.

Before you begin

  1. Follow the Get started guide to install kgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  
    kubectl port-forward deployment/http -n kgateway-system 8080:8080

Create a backend workload with a TLS certificate

The following example uses an NGINX server with a self-signed TLS certificate. For the configuration, see the test directory in the kgateway GitHub repository.

  1. Deploy the NGINX server with a self-signed TLS certificate.

    kubectl apply -f https://raw.githubusercontent.com/kgateway-dev/kgateway/refs/heads/main/test/kubernetes/e2e/features/backendtls/inputs/nginx.yaml
  2. Verify that the NGINX server is running.

    kubectl get pods -l app.kubernetes.io/name=nginx

    Example output:

    NAME    READY   STATUS    RESTARTS   AGE
    nginx   1/1     Running   0          9s

Create a BackendTLSPolicy

Create the BackendTLSPolicy for the NGINX workload. For more information, see the Kubernetes Gateway API docs.

  1. Install the experimental channel of the Kubernetes Gateway API so that you can use BackendTLSPolicy.

    kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/experimental-install.yaml
  2. Create a Kubernetes ConfigMap that has the public CA certificate for the NGINX server.

    kubectl apply -f- <<EOF
    # public cert of self-signed cert loaded into nginx, see nginx.yaml
       # separate file so it can be deleted independently
       apiVersion: v1
       data:
         ca.crt: |
           -----BEGIN CERTIFICATE-----
           MIIDFTCCAf2gAwIBAgIUG9Mdv3nOQ2i7v68OgjArU4lhBikwDQYJKoZIhvcNAQEL
           BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwNzA3MTA0MDQwWhcNMjYw
           NzA3MTA0MDQwWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
           AQEBBQADggEPADCCAQoCggEBANueqwfAApjTfg+nxIoKVK4sK/YlNICvdoEq1UEL
           StE9wfTv0J27uNIsfpMqCx0Ni9Rjt1hzjunc8HUJDeobMNxGaZmryQofrdJWJ7Uu
           t5jeLW/w0MelPOfFLsDiM5REy4WuPm2X6v1Z1N3N5GR3UNDOtDtsbjS1momvooLO
           9WxPIr2cfmPqr81fyyD2ReZsMC/8lVs0PkA9XBplMzpSU53DWl5/Nyh2d1W5ENK0
           Zw1l5Ze4UGUeohQMa5cD5hmZcBjOeJF8MuSTi3167KSopoqfgHTvC5IsBeWXAyZF
           81ihFYAq+SbhUZeUlsxc1wveuAdBRzafcYkK47gYmbq1K60CAwEAAaNbMFkwFgYD
           VR0RBA8wDYILZXhhbXBsZS5jb20wCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsG
           AQUFBwMBMB0GA1UdDgQWBBSoa1Zu2o+pQ6sq2HcOjAglZkp01zANBgkqhkiG9w0B
           AQsFAAOCAQEADZq1EMw/jMl0z2LpPh8cXbP09BnfXhoFbpL4cFrcBNEyig0oPO0j
           YN1e4bfURNduFVnC/FDnZhR3FlAt8a6ozJAwmJp+nQCYFoDQwotSx12y5Bc9IXwd
           BRZaLgHYy2NjGp2UgAya2z23BkUnwOJwJNMCzuGw3pOsmDQY0diR8ZWmEYYEPheW
           6BVkrikzUNXv3tB8LmWzxV9V3eN71fnP5u39IM/UQsOZGRUow/8tvN2/d0W4dHky
           t/kdgLKhf4gU2wXq/WbeqxlDSpjo7q/emNl59v1FHeR3eITSSjESU+dQgRsYaGEn
           SWP+58ApfCcURLpMxUmxkO1ayfecNJbmSQ==
           -----END CERTIFICATE-----
       
       kind: ConfigMap
       metadata:
         name: ca
    
    EOF
  3. Create the BackendTLSPolicy.

    kubectl apply -f - <<EOF
    apiVersion: gateway.networking.k8s.io/v1alpha3
    kind: BackendTLSPolicy
    metadata:
      name: nginx-tls-policy
      labels:
        app: nginx
    spec:
      targetRefs:
      - group: ""
        kind: Service
        name: nginx
      validation:
        hostname: "example.com"
        caCertificateRefs:
        - group: ""
          kind: ConfigMap
          name: ca
    EOF
    Review the following table to understand this configuration.
    Setting Description
    targetRefs The service that you want the Gateway to originate a TLS connection to, such as the NGINX server.
    validation.hostname The hostname that matches the NGINX server certificate.
    validation.caCertificateRefs The ConfigMap that has the public CA certificate for the NGINX server.
  4. Create an HTTPRoute that routes traffic to the NGINX server on the example.com hostname and HTTPS port 8443. Note that the parent Gateway is the sample http Gateway resource that you created before you began.

    kubectl apply -f - <<EOF
    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: HTTPRoute
    metadata:
      name: nginx-route
      labels:
        app: nginx
    spec:
      parentRefs:
      - name: http
        namespace: kgateway-system
      hostnames:
      - "example.com"
      rules:
      - backendRefs:
        - name: nginx
          port: 8443
    EOF

Verify the TLS connection

Now that your TLS backend and routing resources are configured, verify the TLS connection.

  1. Get the external address of the gateway and save it in an environment variable. Note that it might take a few seconds for the gateway address to become available.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS   
    kubectl port-forward svc/http -n kgateway-system 8080:8080

  2. Send a request to the NGINX server and verify that you get back a 200 HTTP response code.

    curl -vi http://$INGRESS_GW_ADDRESS:8080/ -H "host: example.com:8080"
    curl -vi http://localhost:8080/ -H "host: example.com:8080"

    Example output:

    * Host localhost:8080 was resolved.
    * IPv6: ::1
    * IPv4: 127.0.0.1
    *   Trying [::1]:8080...
    * Connected to localhost (::1) port 8080
    > GET / HTTP/1.1
    > Host: example.com:8080
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/1.1 200 OK
    HTTP/1.1 200 OK
  3. Enable port-forwarding on the Gateway.

    kubectl port-forward deploy/http -n kgateway-system 19000
  4. In your browser, open the Envoy stats page at http://127.0.0.1:19000/stats.

  5. Search for the following stats that indicate the TLS connection is working. The count increases each time that the Gateway sends a request to the NGINX server.

    • cluster.kube_default_nginx_8443.ssl.versions.TLSv1.2: The number of TLSv1.2 connections from the Envoy gateway proxy to the NGINX server.
    • cluster.kube_default_nginx_8443.ssl.handshake: The number of successful TLS handshakes between the Envoy gateway proxy and the NGINX server.

Cleanup

You can remove the resources that you created in this guide.
  1. Delete the NGINX server and CA ConfigMap.

    kubectl delete -f https://raw.githubusercontent.com/kgateway-dev/kgateway/refs/heads/main/test/kubernetes/e2e/features/backendtls/inputs/nginx.yaml
    kubectl delete -f https://raw.githubusercontent.com/kgateway-dev/kgateway/refs/heads/main/test/kubernetes/e2e/features/backendtls/inputs/configmap.yaml
  2. Delete the routing resources that your created for the NGINX server.

    kubectl delete backendtlspolicy,configmap,httproute -A -l app=nginx
  3. If you want to re-create a BackendTLSPolicy after deleting one, restart the control plane.

⚠️
Due to a known issue, if you don’t restart the control plane, you might notice requests that fail with a HTTP/1.1 400 Bad Request error after creating the new BackendTLSPolicy.
kubectl rollout restart -n kgateway-system deployment/kgateway
SNI listenerTLS passthrough