In the previous blog, we looked into Kubernetes Gateway API implementation. We saw a demo on gradually shifting traffic from Ingress to Gateway API. Here, I will share a tutorial on securing the traffic in Gateway API using TLS. 

Steps to implement TLS with Kubernetes Gateway API

I will follow the given steps for the demo:

Step #0: Demo overview and prerequisites

To show TLS with K8s Gateway API, I will deploy a Gateway with 2 listeners: HTTP and HTTPS. Both listeners will have an HTTPRoute attached to it, which routes the traffic to the service as shown in the image below:

gateway in k8s gateway api with http and https listeners


We will check if we can access the application through the secure HTTPS route.

The prerequisites for the demo are the following:

  • A controller to implement Kubernetes Gateway API. I have used Istio Ingress for the demo.
  • At the time of writing this piece, the cert-manager needs to have the experimental gateway feature manually enabled for it to work with Gateway API. For that, add –feature-gates=ExperimentalGatewayAPISupport=true to the cert-manager-controller container args:
```
containers:
  - name: cert-manager-controller
    image: "quay.io/jetstack/cert-manager-controller:v1.14.1"
    imagePullPolicy: IfNotPresent
    args:
      - --v=2
      - --cluster-resource-namespace=$(POD_NAMESPACE)
      - --leader-election-namespace=kube-system
      - --acme-http01-solver-image=quay.io/jetstack/cert-manager-acmesolver:v1.14.1
      - --max-concurrent-challenges=60
      - --feature-gates=ExperimentalGatewayAPISupport=true ######### REQUIRED FOR CERTIFICATE TO WORK WITH K8S GATEWAY API

```

I have already configured and uploaded the resources used for the demo to the IMESH GitHub repository.

If you are interested in watching the demo in action, here is the video:

Step #1: Deploy the application, cert-manager, and ClusterIssuer

I’m deploying a simple echoserver service (application.yaml) to tls-gw-api namespace (namespace.yaml):

kubectl apply -f app/application.yaml

Now, let’s deploy the cert-manager (cert-manager.yaml) which has the Gateway feature enabled:

kubectl apply -f certificate/cert-manager.yaml

The cert-manager is up and running:

cert-manager deployment status

Now, I’m using the following ClusterIssuer (ssl-prod-cluster-issuer.yaml) for the prod cluster as the Certificate Authority (CA) to manage the certificates for any namespace in the cluster:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: prod-cluster-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: prod-cluster-issuer
    solvers:
  	- http01:
          gatewayHTTPRoute:
        	parentRefs:
          	  - kind: Gateway
                name: k8s-gateway
                namespace: tls-gw-api

I’m using the HTTP-01 challenge for the certificates to auto-renew in the cluster. The gatewayHTTPRoute field specifies the gateway where the HTTP challenge needs to be solved and certificates to be auto-renewed.

Applying the issuer:

kubectl apply -f certificate/ssl-prod-cluster-issuer.yaml

You can check its deployment status by running the command:

kubectl get clusterissuer

That sums up the configurations for the cert-manager. Now, let’s look at the Kubernetes Gateway API CRDs.

Step #2: Deploy the Gateway CRD

Following is the configuration for the Gateway resource (k8s-gateway.yaml):

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: k8s-gateway
  namespace: tls-gw-api
  annotations:
    cert-manager.io/cluster-issuer: prod-cluster-issuer
    # cert-manager.io/cluster-issuer: staging-cluster-issuer
    service.beta.kubernetes.io/port_80_no_probe_rule: "true" # FOR AZURE
    service.beta.kubernetes.io/port_443_no_probe_rule: "true" # FOR AZURE
