Configuring Cloud Armor

This page shows how to use a BackendConfig custom resource to configure Cloud Armor in Google Kubernetes Engine.

Overview

In a GKE cluster, incoming traffic is handled by HTTP(S) Load Balancing, which is a component of Cloud Load Balancing. Typically, the HTTP(S) load balancer is configured by the GKE Ingress controller, which gets configuration information from a Kubernetes Ingress object. The Ingress is associated with one or more Service objects. Each Service object holds routing information that is used to direct an incoming request to a particular Pod and port.

Beginning with Kubernetes version 1.10.5-gke.3, you can provide additional configuration for the load balancer by associating a Service port with a custom resource named BackendConfig.

The GKE Ingress controller reads configuration information from the BackendConfig and sets up the load balancer accordingly. A BackendConfig holds configuration information that is specific to Cloud Load Balancing. Kubernetes Ingress and Service resources do not offer a way to configure provider-specific features like Cloud Armor; BackendConfig provides a way for you to do that configuration.

Here's the big picture of how you set up a BackendConfig in this exercise:

  1. Create a BackendConfig.
  2. Create a Service, and associate one of its ports with the BackendConfig.
  3. Create an Ingress, and associate the Ingress with the (Service, port) pair.

Before you begin

To prepare for this task, perform the following steps:

  • Ensure that you have enabled the Google Kubernetes Engine API.
  • Enable Google Kubernetes Engine API
  • Ensure that you have installed the Cloud SDK.
  • Set your default project ID:
    gcloud config set project [PROJECT_ID]
  • If you are working with zonal clusters, set your default compute zone:
    gcloud config set compute/zone [COMPUTE_ZONE]
  • If you are working with regional clusters, set your default compute region:
    gcloud config set compute/region [COMPUTE_REGION]
  • Update gcloud to the latest version:
    gcloud components update

Creating a namespace

Create a Kubernetes namespace for the objects in this guide:

kubectl create namespace cloud-armor-how-to

Creating a Deployment

This Deployment manifest declares that you want to run two replicas of the hello-app web application:

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: cloud-armor-how-to
  name: my-deployment
spec:
  selector:
    matchLabels:
      app: hello-app
  replicas: 2
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
      - name: hello-app-container
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080

Copy the manifest to a file named my-deployment.yaml, and create the Deployment:

kubectl apply -f my-deployment.yaml

Creating a Cloud Armor Security Policy and rule

Create a Cloud Armor Security Policy:

gcloud beta compute security-policies create ca-how-to-security-policy \
    --description "policy for Cloud Armor how-to topic"

Create a rule for your security policy:

gcloud beta compute security-policies rules create 1000 \
    --security-policy ca-how-to-security-policy \
    --description "Deny traffic from 192.0.2.0/24." \
    --src-ip-ranges "192.0.2.0/24" \
    --action "deny-404"

View the security policy:

gcloud beta compute security-policies describe ca-how-to-security-policy

Output:

...
kind: compute#securityPolicy
name: ca-how-to-security-policy
rules:
- action: deny(404)
  description: Deny traffic from 192.0.2.0/24.
  kind: compute#securityPolicyRule
  match:
    config:
      srcIpRanges:
      - 192.0.2.0/24
    versionedExpr: SRC_IPS_V1
  preview: false
  priority: 1000
  ...

Creating a BackendConfig

Here's a manifest for a BackendConfig. The manifest specifies a security policy:

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  namespace: cloud-armor-how-to
  name: my-backend-config
spec:
  securityPolicy:
    name: "ca-how-to-security-policy"

Copy the manifest to a file named my-backend-config.yaml, and create the BackendConfig:

kubectl apply -f my-backend-config.yaml

View the BackendConfig:

kubectl get backendconfig my-backend-config --namespace cloud-armor-how-to --output yaml

The output shows the specified security policy:

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: my-backend-config
  namespace: cloud-armor-how-to
  ...
spec:
  securityPolicy:
    name: ca-how-to-security-policy

Creating a Service

Here's a manifest for a Service:

apiVersion: v1
kind: Service
metadata:
  namespace: cloud-armor-how-to
  name: my-service
  labels:
    app: hello-app
  annotations:
    beta.cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
spec:
  type: NodePort
  selector:
    app: hello-app
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080

Save the manifest to a file named my-service.yaml, and create the Service:

kubectl apply -f my-service.yaml

View the Service:

kubectl get service my-service --namespace cloud-armor-how-to --output yaml

The output is similar to this:

apiVersion: v1
kind: Service
metadata:
  annotations:
    beta.cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
  labels:
    app: hello-app
  name: my-service
  namespace: cloud-armor-how-to
  ...
