Implement Cloud Armor Security Policy/s using Terraform

BRK0018
7 min readJan 19, 2022

Google Cloud Armor helps you protect your Google Cloud deployments from multiple types of threats including DDoS [Distributed Denial-of-Service] attacks and Application attacks such as XSS [Cross-Site Scripting] and SQLi [SQL Injection]

Google Cloud Armor provides some automatic protections but other protections must be configured manually by you to get the most out of it’s powerful usage

Note: Google Cloud Armor provides protection only to the ‘Applications which are behind an External Load Balancer’ and some features are ‘only available for an External HTTP<S> Load Balancer

Protect your Google Cloud Deployments with Google Cloud Armor Security Policies

GCP Cloud Armor Security Policies help protect your application by providing Layer 7 filtering and scanning incoming requests for common web application attacks. Each Security Policy is a combination of set of rules that filter the traffic based on conditions such as an incoming request’s IP Address, IP Range, Region Code or Request Headers

These security policies are available *only* for backend services which are behind an external HTTP<S> load balancer

In this blog, I’ll cover how to implement security policies through Terraform for ‘Instance Groups as the backend service’ and primary focus would be to help define WAF Rules and consume them through Security Policy

In order to configure Google Cloud Armor Security Policies, specific IAM permissions are required. Please refer here

Let’s first define the instance_group using the terraform file

instance_group.tf

Applicable variables are defined in variables.tf as below [You’ll notice some more definitions/declarations in the file which will be consumed in the later sections of the blog]

variables.tf — snippet part: 1

Let’s define specific ‘variables’ for specific ‘rules’ which will be consumed with the Cloud Armor Security Policies.

First, the ‘Default ALLOW Rule’.

Depending on your business case needs, you can modify the below rule to either ‘allow’ or ‘deny’ with different set of priorities

variables.tf — snippet part: 2

Rule evaluation order is defined by the ‘rule priority’ from the lowest number to the highest number. The rule with the lowest numeric value assigned has the highest logical priority and is evaluated prior to values with lower logical priorities. The priority for each rule must be set to a number from ‘0 to 2147483646 inclusive’. The priority value 2147483647 is reserved for the ‘default rule’ and it is used as shown in the above image

Please refer ‘Rule evaluation order’ documentation for more details which is very informative and useful

Each Google Cloud Armor security policy contains a ‘default rule’ that is matched if none of the highest priority rules are matched or if there are no other rules in the policy. The default rule is automatically assigned a priority 2147483647 and it is always present in the security policy

Let’s define the Security Policy in cloud_armor_sec_policy.tf as we already have the ‘default rule’ defined in the variables file

cloud_armor_sec_policy.tf — Snipppet part: 1

Now, it’s time to consume the defined rule in the Security Policy with the https load balancer [https_loadbalancer.tf] like shown below:

https_loadbalancer.tf

If I expand the ‘groups’ section which is our Instance Group, it looks like below:

As this blog is focused more on to the Security Policies and WAF Rules., I’ll suggest you to refer ‘External HTTPS Load Balancer’ ‘Health checks’ and ‘Instance Groups’ for more detailed information about them

Pre-configured WAF Rules

Google Cloud Armor comes with Preconfigured rules for XSS, SQLi, LFI, RFI and RCE which are based on the OWASP Modsecurity core rule set

Each preconfigured rule consists of multiple signatures. Incoming requests are evaluated against the preconfigured rules. A request matches a preconfigured rule if the request matches any of the signatures that are associated with the preconfigured rule. A match is made when the ‘evaluatePreconfiguredExpr()’ command return the value ‘true’

During your observation if you see that the preconfigured rule is matching more traffic than necessary or if that rule is blocking more traffic than the actual needs, you can fine tune that rule to reduce the noise by disabling the unnecessary signatures. Use the syntax like below to reduce the noise from ‘SIGNATURE1’ and ‘SIGNATURE2’ for a particular ‘RULE’ using the custom rules language

evaluatePreconfiguredExpr(RULE, ['SIGNATURE1', 'SIGNATURE2'])

Using the above format, the rule for SQLi sensitivity level 3 can be written like below where the below rule helps ignore the traffic by unblocking the traffic from signatures ‘owasp-crs-v030001-id942421-sqli’ and ‘owasp-crs-v030001-id942432-sqli’

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

Each preconfigured rule has a sensitivity level that corresponds to a ModeSecurity paranoia level. Lower the sensitivity level indicates a higher confidence signature, which is less likely to generate a false positive. A higher sensitivity level increases security but also increases the risk of generating a false positive

Sensitivity Level 1 is advised for beginners, installations covering many different sites and applications and Sensitivity Level 4 is aimed at experienced users protecting installations with high security requirements. Running at Level 4, will likely produce a very high number of FPs which have to be treated before the site can go to production

Now., Let’s define a WAF rule for SQLi with the sensitivity level 2 [To configure a rule at a particular sensitivity level, you need to disable the signatures at other greater sensitivity levels]. In this case, we should disable the signatures which are levels 3 and 4 and the WAF rule looks like below

evaluatePreconfiguredExpr('sqli-stable', 
['owasp-crs-v030001-id942251-sqli',
'owasp-crs-v030001-id942420-sqli',
'owasp-crs-v030001-id942431-sqli',
'owasp-crs-v030001-id942460-sqli',
'owasp-crs-v030001-id942421-sqli',
'owasp-crs-v030001-id942432-sqli']
)

Similarly, If we’ve to define a rule with the sensitivity level 1., we should disable all the signatures at level 2, 3 and 4 respectively for that particular WAF

Now, as we understand how the rules get defined and how you use signatures to either block or unblock your traffic needs — Let’s declare WAF rule for OWASP TOP 10 in variables.tf.

You can add or delete specific signatures for each of these vulnerabilities based on your needs and also you can remove certain rules if necessary. Fine-tuning can be as granular as you can think of as long as you are able to identify the specific traffic requirements and define the correct syntax for that WAF rule

variables.tf — snippet part: 3

Let’s consume the OWASP TOP 10 WAF rule through the Security Policy

cloud_armor_sec_policy.tf — Snipppet part: 2

Recently, Apache Log4j issue made a big news in the cyber security space and google cloud deployments are no exception to protection. So, you can declare a WAF rule for the Apache Log4J as well in the variables.tf like shown below

variables.tf — snippet part: 4

and consume it through the Security Policy

cloud_armor_sec_policy.tf — Snipppet part: 3

Wonderful blog from google cloud is here about the recommendations to deal with the Apache Log4j 2 vulnerability

Finally, the cloud_armor_sec_policy.tf file looks like below:

cloud_armor_sec_policy.tf

In case if you want to only allow users from a specific region [say ‘AU’] and disallow the access to your application/s from other regions [‘non-AU’], you can define a variable in the variables.tf like below as a sample and consume it similar to other examples shown above under the security policy

sample for region specific access

The variables defined for iap and security_response_headers in variables.tf are like below

variables.tf — snippet part: 5

In case if you face any issues with Google Cloud Armor, there is an amazing detailed troubleshooting guide available to refer along with request logging

That’s it for now.

Hope this information finds useful.

Thanks for taking time to read!

--

--

BRK0018

Human being First, followed by A Husband and A Father of Two Smiles — Rest is the Magic!