Mastering Kubernetes with Cilium: Empowering L7 Traffic Control

Mastering Kubernetes with Cilium: Empowering L7 Traffic Control

With the new release of the Cilium CNI on our Kubernetes Service you’ll get the ability to filter traffic based on L7 properties. It’s very powerful and can help a lot with your services security.

In this tutorial, we’ll be securing an API endpoint to allow access only to specific routes by our client. All other traffic won’t even be forwarded to the service, thus keeping the load of the API as low as possible.

Prerequisites

Before you get started, you’ll need the following:

  1. A Kubernetes cluster up and running with Cilium.
  2. kubectl command-line tool installed and configured to communicate with your cluster.
  3. Basic knowledge of Kubernetes pods, services, and namespaces.

Creating API + API-Client

$ kubectl apply -f \
https://gist.github.com/modzilla99/0cbdd39cc838e752ae8cdb5ec3cdd03a/\
raw/adb7218bb1e9195c42192d71809f6f7043e50cd7/deploy.yaml
configmap/apiconfig created
deployment.apps/api created
service/api created
pod/api-client created

We’ve now created a service called “api” that is bound to the “api” deployment. It has two routes. The route /secret should be private and /greetings should be publicly accessible. Since we don’t have any NetworkPolcies defined, both routes are accessible by any pod running in the cluster.

Testing the API

So let’s try out the API first:

$ kubectl exec -it api-client -- curl -H 'X-Security-Header: mysecret' api/secret/mysecret
{
  "name": "mysecret",
  "secret": "syferghewg"
}
$ kubectl exec -it api-client -- curl api/greetings/World
Greetings, World!

$ kubectl exec -it api-client -- curl google.com
The document has moved

As you can see the API is fully accessible without any restrictions and the api-client pod has full access to the web.

Limiting Access

In order to minimize access, we aim to restrict the pods access exclusively to our api and api.github.com. The subsequent NetworkPolicy will effectively accomplish this goal:

 

---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-client-egress
spec:
  endpointSelector:
    # Apply policy to api client
    matchLabels:
      app: client
      type: production
  ingress:
  # Drop all incoming traffic
  - {}
  egress:
  # Allow API acccess to GitHub only via HTTPs
  - toFQDNs:
    - matchName: api.github.com
    toPorts:
    - ports:
      - port: "443"
  # Allow access to our API
  - toEndpoints:
    - matchLabels:
        app: api
        type: production
    toPorts:
    - ports:
      - port: "6090"
        protocol: TCP
  # Allow DNS queries
  - toEndpoints:
    - matchLabels:
        io.kubernetes.pod.namespace: kube-system
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: "53"
    rules:
      dns:
      - matchPattern: "*"

Now the pod can only talk to our and the GitHub API as well as query the kubernetes DNS server. Any request to other resources will fail now:

$ kubectl exec -it api-client -- curl -4 --connect-timeout 3 -v google.com
* processing: google.com
* Trying 142.250.184.238:80...
* ipv4 connect timeout after 2974ms, move on!
* Failed to connect to google.com port 80 after 3001 ms: Timeout was reached
* Closing connection
curl: (28) Failed to connect to google.com port 80 after 3001 ms: Timeout was reached

$ kubectl exec -it api-client -- curl -4 https://api.github.com/user
{
  "message": "Requires authentication",
  "documentation_url": "https://docs.github.com/rest/users/users#get-the-authenticated-user"
}

Let’s secure secure our api next. We don’t want the pod to able to communicate externally and it should only allow ingress traffic to our two routes. Since the /greetings Endpoint is supposed to be public, we’ll open the access to anyone whilst restricting the access to the /secret Route to our api-client.

---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-api-ingress
spec:
  endpointSelector:
    matchLabels:
      app: api
      type: production
  egress:
  # Deny any external traffic
  - {}
  ingress:
  # Allow access to /secret route from our api client 
  - fromEndpoints:
    - matchLabels:
        app: client
        type: production
    toPorts:
    - ports:
      - port: "6090"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/secret/.*"
          # Require HTTP header for authorization
          headers:
          - 'X-Security-Header: mysecret'
  # Allow access to /greetings route from anyone
  - toPorts:
    - ports:
      - port: "6090"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/greetings/.*"

Now we can only access the /secret route with the required header and from the api-client pod:

 

$ kubectl exec -it api-client -- curl api/secret/mysecret
Access denied

$ kubectl exec -it api-client -- curl -H 'X-Security-Header: mysecret' api/secret/mysecret
{
  "name": "mysecret",
  "secret": "adasge53whbetn"
}

Conclusion

Cilium NetworkPolicies are a powerful way to control network traffic within your cluster. By defining rules for ingress and egress traffic, you can secure your applications and isolate them as needed. This tutorial covered the basics of creating and using NetworkPolicies, but you can create more complex policies to suit your specific needs.

Need help with this topic? Feel free to send us a message or subscribe to our newsletter for more content on the topic.