spec:
  clusterIP: 10.19.249.137
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32629
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello-app
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

For the purpose of this exercise, these are the important things to note about your Service:

  • Port 80 of the Service is associated with a BackendConfig named my-backend-config. The beta.cloud.google.com/backend-config annotation specifies this.

  • The Service has type NodePort. This is the required type for Services that are going to be associated with an Ingress.

  • Any Pod that has the label app: hello-app is a member of the Service. The selector field specifies this.

  • Traffic directed to the service on TCP port 80 is routed to TCP port 8080 in one of the member Pods. The port and targetPort fields specify this.

Reserving a static external IP address

Reserve a static external IP address:

gcloud compute addresses create cloud-armor-how-to-address --global

View the static external IP address:

gcloud compute addresses list --filter "name=cloud-armor-how-to-address"

Output:

NAME                        REGION  ADDRESS        STATUS
cloud-armor-how-to-address          203.0.113.2    RESERVED

Creating an Ingress

Here's a manifest for an Ingress:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: cloud-armor-how-to
  name: my-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "cloud-armor-how-to-address"
spec:
  backend:
    serviceName: my-service
    servicePort: 80

Copy the manifest to a file named my-ingress.yaml, and create the Ingress:

kubectl create -f my-ingress.yaml

Wait a few minutes for the Kubernetes Ingress controller to configure a Cloud load balancer, and then view the Ingress:

kubectl get ingress my-ingress --output yaml --namespace cloud-armor-how-to

Output:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  namespace: cloud-armor-how-to
  ...
spec:
  backend:
    serviceName: my-service
    servicePort: 80
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.2

For the purpose of this exercise, here are the important things to note about your Ingress:

  • The IP address for incoming traffic is listed under loadBalancer:ingress:.

  • The Ingress has one rule that applies to incoming HTTP requests from any host. This is because there is no host field in the rule. So by default, the rule applies to all hosts.

  • All incoming requests are treated the same, regardless of the URL path. This is specified by the path value /*.

  • Incoming requests are routed to a Pod that is a member of my-service. In this exercise, the member pods have the label app: hello-app.

  • Requests are routed to the Pod on the target port specified in my-service. In this exercise, the Pod target port is 8080.

Viewing the web app

Wait several minutes. Then, in your browser, enter your static external IP address.

The page displays the response from the hello-app web application that is running in one of the Pods of your Deployment:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-f9fbj

Use curl to view the web app:

curl -v [STATIC_ADDRESS]

where [STATIC_ADDRESS] is your static external IP address.

The output is the response from hello-app:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-zpb94

Detaching the security policy

To detach the security policy from an Ingress, set the security policy name to empty in the BackendConfig. Here is an example of detaching the security policy:

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  namespace: cloud-armor-how-to
  name: my-backend-config
spec:
  securityPolicy:
    name: ""

Limitations

Quota limit

There is a hard limit on the number of Cloud Armor rules you can write while this feature is in Beta. This limit will be removed when the feature is generally available.

Only IP whitelist and blacklist

Cloud Armor supports only IP whitelist and IP blacklist.

Troubleshooting

BackendConfig not found

This error occurs when a BackendConfig for a Service port is specified in the Service annotation, but the actual BackendConfig resource could not be found. This can occur if you have not created the BackendConfig resource at all, created it in the wrong namespace, or misspelled the reference in the Service annotation.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: error getting BackendConfig for port 80 on service “default/my-service”:
no BackendConfig for service port exists

Security Policy not found

After the Ingress object is created, if the security policy isn’t properly associated with the load balancer service, evaluate the Kubernetes event to see if there is a configuration mistake. Specifically, if your BackendConfig specifies a nonexistent policy, a warning event will be emitted periodically. To fix this problem, make sure you specify the correct security policy, by name, in your BackendConfig.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: The given security policy "my-policy" does not exist.

Cleaning up

After completing the exercises on this page, follow these steps to remove the resources to prevent unwanted charges incurring on your account:

Delete the Kubernetes objects that you created for this exercise:

kubectl delete ingress my-ingress --namespace cloud-armor-how-to
kubectl delete service my-service --namespace cloud-armor-how-to
kubectl delete backendconfig my-backend-config --namespace cloud-armor-how-to
kubectl delete deployment my-deployment --namespace cloud-armor-how-to
kubectl delete namespace cloud-armor-how-to

Delete your static external IP address:

gcloud compute addresses delete cloud-armor-how-to-address --global

Delete your security policy:

gcloud beta compute security-policies delete ca-how-to-security-policy

What's next

Was this page helpful? Let us know how we did:

Send feedback about...

Kubernetes Engine