Introduction to Google Cloud Armor - Part 1

Policies and rules for WAF and DDoS protection

Krisztian Sala
Globant
7 min readDec 23, 2021

--

This article is broken into two parts. In the first one you will learn about Google Cloud Armor, what is it and how it use it to protect your applications using policies and rules. In the second one you will see how to apply it targets and use it in GKE. We will also explore the logs that it generates.

To get a high level glimpse of it’s features, check out the official overview by Google. Also, when you start using it and are curious about the implementation details, read the documentation, because it provides details about all the capabilities this tool provides.

I aim to keep this guide somewhere in the middle of those two, to provide enough details to start using Cloud Armor, but not to bore you about the unnecessary information.

Introduction

Cloud Armor is GCP’s DDoS protection solution, which means that it can provide automatic L3 and L4 DDoS protection, but also capable of giving Layer 7 protection. The former is a common capability that every major cloud provider and web hosting platform is capable of. But the latter includes some pretty innovative features, like the rate limiting, bot management and adaptive protection.

Cloud Armor is also a Web Application Firewall, meaning that it can recognize and block common attack types (think OWASP top 10). It contains preconfigured rules that can be fine tuned to sub rules. So you can protect your application against all SQL injection attacks, common XSS intrusions and a few session fixation attacks if you want.

You will create policies, that apply to load balancers. Each policy can contain multiple rules, where you specify the actual things that you want to block.
You can check the up-to-date pricing schema here about policies, rules and other influencing factors.

Set up Cloud Armor

Cloud Armor dashboard

There are multiple ways to create policies: the GCP dashboard, gcloud or even Terraform. We will focus on the first two for now.

Start by opening GCP and type cloud armor in the search bar. If you never used it before, you will have to activate the API (it doesn’t cost anything).
After that is done, it’s time to create your first policy.

Create a policy

Press the Create policy button on the top and you will be taken to a new screen:

Create new policy from the UI

Here choose a suiting name (this is the hardest part), because you can’t change it later. For the policy type, you can leave the Backend security policy, because it has more protection capabilities.
The default rule specifies that all requests are allowed or blocked by default. This is pretty taste and case specific. For public applications, where I want to block only a few IPs or countries, I usually let Allow as the default. For private applications, I usually choose Deny.

You can also choose the response code for the Deny action. Again, this is case specific, but you may want to use 403 if you want to let the user know that the action is forbidden. And you may want to choose 404 if you want to make the impression that the resource they are looking for doesn’t exist. There’s also 502, which I don’t use.

These were the required fields, you could press the Create policy button and it would be ready. But you have the options to create the rules in this screen and also to attach it to load balancers.

The adaptive protection is a premium feature, but you can still turn it on as a preview. It learns the request patterns accessing your site and will alert/suggest remediation based on abnormal traffic.

Policies can also be created using the gcloud command line utility:

gcloud compute security-policies create my-security-policy \
--description "My awesome policy"

Create rules

Rules also have an Allow or Deny action, similar to the policy base rule and in basic scenarios, if the base rule is Allow, then the other rules would be deny and vice versa. But there are more complicated cases and we will enter into some details later on.

For now, you can click on add rule to create your first rule in this policy. The basic mode is pretty… basic, you can allow or block IP ranges. So if I wanted to allow my IP only, I would set the base rule as deny and create a rule to allow my IP address (you can get it by calling curl ifconfig.me).

If Preview only is enabled, then the rule action will only be logged, but not actually enforced.

Creating a new rule (overview on the right side)

The advanced mode is where you would create more complex rules by using logical operators, filter based on header values or referencing pre-configured rules (WAF). There are many examples in the documentation.
You can also use logical operators to cut some costs by having multiple conditions inside a single rule.

Say if we wanted to block all requests coming from China and Russia (no offense) and the ones where the user agent contains curl, we would create something like this:

'[CN, RU]'.contains(origin.region_code) || request.headers['user-agent'].contains('curl')

WAF rules

Check this page to see all the possibilities regarding the vulnerability types that can be filtered out.

