Routes

Define TLSRoutes for HAProxy Unified Gateway

The Gateway API defines specialized resources for routing different types of network traffic. You would use a TLSRoute resource to route TCP traffic that’s encrypted by TLS, where the backend application is responsible for terminating the TLS connection. For this use case, you’d configure a listener on the Gateway that uses the TLS protocol and sets TLS passthrough.

Prerequisites checklist Jump to heading

Before continuing, ensure that you’ve met these prerequisites:

  • Define a Gateway. When defining a Gateway, add a TLS listener that set its allowedRoutes property to accept routes of kind TLSRoute.

Implement TLS passthrough Jump to heading

TLS passthrough is useful when the backend application terminates the TLS connection, giving you end-to-end encryption. The Gateway doesn’t decrypt the traffic. You can use this with HTTP applications where the web server handles the TLS or with non-HTTP applications that transfer data over TLS-encrypted TCP.

To implement TLS passthrough:

  1. Define a TLSRoute resource for your target domain(s). In this example, we route traffic for the hostname payments.example.com.

    Section Description
    parentRefs References one or more Gateways to which a TLSRoute tries to attach. This TLSRoute will attach to Gateway resources that have listeners with allowedRoutes matching the TLSRoute kind and a matching namespace. The sectionName field specifies the name of the listener on the Gateway to attach to. In this example, we attach to the TLS listener.
    hostnames Lists hostnames to accept traffic for. This also sets the TLS SNI value to match when determining whether to route traffic to the backend application.
    rules Each entry has a backendRefs section to route to the specified service. For example, requests for payments.example.com will route to payments-svc. A backendRefs entry refers to the name of a Service resource and its listening port.
    tlsroute.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: TLSRoute
    metadata:
    name: payments-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    sectionName: tls-listener
    hostnames:
    - "payments.example.com"
    rules:
    - backendRefs:
    - name: payments-svc
    namespace: default
    port: 443
    tlsroute.yaml
    yaml
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: TLSRoute
    metadata:
    name: payments-route
    namespace: default
    spec:
    parentRefs:
    - name: example-haproxy-gateway
    sectionName: tls-listener
    hostnames:
    - "payments.example.com"
    rules:
    - backendRefs:
    - name: payments-svc
    namespace: default
    port: 443
  2. Apply the changes with kubectl:

    nix
    kubectl apply -f tlsroute.yaml
    nix
    kubectl apply -f tlsroute.yaml
    output
    text
    tlsroute.gateway.networking.k8s.io/payments-route created
    output
    text
    tlsroute.gateway.networking.k8s.io/payments-route created
  3. If your TLSRoute resource lists a backendRefs Service that’s in a namespace that’s different from the namespace where the TLSRoute is defined, you’ll need to add a ReferenceGrant for cross-namespace service references.

Try a test application

