This page shows how to use a BackendConfig custom resource to configure Google 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 Google 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:
- Create a BackendConfig.
- Create a Service, and associate one of its ports with the BackendConfig.
- 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
Familiarize yourself with the BackendConfig custom resource.
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 Google Cloud Armor Security Policy and rule
Create a Google Cloud Armor Security Policy:
gcloud beta compute security-policies create ca-how-to-security-policy \ --description "policy for Google 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
. Thebeta.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. Theselector
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
andtargetPort
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 labelapp: 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 Google 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
Google 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