spec:
  gatewayClassName: istio
  listeners:
    - name: http-listener
  hostname: "*.imesh.ai"
  port: 80
  protocol: HTTP
  allowedRoutes:
      namespaces:
          from: All
    - name: https-listener
  hostname: test.imesh.ai
  port: 443
  protocol: HTTPS
  tls:
      mode: Terminate
      certificateRefs:
          - name: "test-imesh-cert"
  allowedRoutes:
      namespaces:
          from: All
  • In the above file, metadata specifies the gateway (k8s-gateway) and the respective namespace (tls-gw-api).
  • The annotation cert-manager.io/cluster-issuer: prod-cluster-issuer specifies the ClusterIssuer configured in step #1. Make sure to change the issuer and annotation if you are using a namespace issuer (i.e., Issuer resource).
  • gatewayClassName shows the controller (Istio) used for implementing the Gateway API resources. Istio is already installed in the cluster.
  • listeners field lists http-listener and https-listener which use HTTP and HTTPS protocols, respectively. http-listener has the hostname *.imesh.ai while https-listener has test.imesh.ai.
    • Note that an HTTP listener is mandatory even if you need only the HTTPS route or TLS. Without an HTTP listener, the certificate will not be able to solve the HTTP challenge since it happens in plain-text/HTTP format. For better security, it is advisable to add all your application-level routes to the HTTPS listener and just have the HTTP listener for certificate renewal purposes.
  • tls field refers to the tls termination behavior (termination) and the certificates used by the Gateway. The certificate’s secret information is mentioned under certificateRefs.

Applying the Gateway:

kubectl apply -f gw-api/k8s-gateway.yaml

The Gateway is running:

gateway resource deployment status

Now, let’s create routes for the Gateway listeners to take the requests to the echoserver service.

Step #3: Attach HTTPRoutes to HTTP and HTTPS listeners

I have created 2 HTTPRoute resources: insecure-http-route and secure-http-route. In both resources, the sectionName field refers to the respective listener in the Gateway to which the route gets attached.

In the insecure-http-route resource, the sectionName is http-listener which means that the HTTPRoute gets attached to the HTTP listener in the Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: insecure-http-route
  namespace: tls-gw-api
spec:
  parentRefs:
    - kind: Gateway
  name: k8s-gateway
  sectionName: http-listener
``` 

Similarly, secure-http-route attaches to the https-listener in the Gateway.

I have also added filter chains to inject request and response headers to identify whether the request comes from secure or insecure routes. The below configuration is from secure-http-route, and it injects HTTPS-Secure header:

``` filters:
  - type: ResponseHeaderModifier
    responseHeaderModifier:
      add:
        - name: PROTOCOL
          value: HTTPS-Secure
  - type: RequestHeaderModifier
    requestHeaderModifier:
      add:
        - name: PROTOCOL
          value: HTTPS-Secure
```

Similarly, insecure-http-route injects HTTP-Insecure value to the header.

Both the HTTPRoute resources route traffic from the Gateway to echoserver service in port 80.

Let us apply the routes:

kubectl apply -f gw-api/insecure-http-route.yaml
kubectl apply -f gw-api/secure-http-route.yaml

You can verify the routes attached to the Gateway listeners by describing the Gateway:

kubectl describe gateway k8s-gateway -n tls-gw-api 

We can also check if the certificate is generated:

kubectl get certificate -n tls-gw-api

Now, let us verify if we can access the application from the browser through both HTTP and HTTPS routes.

Step #4: Access the Gateway

Let us try the insecure, HTTP route by typing in http://test.imesh.ai in the browser:

accessing the application through insecure http route

You can see the protocol=HTTP-Insecure in the request headers, which means that it is coming from the insecure HTTP route.

If I try https://test.imesh.ai, you can see that the protocol value is HTTPS-Secure, meaning the request comes from the secure HTTP route.
 

accessing the application through the secure http TLS route

You can also view the certificate issued for the connection:

certificate details

IMESH Enterprise Gateway API support

The TLS setup with Gateway API that we saw above is a simple one. In real-life production clusters, DevOps and architects deal with multiple certificates used for different domains. Managing certificates and their auto-renewal can quickly become complicated in those scenarios. IMESH provides support for Gateway API challenges, and you can contact us anytime if you need help.

Md Azmal

Md Azmal

Azmal is one of the founding engineers at IMESH, who focuses on boosting enterprise performance and security using Istio and Envoy. He's a versatile full-stack developer skilled in building scalable and performant applications in the cloud. Azmal's interest in cybersecurity and networking has led him to work on various research projects in network security.

Leave a Reply