Google Cloud Armor. Yes, Why Not?

Google Cloud Armor is a Google Cloud Platform (GCP) service to protect web applications. I've used it a couple of times, and it's a highly recommended service to use if you have applications running on Google's servers, especially because of it's ease of use.

In this article, I will present a very basic overview of the service, as well as some examples of use cases I had experience with.

# Google Cloud Armor Overview

Google Cloud Armor is GCP's offering to protect deployments from multiple types of threats, like denial-of-service (DDoS) attacks and application attacks such as cross-site scripting (XSS) and SQL injection (SQLi).

We use Google Cloud Armor to protect applications behind load balancers by using security policies. Those policies are configured by adding rules to them, and those rules define match conditions and actions to take in case those conditions are met.

The service provides preconfigured web application firewall (WAF) rules to detect attack patterns based on signatures. These rules help protect web applications and services from common attacks from the internet, such as those defined by OWASP.

Other notable features include Adaptive Protection, that helps protect agains L7 DDoS attacks and Named IP address lists, a feature exclusive to Managed Protection Plus subscribers, that provides preconfigured lists of IP addresses to be used when defining rules. For example, there's a list called `iplist-search-engines-crawlers` that matches IP addresses of search engine crawlers and another one called `iplist-known-malicious-ips` that matches IP addresses known to attack web applications as the names suggest.

# Security Policies

As previously mentioned, Google Cloud Armor is configured by creating security policies. These allow us to allow, deny, rate-limit or redirect requests based on a set of rules. Those rules consist of a match condition and an action to take when that condition is met.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509832/79292bd8-c258-67b8-2356-4c2b84d57489.png)

The image above shows the page to create a new security policy. As we can see, a security policy has a type. The "Backend security policy" allows us to protect resources such as instance groups, buckets in Cloud Storage and serverless applications deployed to App Engine, Cloud Run or Cloud Functions. I have no experience with the other policy types, but they are detailed in the [documentation](https://cloud.google.com/armor/docs/security-policy-overview#policy-types).

Please note we also define the "Default rule action" whether allowing or denying requests by default, so that our policy becomes a denylist or an allowlist based policy.

# Rules

Rules are what we use to customize the behavior of our security policies and the image below shows the page to create a new rule.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509832/e264bfde-5ac9-7992-deb2-c1b3edfcc0fa.png)


We need to define a condition so we can take action on incoming requests that match it. The condition can be set in two modes:
- Basic mode: we just provide a list of IP addresses or address ranges in the CIDR notation
- Advanced mode: this mode allow us to customize our rule by writing a match condition with the Common Expression Language (CEL). These are some examples of expressions we can write:
    - Match the country the request is coming from: `origin.region_code == '${country_code}'`
    - Match a certain user agent: `has(request.headers['user-agent']) && request.headers['user-agent'].contains('Chrome')`
    - Use a preconfigured WAF rule to prevent XSS attacks: `evaluatePreconfiguredWaf('xss-v33-stable', {'sensitivity': 1, 'opt_out_rule_ids': ['owasp-crs-v030301-id941120-xss']})`. Note we specify a `sensitivity` here. If we increase it, we get a better security level, but the risk of generating a false positive also increases. We can also use `opt_out_rule_ids` to specify signatures we want to ignore in the evaluation. We may do so if we realize we are getting too many false positives for a certaing signature.

In the "Action" field, we define what action to take when the incoming request matches the condition we defined. We can allow, deny, throttle or redirect those request and, also, apply a "Rate based ban". With that, we can ban a certain IP from accessing our application if too many requests are made, for example.

We can also use the "Enable preview only" to check the effects of a certain rule without actually affecting the requests. By checking the corresponding logs, we can check which requests would be affected by the rule if that was not in preview mode.

Finally, we set a priority for the rule. The incoming requests are evaluated for each of the rules in a security policy in the order determined by the "Priority" field. Note that the default rule has the lowest priority (`2,147,483,647`) and is evaluated last.

# Examples of Use

Below, are examples of use cases for Google Cloud Armor I had experience with. You can check more integration scenarios in the documentation below:

https://cloud.google.com/armor/docs/integrating-cloud-armor

## Google Kubernetes Engine (GKE)

Once we have a security policy in place, it's easy to integrate it to our application. We do so by creating a `BackendConfig` resource in our cluster through a manifest such as below:

```yaml
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  namespace: my-namespace
  name: cloud-armor-policy-backend
spec:
  securityPolicy:
    name: "my-cloud-armor-policy"
```

We specify our policy name in the `name: "my-cloud-armor-policy"` line. Then, we just need to add an annotation to the Kubernetes service we want the policy to be applied to.

```yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    cloud.google.com/backend-config: '{"default": "cloud-armor-policy-backend"}'
...
```

## Serverless Applications

A security policy can also be applied to applications running on GCP's serverless services such as App Engine, Cloud Run and Cloud Functions. This is mostly a matter of setting up a load balancer to handle the requests for the application since enabling Google Cloud Armor is as simple as selecting the desired security policy in the process. You can find a very detailed tutorial on how to setup a load balancer for serverless applications in the article below:

https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless

We can specify the security policy we want to apply when setting up the backend service for our load balancer.

Finally, it's important to note that services deployed to GCP's serverless services can be accessed directly through their default URLs. Such access bypasses the load balancer and the configured security policy, so we need to [setup the service's ingress settings](https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless#enable-armor) to allow only requests from the load balancer.

# References

- https://cloud.google.com/armor/docs/cloud-armor-overview
- https://cloud.google.com/armor/docs/threat-intelligence
- https://cloud.google.com/armor/docs/security-policy-overview
- https://cloud.google.com/armor/docs/integrating-cloud-armor
- https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless