Add a builtin gateway

To get traffic from outside your mesh inside it (North/South) with Kuma 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.

  1. ---
  2. title: service graph of the demo app with a builtin gateway on front
  3. ---
  4. flowchart LR
  5. subgraph edge-gateway
  6. gw0(/ :8080)
  7. end
  8. demo-app(demo-app :5000)
  9. redis(redis :6379)
  10. gw0 --> demo-app
  11. demo-app --> redis

Prerequisites

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

Start a gateway

Create a MeshGatewayInstance

A MeshGatewayInstance configures the pods that will run the gateway.

Create it by running:

  1. echo "apiVersion: kuma.io/v1alpha1
  2. kind: MeshGatewayInstance
  3. metadata:
  4. name: edge-gateway
  5. namespace: kuma-demo
  6. spec:
  7. replicas: 1
  8. serviceType: LoadBalancer" | kubectl apply -f -

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 may be helpful.

Define a listener using MeshGateway

MeshGateway defines listeners for the gateway.

Define a single HTTP listener on port 8080:

  1. echo "apiVersion: kuma.io/v1alpha1
  2. kind: MeshGateway
  3. mesh: default
  4. metadata:
  5. name: my-gateway
  6. spec:
  7. selectors:
  8. - match:
  9. kuma.io/service: edge-gateway_kuma-demo_svc
  10. conf:
  11. listeners:
  12. - port: 8080
  13. protocol: HTTP
  14. tags:
  15. port: http-8080" | kubectl apply -f -

Notice how the selector selects the kuma.io/service tag of the previously defined MeshGatewayInstance.

Now look at the pods running in the namespace by running:

  1. kubectl get pods -n kuma-demo

Observe the gateway pod:

  1. NAME READY STATUS RESTARTS AGE
  2. redis-5fdb98848c-5tw62 2/2 Running 0 5m5s
  3. demo-app-c7cd6588b-rtwlj 2/2 Running 0 5m5s
  4. edge-gateway-66c76fd477-ncsp5 1/1 Running 0 18s

Retrieve the public URL for the gateway with:

  1. export PROXY_IP=$(kubectl get svc --namespace kuma-demo edge-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  2. echo $PROXY_IP

Check the gateway is running:

  1. curl -v ${PROXY_IP}:8080

Which outputs:

  1. * Trying 127.0.0.1:8080...
  2. * Connected to 127.0.0.1 (127.0.0.1) port 8080
  3. > GET / HTTP/1.1ó
  4. > Host: 127.0.0.1:8080
  5. > User-Agent: curl/8.4.0
  6. > Accept: */*
  7. >
  8. < HTTP/1.1 404 Not Found
  9. < content-length: 62
  10. < content-type: text/plain
  11. < vary: Accept-Encoding
  12. < date: Fri, 09 Feb 2024 10:07:26 GMT
  13. < server: Kuma Gateway
  14. <
  15. This is a Kuma MeshGateway. No routes match this MeshGateway!

Notice the gateway says that there are no routes configured.

Define a route using MeshHTTPRoute

MeshHTTPRoute defines HTTP routes inside your service mesh. Attach a route to an entire gateway or to a single listener by using targetRef.kind: MeshGateway

  1. echo "apiVersion: kuma.io/v1alpha1
  2. kind: MeshHTTPRoute
  3. metadata:
  4. name: edge-gateway-route
  5. namespace: kuma-system
  6. labels:
  7. kuma.io/mesh: default
  8. spec:
  9. targetRef:
  10. kind: MeshGateway
  11. name: my-gateway
  12. to:
  13. - targetRef:
  14. kind: Mesh
  15. rules:
  16. - matches:
  17. - path:
  18. type: PathPrefix
  19. value: "/"
  20. default:
  21. backendRefs:
  22. - kind: MeshService
  23. name: demo-app
  24. namespace: kuma-demo
  25. port: 5000" | kubectl apply -f -

Now try to reach our gateway again:

  1. curl -v ${PROXY_IP}:8080

which outputs:

  1. * Trying 127.0.0.1:8080...
  2. * Connected to 127.0.0.1 (127.0.0.1) port 8080
  3. > GET / HTTP/1.1
  4. > Host: 127.0.0.1:8080
  5. > User-Agent: curl/8.4.0
  6. > Accept: */*
  7. >
  8. < HTTP/1.1 403 Forbidden
  9. < content-length: 19
  10. < content-type: text/plain
  11. < date: Fri, 09 Feb 2024 10:10:16 GMT
  12. < server: Kuma Gateway
  13. < x-envoy-upstream-service-time: 24
  14. <
  15. * Connection #0 to host 127.0.0.1 left intact
  16. RBAC: access denied%

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:

  1. echo "apiVersion: kuma.io/v1alpha1
  2. kind: MeshTrafficPermission
  3. metadata:
  4. namespace: kuma-demo
  5. name: demo-app
  6. spec:
  7. targetRef:
  8. kind: MeshSubset
  9. tags:
  10. app: demo-app
  11. from:
  12. - targetRef:
  13. kind: MeshSubset
  14. tags:
  15. kuma.io/service: edge-gateway_kuma-demo_svc
  16. default:
  17. action: Allow" | kubectl apply -f -

Check it works with:

  1. curl -XPOST -v ${PROXY_IP}:8080/increment

Now returns a 200 OK response:

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

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:

  1. openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=${PROXY_IP}"
  1. echo "apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: my-gateway-certificate
  5. namespace: kuma-system
  6. labels:
  7. kuma.io/mesh: default
  8. data:
  9. value: "$(cat tls.key tls.crt | base64)"
  10. type: system.kuma.io/secret" | kubectl apply -f -

Now update the gateway to use this certificate:

  1. echo "apiVersion: kuma.io/v1alpha1
  2. kind: MeshGateway
  3. mesh: default
  4. metadata:
  5. name: my-gateway
  6. spec:
  7. selectors:
  8. - match:
  9. kuma.io/service: edge-gateway_kuma-demo_svc
  10. conf:
  11. listeners:
  12. - port: 8080
  13. protocol: HTTPS
  14. tls:
  15. mode: TERMINATE
  16. certificates:
  17. - secret: my-gateway-certificate
  18. tags:
  19. port: http-8080" | kubectl apply -f -

Check the call to the gateway:

  1. curl -X POST -v --insecure "https://${PROXY_IP}:8080/increment"

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

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

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

Next steps