To block all SQL injection and cross site scripting attacks, we would set this rule: evaluatePreconfiguredExpr('xss-stable') || evaluatePreconfiguredExpr('sqli-stable').

I don’t recommend doing this right away in a production environment. This is one of the cases, where you would turn on the preview mode, so all the requests will be logged that would be blocked by these rules. After, conduct some extensive testing, and if you see that some request would have been blocked that normally shouldn’t have been, you can make some exceptions.

Let’s say you notice owasp-crs-v030001-id942460-sqli is appearing a lot of times during testing. If you check the table here, you notice that this is a level 3 sensitivity rule, indicating a higher chance of false positive. So we want to exclude it. In this case, we can use the rule like so:

evaluatePreconfiguredExpr('sqli-stable', ['owasp-crs-v030001-id942460-sqli']) 

The second part of the function can contain an array of one or more elements separated by a comma, where you can specify all the signatures that you want to exclude from the general rule.

In the second part of this series I’m gonna show you a script that can be used to filter the unique signatures that appear in the logs.

Log4j

In December 2021 a major vulnerability showed up in the very commonly used log4j Java library. It was so critical (CVSS score of 10) and easy to exploit, that the Cloud Armor team introduced a new expression that can be used to protect against it. You can do so by adding this rule:

evaluatePreconfiguredExpr('cve-canary')

Find out more information about this rule and the vulnerability in their blog post.

Rate limiting

This is a very useful L7 protection feature, that at the time of writing is still in preview (but works pretty well). Currently these rules can only be created from the command line, but it might get UI support in the future.

Here’s an example:

gcloud beta compute security-policies rules create 700 \
--security-policy my-security-policy \
--src-ip-ranges="100.0.0.0/24" \
--action=throttle \
--rate-limit-threshold-count=100 \
--rate-limit-threshold-interval-sec=60 \
--conform-action=allow \
--exceed-action=deny-404 \
--enforce-on-key=IP

You identify the rule by it’s priority (700 in the above example) and the security policy it’s applied to (my-security-policy).

The action can be either throttle or rate-based-ban. The rule will calculate the amount of requests that is arriving from each specific IP address (defined by enforce-on-key) and if those exceed the limit specified by rate-limit-threshold-count(100) in the last rate-limit-threshold-interval-sec (60) seconds, then the following requests will be denied with a 404 status code (exceed-action).
In case of throttle, when the amount of requests are again lower than the allowed limit in the time interval, the user can continue to make new requests and get responses. However with rate-based-ban, you can block them from making new requests for a set amount of time (for example 10 minutes).

If we want to block in preview all requests for 5 minutes coming from the US if they exceed 100 requests in the last 10 minutes, we can create this rule:

gcloud beta compute security-policies rules create 825 \
--security-policy my-security-policy \
--expression "origin.region_code == 'US'" \
--action=rate-based-ban \
--ban-duration-sec=300 \
--ban-threshold-count=100 \
--ban-threshold-interval-sec=600 \
--conform-action=allow \
--exceed-action=deny-403 \
--enforce-on-key=IP \
--description "Ban US requests" \
--preview

The ban can be combined with the throttling to create complex rules.

To see all the available options that can be specified, check the documentation here.

Other features

You can use bot management to block non-human visitors by providing them reCAPTCHA challenges. This is useful if you suspect that your website could be subject of web scraping, common cases including eCommerce, news sites, job boards etc.

Adaptive protection uses machine learning to detect anomalies in traffic. Without the managed protection plus, you will only get an alert if something unusual is happening, but no attack signatures or ready-made rules to deploy quickly.

You can use named IP lists to easily allow all traffic from a few CDN providers: evaluatePreconfiguredExpr(‘sourceiplist-cloudflare’)

In this first article we got familiar with Cloud Armor, introduced policies and rules and explored the different kinds of rules and use cases that exist.

In the next part, we will apply these rules to target load balancers, both from the UI and from Kubernetes configuration. After this, we will explore the logging and use scripts to filter the logs.

--

--

Krisztian Sala
Globant
Writer for

DevOps Engineer with interests in security, web development and microservices.