To try out TLS passthrough, deploy this application. It uses HAProxy as the backend application. HAProxy terminates TLS and returns a Hello! message.

  1. On Linux, you can generate a self-signed TLS certificate for the domain payments.example.com with this command:

    nix
    openssl req -x509 -nodes -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=payments.example.com" -days 365
    nix
    openssl req -x509 -nodes -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=payments.example.com" -days 365
  2. Use the kubectl create secret command to store your TLS certificate and key in the Kubernetes cluster:

    nix
    kubectl create secret tls example-tls --cert=tls.crt --key=tls.key
    nix
    kubectl create secret tls example-tls --cert=tls.crt --key=tls.key
  3. Define a ConfigMap, Deployment, and Service for the application.

    payments-svc.yaml
    yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: haproxy-conf
    data:
    haproxy.cfg: |
    global
    log stdout format raw local0
    pidfile /var/run/haproxy.pid
    ssl-load-extra-del-ext
    ssl-load-extra-files key
    defaults
    log global
    mode http
    option httplog
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    frontend mysite
    bind :80
    bind :443 ssl crt /usr/local/etc/haproxy/ssl/tls.crt
    http-request return status 200 content-type text/plain lf-string "Hello!"
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: payments-app
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: payments-app
    template:
    metadata:
    labels:
    app: payments-app
    spec:
    containers:
    - name: haproxy
    image: haproxytech/haproxy-ubuntu:latest
    ports:
    - containerPort: 443
    - containerPort: 80
    volumeMounts:
    - name: haproxy-conf
    mountPath: /usr/local/etc/haproxy/haproxy.cfg
    subPath: haproxy.cfg
    - name: example-tls
    mountPath: /usr/local/etc/haproxy/ssl
    volumes:
    - name: haproxy-conf
    configMap:
    name: haproxy-conf
    - name: example-tls
    secret:
    secretName: example-tls
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: payments-svc
    namespace: default
    spec:
    selector:
    app: payments-app
    type: NodePort
    ports:
    - name: http
    port: 80
    targetPort: 80
    - name: https
    port: 443
    targetPort: 443
    payments-svc.yaml
    yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: haproxy-conf
    data:
    haproxy.cfg: |
    global
    log stdout format raw local0
    pidfile /var/run/haproxy.pid
    ssl-load-extra-del-ext
    ssl-load-extra-files key
    defaults
    log global
    mode http
    option httplog
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    frontend mysite
    bind :80
    bind :443 ssl crt /usr/local/etc/haproxy/ssl/tls.crt
    http-request return status 200 content-type text/plain lf-string "Hello!"
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: payments-app
    namespace: default
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: payments-app
    template:
    metadata:
    labels:
    app: payments-app
    spec:
    containers:
    - name: haproxy
    image: haproxytech/haproxy-ubuntu:latest
    ports:
    - containerPort: 443
    - containerPort: 80
    volumeMounts:
    - name: haproxy-conf
    mountPath: /usr/local/etc/haproxy/haproxy.cfg
    subPath: haproxy.cfg
    - name: example-tls
    mountPath: /usr/local/etc/haproxy/ssl
    volumes:
    - name: haproxy-conf
    configMap:
    name: haproxy-conf
    - name: example-tls
    secret:
    secretName: example-tls
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: payments-svc
    namespace: default
    spec:
    selector:
    app: payments-app
    type: NodePort
    ports:
    - name: http
    port: 80
    targetPort: 80
    - name: https
    port: 443
    targetPort: 443
  4. Apply the changes with kubectl:

    nix
    kubectl apply -f payments-svc.yaml
    nix
    kubectl apply -f payments-svc.yaml
    output
    text
    configmap/haproxy-conf created
    deployment.apps/payments-app created
    service/payments-svc created
    output
    text
    configmap/haproxy-conf created
    deployment.apps/payments-app created
    service/payments-svc created
  5. If you set the HAProxy Unified Gateway service to have a type of NodePort, then you can edit the service definition to set a different NodePort value. Otherwise, a random port is assigned.

    nix
    kubectl edit service -n haproxy-unified-gateway haproxy-unified-gateway
    nix
    kubectl edit service -n haproxy-unified-gateway haproxy-unified-gateway

    Then change the nodePort to the port you want to use. Here, we use 31444:

    yaml
    - name: tls-31444
    nodePort: 31444
    port: 31444
    protocol: TCP
    targetPort: 31444
    yaml
    - name: tls-31444
    nodePort: 31444
    port: 31444
    protocol: TCP
    targetPort: 31444
  6. Make a request on the worker node:

    nix
    curl -k -H "Host: payments.example.com" --resolve "payments.example.com:31444:127.0.0.1" https://payments.example.com:31444/
    nix
    curl -k -H "Host: payments.example.com" --resolve "payments.example.com:31444:127.0.0.1" https://payments.example.com:31444/
    output
    text
    Hello!
    output
    text
    Hello!

Add a ReferenceGrant for cross-namespace service references Jump to heading

If your TLSRoute lists a Service under backendRefs that’s in a different namespace than the TLSRoute resource, you’ll need to define a ReferenceGrant resource to allow cross-namespace communication between the TLSRoute and the Service.

Below, we allow a TLSRoute in the default namespace to reference a Service in the foo namespace. Note that the to section doesn’t need a namespace because a ReferenceGrant can refer only to resources defined in its own namespace.

foo-referencegrant.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: ReferenceGrant
metadata:
name: refgrantns1
namespace: foo
spec:
from:
- group: "gateway.networking.k8s.io"
kind: "TLSRoute"
namespace: default
to:
- group: ""
kind: "Service"
foo-referencegrant.yaml
yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: ReferenceGrant
metadata:
name: refgrantns1
namespace: foo
spec:
from:
- group: "gateway.networking.k8s.io"
kind: "TLSRoute"
namespace: default
to:
- group: ""
kind: "Service"

Apply the changes with kubectl:

nix
kubectl apply -f foo-referencegrant.yaml
nix
kubectl apply -f foo-referencegrant.yaml

See also Jump to heading

Do you have any suggestions on how we can improve the content of this page?