avatarAbdellfetah SGHIOUAR

Summary

The article outlines the process of securing the default backend of a Google Kubernetes Engine (GKE) Ingress with Google Cloud Armor to enforce security policies on all traffic, including unmatched requests.

Abstract

When configuring a Google Cloud Load Balancer (GCLB) with GKE Ingress, a default backend is automatically provisioned to handle non-matching traffic. This backend responds with a 404 error when accessed without a configured rule. To protect this default backend and enforce specific security policies, such as allowing traffic only from certain regions or IP addresses, one must create a BackendConfig object and annotate the Kubernetes Service with it. The article demonstrates how to apply a Cloud Armor policy to the default backend by either modifying the existing default backend service in the kube-system namespace or by defining a custom default backend and annotating it with the desired security policy. This ensures that all traffic, including that directed to the default backend, adheres to the specified security rules.

Opinions

  • The author suggests that securing the default backend with Cloud Armor is essential for maintaining consistent security policies across all ingress traffic.
  • It is implied that the default behavior of the GKE Ingress default backend, which is to return a 404 error for unmatched requests, may not be secure or desirable for all deployments.
  • The article conveys that while GKE Ingress automates the creation of a default backend, users have the flexibility to customize this backend and apply security policies as needed.
  • The author indicates that the ability to apply Cloud Armor policies per backend provides flexibility in managing security at a granular level.
  • There is an opinion that the process of securing the default backend, while requiring additional configuration, is straightforward and enhances the overall security posture of the GKE Ingress setup.

Protecting GKE Ingress default backend with Cloud Armor

Source: https://readmedium.com/cloud-armor-setup-and-configuration-on-gke-hosted-application-9fe8847a0c0f

When deploying a GCLB (Google Cloud Load Balancer) with the native Ingress on GKE (aka gce-ingress). Depending on the rules configured in the Ingress object a default backend is added automatically for you. The purpose of this default backend is to handle traffic routing when a request doesn’t match the configured rules. Let take this Ingress as an example:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: gke-foobar-public-ip
    networking.gke.io/managed-certificates: foobar-certificate
    networking.gke.io/v1beta1.FrontendConfig: ingress-security-config
spec:
  rules:
  - host: foo.domain.com
    http:
      paths:
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: foo
            port:
              number: 8080
  - host: bar.domain.com
    http:
      paths:
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: bar
            port:
              number: 8080

This Ingress object defines and HTTPS load balancer with Google Managed Certificates and two rules for traffic:

  • Traffic matching foo.domain.com is sent to the foo backend on port 8080.
  • Traffic matching bar.domain.com is sent to the bar backend on port 8080.

Now what happens if let’s say we have a 3rd host test.domain.com which points to the same IP address of the load balancer ? If someone tries to connect to this host, they will get the following message:

response 404 (backend NotFound), service rules for the path non-existent

This response comes from a pod in the kube-system namespace called l7-default-backend-xxxxxxx-xxxx.

[Searching for information about Cloud Armor? Read Introducing Cloud Armor features to help improve efficacy: advanced rule tuning and auto deploy.]

This pod is deployed by default when you enable Ingress on GKE and an implicit rule is configured on the load balancer to route traffic that doesn’t match any of the rules to this pod. This can be visible in the configuration of the GCLB load balancer created by the Ingress.

Default backend rules in GCLB

This behaviour cannot be disabled unless you specify your own default backend in the spec of the Ingress:

spec:
  defaultBackend:
    service:
      name: my-products
      port:
        number: 60001

So what if you you have a policy you want to enforce on all your backends including the one added by GCLB ?

Let’s say for example you have a Cloud Armor policy that only allows certain region or IP addresses to connect to the load balancer. Let’s this policy as an example. Here i have a simple rule to only allow my public IP and block all other traffic.

Cloud Armor Poplicy

In order to apply this policy to the load balancer created by my Ingress above i have to create a BackendConfig Object and annotate the Kubernetes Service with this object. Cloud Armor Policies can be applied to individual backend services separately, giving you flexibility to choose which policy to apply to which backend. In my example this is what i need to do:

#Define a ackendConfig object
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: cloudarmor-allow-my-ip
spec:
  securityPolicy:
    name: allow-my-ip
---
#Annotate the service with the object above
apiVersion: v1
kind: Service
metadata:
  name: foo
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
    cloud.google.com/backend-config: '{"default": "cloudarmor-allow-my-ip"}'
spec:
  ports:
  - port: 8080
    targetPort: 8080
    name: http 
  selector:
    app: foo
  type: ClusterIP

If you apply these manifests and wait few seconds you will see that the load balancer will allow you to connect from the IP address defined in the policy but will return a 403 if you try to connect from anywhere else.

Except for the default backend. Because Clour Armor policies are per backend and in our case we are using the Ingress default backend the policy will not apply to this one. You will have explicitly set it up.

To do that simply create a BackendConfig object in the kube-system namespace (this is required as BackendConfigs cannot be references cross-namespaces) and edit the Service called default-http-backend in the kube-system namespace and add the annotation like the one above:

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: cloudarmor-allow-my-ip
  namespace: kube-system
spec:
  securityPolicy:
    name: allow-my-ip
kubectl edit svc default-http-backend -m kube-system

Now wait few seconds and try to with an unmatched path and you should get a 403.

In conclusion adding policies to default backend is simple and can be done two ways:

1 —Create a BackendConfig in the kube-system namespace and edit the existing Service for the implicit default-backend pod and annotate it with the policies you need.

2 — Define you own default backend pod and Service. Annotate the Service properly and add your default backend port to the spec part of the Ingress

Hope this helped ;)

Gke
Cloud Armor
Security
Kubernetes
Google Cloud Platform
Recommended from ReadMedium