Kubernetes Gateway API

Uses: Kong Mesh

To get traffic from outside your mesh inside it (North/South) with Kong Mesh you can use a builtin gateway.

In the quickstart, traffic was only able to get in the mesh by port-forwarding to an instance of an app inside the mesh. In production, you typically set up a gateway to receive traffic external to the mesh. In this guide you will add a built-in gateway in front of the demo-app service and expose it publicly. We will deploy and configure Gateway using Kubernetes Gateway API.

Service graph of the demo app with a builtin gateway on front:

Prerequisites

  • Completed quickstart to set up a zone control plane with demo application

Install Gateway API CRDs

To install Gateway API please refer to official installation instruction.

You also need to manually install Kong Mesh GatewayClass:

echo "apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: kuma
spec:
  controllerName: gateways.kuma.io/controller" | kubectl apply -f -
Copied to clipboard!

At this moment, when you install Gateway API CRDs after installing Kong Mesh control plane you need to restart it to start Gateway API controller. To do this run:

kubectl rollout restart deployment kong-mesh-control-plane -n kong-mesh-system
Copied to clipboard!

Start a gateway

The Gateway resource represents the proxy instance that handles traffic for a set of Gateway API routes. You can create gateway with a single listener on port 8080 by running:

echo "apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: kuma
  namespace: kuma-demo
spec:
  gatewayClassName: kuma
  listeners:
   - name: proxy
     port: 8080
     protocol: HTTP" | kubectl apply -f -
Copied to clipboard!

The Kubernetes cluster needs to support LoadBalancer for this to work.

If you are running minikube you will want to open a tunnel with minikube tunnel -p mesh-zone.

You may not have support for LoadBalancer if you are running locally with kind or k3d. One option for kind is kubernetes-sigs/cloud-provider-kind.

You can now check if the gateway is running in the demo app kuma-demo namespace:

kubectl get pods -n kuma-demo
Copied to clipboard!

Observe the gateway pod:

NAME                       READY   STATUS    RESTARTS   AGE
demo-app-d8d8bdb97-vhgc8   2/2     Running   0          5m
kuma-cfcccf8c7-hlqz5       1/1     Running   0          20s
redis-5484ddcc64-6gbbx     2/2     Running   0          5m
Copied to clipboard!

Retrieve the public URL for the gateway with:

export PROXY_IP=$(kubectl get svc --namespace kuma-demo kuma -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $PROXY_IP
Copied to clipboard!

Check the gateway is running:

curl -v ${PROXY_IP}:8080
Copied to clipboard!

Which outputs:

*   Trying 127.0.0.1:8080...
* Connected to 35.226.116.24 (35.226.116.24) port 8080
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< content-length: 62
< content-type: text/plain
< vary: Accept-Encoding
< date: Mon, 04 Nov 2024 13:21:07 GMT
< server: Kuma Gateway
<
This is a Kuma MeshGateway. No routes match this MeshGateway!
* Connection #0 to host 35.226.116.24 left intact
Copied to clipboard!

Notice the gateway says that there are no routes configured.

Define a route using HTTPRoute

HTTPRoute resources contain a set of matching criteria for HTTP requests and upstream Services to route those requests to.

echo "apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo
  namespace: kuma-demo
spec:
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: kuma
      namespace: kuma-demo
  rules:
    - backendRefs:
      - kind: Service
        name: demo-app
        port: 5000
        weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /" | kubectl apply -f -
Copied to clipboard!

Now try to reach our gateway again:

curl -v ${PROXY_IP}:8080
Copied to clipboard!

which outputs:

*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< content-length: 19
< content-type: text/plain
< date: Fri, 09 Feb 2024 10:10:16 GMT
< server: Kuma Gateway
< x-envoy-upstream-service-time: 24
<
* Connection #0 to host 127.0.0.1 left intact
RBAC: access denied%
Copied to clipboard!

Notice the forbidden error. This is because the quickstart has very restrictive permissions as defaults. Therefore, the gateway doesn’t have permissions to talk to the demo-app service.

To fix this, add a MeshTrafficPermission:

echo "apiVersion: kuma.io/v1alpha1
kind: MeshTrafficPermission
metadata:
  namespace: kuma-demo 
  name: allow-gateway
spec:
  targetRef:
    kind: Dataplane
    labels:
      app: demo-app
  from:
    - targetRef:
        kind: MeshSubset
        tags: 
          kuma.io/service: kuma_kuma-demo_svc 
      default:
        action: Allow" | kubectl apply -f -
Copied to clipboard!

Check it works with:

curl -XPOST -v ${PROXY_IP}:8080/increment
Copied to clipboard!

Now it returns a 200 OK response:

*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> POST /increment HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< x-powered-by: Express
< content-type: application/json; charset=utf-8
< content-length: 42
< etag: W/"2a-gDIArbqhTz783Hls/ysnTwRRsmQ"
< date: Fri, 09 Feb 2024 10:24:33 GMT
< x-envoy-upstream-service-time: 6
< server: Kuma Gateway
<
* Connection #0 to host 127.0.0.1 left intact
{"counter":1,"zone":"local","err":null}
Copied to clipboard!

Securing your public endpoint with a certificate

The application is now exposed to a public endpoint thanks to the gateway. We will now add TLS to our endpoint.

Create a certificate

Create a self-signed certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=${PROXY_IP}"
Copied to clipboard!

Create Kubernetes secret with generated certificate:

echo "apiVersion: v1
kind: Secret
metadata:
  name: my-gateway-certificate
  namespace: kuma-demo
type: kubernetes.io/tls
data:
  tls.crt: "$(cat tls.crt | base64)"
  tls.key: "$(cat tls.key | base64)"" | kubectl apply -f - 
Copied to clipboard!

Now update the gateway to use this certificate:

echo "apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: kuma
  namespace: kuma-demo
spec:
  gatewayClassName: kuma
  listeners:
    - name: proxy
      port: 8080
      protocol: HTTPS
      tls:
        certificateRefs:
          - name: my-gateway-certificate" | kubectl apply -f -
Copied to clipboard!

Check the call to the gateway:

curl -X POST -v --insecure "https://${PROXY_IP}:8080/increment"
Copied to clipboard!

Which should output a successful call and indicate TLS is being used:

*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=127.0.0.1
*  start date: Feb  9 10:49:13 2024 GMT
*  expire date: Feb  8 10:49:13 2025 GMT
*  issuer: CN=127.0.0.1
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://127.0.0.1:8080/increment
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 127.0.0.1:8080]
* [HTTP/2] [1] [:path: /increment]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> POST /increment HTTP/2
> Host: 127.0.0.1:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 200
< x-powered-by: Express
< content-type: application/json; charset=utf-8
< content-length: 42
< etag: W/"2a-BZZq4nXMINsG8HLM31MxUPDwPXk"
< date: Fri, 09 Feb 2024 13:41:11 GMT
< x-envoy-upstream-service-time: 19
< server: Kuma Gateway
< strict-transport-security: max-age=31536000; includeSubDomains
<
* Connection #0 to host 127.0.0.1 left intact
{"counter":5,"zone":"local","err":null}%
Copied to clipboard!

Note that we’re using --insecure as we have used a self-signed certificate.

Next steps

Did this doc help?

Something wrong?

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!