Cloud Armor Preconfigured WAF Rules Codelab

Hello! Welcome to the Cloud Armor preconfigured WAF rules codelab!

Google Cloud Armor is Google's enterprise edge network security solution providing DDOS protection, WAF rule enforcement, and adaptive manageability at scale.

Cloud Armor has extended the preconfigured WAF rule sets to mitigate against the OWASP Top 10 web application security vulnerabilities. The rule sets are based on the OWASP Modsecurity core rule set version 3.0.2 to protect against some of the most common web application security risks including local file inclusion (lfi), remote file inclusion (rfi), remote code execution (rce), and many more.

In this codelab, you learn how to mitigate some of the common vulnerabilities by using Google Cloud Armor WAF rules.

What you'll learn

  • How to set up an Instance Group and a Global Load Balancer to support a service
  • How to configure Cloud Armor security policies with preconfigured WAF rules to protect against lfi, rce, scanners, protocol attacks, and session fixation
  • How to validate that Cloud Armor mitigated an attack by observing logs.

What you'll need

  • Basic knowledge of Google Compute Engine ( codelab)
  • Basic networking and TCP/IP knowledge
  • Basic Unix/Linux command line knowledge
  • It is helpful to have completed a tour of networking in GCP with Networking in the Google Cloud
  • (Optional) Complete the Cloudnet20 Cloud Armor lab for learning to protect workloads with SQL injection, IP-based, and geo-based rules.

Codelab topology & use case

119e13312f3cec25.jpeg

Figure 1 - Cloud Armor WAF rules codelab topology

The OWASP Juice Shop application is useful for security training and awareness, because it contains instances of each of the OWASP Top 10 security vulnerabilities—by design. An attacker can exploit it for testing purposes. In this codelab, we will use it to demonstrate some application attacks followed by protecting the application with Cloud Armor WAF rules. The application will be fronted by a Google Cloud Load Balancer, onto which the Cloud Armor security policy and rules will be applied. It will be served on the public internet thus reachable from almost anywhere and protected using Cloud Armor and VPC firewall rules.

Self-paced environment setup

  1. Sign in to Cloud Console and create a new project or reuse an existing one. If you don't already have a Gmail or Google Workspace account, you must create one.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Remember the project ID, a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID.

  1. Next, you'll need to enable billing in Cloud Console in order to use Google Cloud resources.

Running through this codelab shouldn't cost much, if anything at all. Be sure to to follow any instructions in the "Cleaning up" section which advises you how to shut down resources so you don't incur billing beyond this tutorial. New users of Google Cloud are eligible for the $300 USD Free Trial program.

Start Cloud Shell

While Google Cloud can be operated remotely from your laptop, in this codelab you will be using Google Cloud Shell, a command line environment running in the Cloud.

From the GCP Console click the Cloud Shell icon on the top right toolbar:

bce75f34b2c53987.png

It should only take a few moments to provision and connect to the environment. When it is finished, you should see something like this:

f6ef2b5f13479f3a.png

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on Google Cloud, greatly enhancing network performance and authentication. All of your work in this lab can be done with simply a browser.

Before you begin

Inside Cloud Shell, make sure that your project id is set up

gcloud config list project
gcloud config set project [YOUR-PROJECT-NAME]
PROJECT_ID=[YOUR-PROJECT-NAME]
echo $PROJECT_ID

Enable APIs

Enable all necessary services

gcloud services enable compute.googleapis.com
gcloud services enable logging.googleapis.com        
gcloud services enable monitoring.googleapis.com 

Create a VPC network

From Cloud Shell

gcloud compute networks create ca-lab-vpc --subnet-mode custom

Output

Created
NAME        SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
ca-lab-vpc  CUSTOM       REGIONAL

Create a subnet

From Cloud Shell

gcloud compute networks subnets create ca-lab-subnet \
        --network ca-lab-vpc --range 10.0.0.0/24 --region us-central1

Output

Created 
NAME           REGION       NETWORK       RANGE
ca-lab-subnet  us-central1  ca-lab-vpc    10.0.0.0/24

Create VPC firewall rules

After creating the VPC and subnet, now you will set up a few firewall rules. The first firewall rule will be used to allow all IPs to access the external IP of the test application's website on port 3000. The second firewall rule will be used to allow health-checks from source IP of the load balancers.

From Cloud Shell

gcloud compute firewall-rules create allow-js-site --allow tcp:3000 --network ca-lab-vpc

Output

Creating firewall...done.
NAME           NETWORK     DIRECTION  PRIORITY  ALLOW     DENY  DISABLED
allow-js-site  ca-lab-vpc  INGRESS    1000      tcp:3000        False

Create FW rules to allow health-checks from the Google health-check ranges.

From Cloud Shell

gcloud compute firewall-rules create allow-health-check \
    --network=ca-lab-vpc \
    --action=allow \
    --direction=ingress \
    --source-ranges=130.211.0.0/22,35.191.0.0/16 \
    --target-tags=allow-healthcheck \
    --rules=tcp

Output

Creating firewall...done.
NAME                NETWORK     DIRECTION  PRIORITY  ALLOW  DENY  DISABLED
allow-health-check  ca-lab-vpc  INGRESS    1000      tcp          False

Next step is to create the test application, in this case the OWASP Juice Shop web server.

When creating the compute instance, we are using a container image to ensure the server has the appropriate services. This server will be deployed in us-central1-c and will have a network tag which will allow health checks.

Create the OWASP Juice Shop application

Use the open source well-known OWASP Juice Shop application to serve as the vulnerable application. You can also use this application to do OWASP security challenges through their website.

From Cloud Shell

gcloud compute instances create-with-container owasp-juice-shop-app --container-image bkimminich/juice-shop \
     --network ca-lab-vpc \
     --subnet ca-lab-subnet \
     --private-network-ip=10.0.0.3 \
     --machine-type n1-standard-2 \
     --zone us-central1-c \
     --tags allow-healthcheck

Output

NAME                  ZONE           MACHINE_TYPE   PREEMPTIBLE  
owasp-juice-shop-app  us-central1-c  n1-standard-2               

INTERNAL_IP  EXTERNAL_IP     STATUS
10.0.0.3     <public IP>     RUNNING

Set up the Cloud load balancer component: instance group

Create the unmanaged instance group.

From Cloud Shell

gcloud compute instance-groups unmanaged create juice-shop-group \
    --zone=us-central1-c

Output

NAME              LOCATION       SCOPE  NETWORK  MANAGED  INSTANCES
juice-shop-group  us-central1-c  zone                     0

Add the Juice Shop GCE instance to the unmanaged instance group.

From Cloud Shell

gcloud compute instance-groups unmanaged add-instances juice-shop-group \
    --zone=us-central1-c \
    --instances=owasp-juice-shop-app

Output

Updated [https://www.googleapis.com/compute/v1/projects/<project name>/zones/us-central1-c/instanceGroups/juice-shop-group].

Set the named port to that of the Juice Shop application.

From Cloud Shell

gcloud compute instance-groups unmanaged set-named-ports \
juice-shop-group \
   --named-ports=http:3000 \
   --zone=us-central1-c

Output

Updated [https://www.googleapis.com/compute/v1/projects/<project name>/zones/us-central1-c/instanceGroups/juice-shop-group].

Now that you created the unmanaged instance group, the next step is to create a health check, backend service, URL map, target proxy, and forwarding rule.

Set up the Cloud load balancer component: health check

Create the health-check for the Juice Shop service port.

From Cloud Shell

gcloud compute health-checks create tcp tcp-port-3000 \
        --port 3000

Output

Created 
NAME           PROTOCOL
tcp-port-3000  TCP

Set up the Cloud load balancer component: backend service

Create the backend service parameters.

From Cloud Shell

gcloud compute backend-services create juice-shop-backend \
        --protocol HTTP \
        --port-name http \
        --health-checks tcp-port-3000 \
        --enable-logging \
        --global 

Output

NAME                BACKENDS  PROTOCOL
juice-shop-backend            HTTP

Add the Juice Shop instance group to the backend service.

From Cloud Shell

 gcloud compute backend-services add-backend juice-shop-backend \
        --instance-group=juice-shop-group \
        --instance-group-zone=us-central1-c \
        --global

Output

Updated [https://www.googleapis.com/compute/v1/projects/cythom-host1/global/backendServices/juice-shop-backend].

Set up the Cloud load balancer component: URL map

Create the URL map to send to the backend.

From Cloud Shell

gcloud compute url-maps create juice-shop-loadbalancer \
        --default-service juice-shop-backend

Output

NAME                     DEFAULT_SERVICE
juice-shop-loadbalancer  backendServices/juice-shop-backend

Set up the Cloud load balancer component: target proxy

Create the Target Proxy to front the URL map.

From Cloud Shell

gcloud compute target-http-proxies create juice-shop-proxy \
        --url-map juice-shop-loadbalancer

Output

NAME              URL_MAP
juice-shop-proxy  juice-shop-loadbalancer

Set up the Cloud load balancer component: forwarding rule

Create the forwarding rule for the Load Balancer.

From Cloud Shell

gcloud compute forwarding-rules create juice-shop-rule \
        --global \
        --target-http-proxy=juice-shop-proxy \
        --ports=80

Output

Created [https://www.googleapis.com/compute/v1/projects/cythom-host1/global/forwardingRules/juice-shop-rule].

Verify the Juice Shop service is online

From Cloud Shell

PUBLIC_SVC_IP="$(gcloud compute forwarding-rules describe juice-shop-rule  --global --format="value(IPAddress)")"

From Cloud Shell

echo $PUBLIC_SVC_IP

Output

<public VIP of service>

Wait a few minutes before continuing on, else you may retrieve a HTTP/1.1 404 Not Found response.

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP

Output

HTTP/1.1 200 OK
<...>

You can also go to the browser to view the Juice Shop!

428c18eee6708c28.png

We're now ready to explore the Juice Shop vulnerabilities and how to protect against them with Cloud Armor WAF rule sets.

In the interest of saving time, we will demonstrate the states before and after Cloud Armor WAF rules are propagated in condensed steps.

Observe an LFI vulnerability: path traversal

Local File Inclusion is the process of observing files present on the server by exploiting lack of input validation in the request to potentially expose sensitive data. The following simply shows a path traversal is possible. In your browser or with curl, observe an existing path served by the application.

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/ftp

Output

HTTP/1.1 200 OK
<...>

And also observe that path traversal works too:

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/ftp/../

Output

HTTP/1.1 200 OK
<...>

Observe an RCE vulnerability

Remote Code Execution includes various UNIX and Windows command injection scenarios allowing attackers to execute OS commands usually restricted to privileged users. The following shows a simple ls command execution passed in.

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/ftp?doc=/bin/ls

Output

HTTP/1.1 200 OK
<...>

You may remove the curl flags to observe the full output.

Observe a well-known scanner's access

Both commercial and open source scan applications for various purposes, including scanning for vulnerabilities. These tools use well-known User-Agent and other Headers. Observe curl works with a well-known User-Agent Header:

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP -H "User-Agent: blackwidow"

Output

HTTP/1.1 200 OK
<...>

Observe a protocol attack: HTTP splitting

Some web applications use input from the user to generate the headers in the responses. If the application doesn't filter the input properly, an attacker can potentially poison the input parameter with the sequence %0d%0a (the CRLF sequence that is used to separate different lines). The response could then be interpreted as two responses by anything that happens to parse it, like an intermediary proxy server, potentially serving false content in subsequent requests. Insert the sequence %0d%0a into the input parameter, which can lead to serving a misleading page.

From Cloud Shell

curl -Ii "http://$PUBLIC_SVC_IP/index.html?foo=advanced%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2035%0d%0a%0d%0a<html>Sorry,%20System%20Down</html>"

Output

HTTP/1.1 200 OK
<...>

Observe session fixation

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP -H session_id=X

Output

HTTP/1.1 200 OK
<...>

List the preconfigured WAF rules:

From Cloud Shell

gcloud compute security-policies list-preconfigured-expression-sets

Output

EXPRESSION_SET
Sqli-canary
RULE_ID
    owasp-crs-v030001-id942110-sqli
    owasp-crs-v030001-id942120-sqli
<...>

Create the Cloud Armor security policy

From Cloud Shell:

gcloud compute security-policies create block-with-modsec-crs \
    --description "Block with OWASP ModSecurity CRS"

Update the security policy default rule

Note that the default rule priority has a numerical value of 2147483647

From Cloud Shell:

gcloud compute security-policies rules update 2147483647 \
    --security-policy block-with-modsec-crs \
    --action "deny-403"

Since the default rule is configured with action deny, we must allow access from your IP. Please find your public IP (curl, ipmonkey, whatismyip, etc).

From Cloud Shell:

MY_IP=$(curl ifconfig.me)

Add the first rule to allow access from your IP (INSERT YOUR IP BELOW)

From Cloud Shell:

gcloud compute security-policies rules create 10000 \
    --security-policy  block-with-modsec-crs  \
    --description "allow traffic from my IP" \
    --src-ip-ranges "$MY_IP/32" \
    --action "allow"

Update the security policy to block LFI attacks

Apply the OWASP ModSecurity Core Rule Set that prevents path traversal for local file inclusions.

From Cloud Shell:

gcloud compute security-policies rules create 9000 \
    --security-policy block-with-modsec-crs  \
    --description "block local file inclusion" \
     --expression "evaluatePreconfiguredExpr('lfi-stable')" \
    --action deny-403

Update the security policy to block Remote Code Execution (rce)

Per the OWASP ModSecurity Core Rule Set, apply rules that look for rce, including command injection. Typical OS commands are detected and blocked.

From Cloud Shell:

gcloud compute security-policies rules create 9001 \
    --security-policy block-with-modsec-crs  \
    --description "block rce attacks" \
     --expression "evaluatePreconfiguredExpr('rce-stable')" \
    --action deny-403

Update the security policy to block security scanners

Apply the OWASP ModSecurity Core Rule Set to block well-known security scanners, scripting HTTP clients, and web crawlers.

From Cloud Shell:

gcloud compute security-policies rules create 9002 \
    --security-policy block-with-modsec-crs  \
    --description "block scanners" \
     --expression "evaluatePreconfiguredExpr('scannerdetection-stable')" \
    --action deny-403

Update the security policy to block protocol attacks

Per the OWASP ModSecurity Core Rule Set, apply rules that look for Carriage Return (CR) %0d and Linefeed (LF) %0a characters and other types of protocol attacks like HTTP Request Smuggling.

From Cloud Shell:

gcloud compute security-policies rules create 9003 \
    --security-policy block-with-modsec-crs  \
    --description "block protocol attacks" \
     --expression "evaluatePreconfiguredExpr('protocolattack-stable')" \
    --action deny-403

Update the security policy to block session fixation

Per the OWASP ModSecurity Core Rule Set, apply rules that...

From Cloud Shell:

gcloud compute security-policies rules create 9004 \
    --security-policy block-with-modsec-crs  \
    --description "block session fixation attacks" \
     --expression "evaluatePreconfiguredExpr('sessionfixation-stable')" \
    --action deny-403

Attach the security policy to the backend service

From Cloud Shell:

gcloud compute backend-services update juice-shop-backend \
    --security-policy block-with-modsec-crs \
    --global

Rules may take some time to propagate (but not more than 10 mins). Once confident that sufficient time has passed, test the vulnerabilities previously demonstrated to confirm Cloud Armor WAF rule enforcement in the next step.

Confirm the LFI vulnerability is mitigated

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/?a=../

Output

HTTP/1.1 403 Forbidden
<...>

Confirm the RCE attack is mitigated

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/ftp?doc=/bin/ls

Output

HTTP/1.1 403 Forbidden
<..>

Confirm well-known scanner detection

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP -H "User-Agent: blackwidow"

Output

HTTP/1.1 403 Forbidden
<..>

Confirm a protocol attack is mitigated

Per the OWASP ModSecurity Core Rule Set ver.3.0.2, the protocol attack is mitigated by

From Cloud Shell

curl -Ii "http://$PUBLIC_SVC_IP/index.html?foo=advanced%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2035%0d%0a%0d%0a<html>Sorry,%20System%20Down</html>"

Output

HTTP/1.1 403 Forbidden
<..>

Confirm session fixation attempts are blocked

From Cloud Shell

curl -Ii http://$PUBLIC_SVC_IP/?session_id=a

Output

HTTP/1.1 403 Forbidden
<..>

Now that we've created the security policy, let's take a look at exactly what rules have been configured.

d00e4102fc89e44f.png

Rules are evaluated by priority: lower numbers are evaluated first and once triggered, processing does not continue for rules with higher priority values.

  • Priority 9000 - Block LFI (local file inclusion)
  • Priority 9001 - Block RCE (remote code execution/command injection)
  • Priority 9002 - Block Scanners Detected
  • Priority 9003 - Block Protocol Attacks like HTTP splitting and HTTP smuggling
  • Priority 9004 - Block Session Fixation Attacks
  • Priority 10000 - Allow your IP to access the Website
  • Priority Default - Deny.

*Notice the "allow your IP" rule is configured with the highest priority number to allow access to the site, however blocks any attack.

From the Cloud Armor Console page, you can view details of the security policy and click the Logs tab followed by the View policy logs link to be directed to the Cloud Logging page. It will automatically filter based on the security policy of interest, e.g. resource.type:(http_load_balancer) AND jsonPayload.enforcedSecurityPolicy.name:(block-with-modsec-crs). Observe the 403 error response codes and expand the log details to observe the enforced security policy's name, matched field value, and further down the preconfigured expression IDs (or the signature id). The following screenshots show examples of the logs for the enforced security policies configured in this codelab.

LFI log

983a6cab0cff940d.png

RCE log

988a3a571f9d9d45.png

Scanner detection log

7ed661863ba27555.png

Protocol attack log

17ee3cbe0bd98939.png

Session fixation log

80d1ddfd0fe982e1.png

Clean up the resources now that you have completed the lab.

Run these commands to delete the Cloud Armor security policy, the Load Balancer, the instances, the firewall rules and the VPC network.

Remove the Cloud Armor security policy from the backend service

gcloud -q compute backend-services update juice-shop-backend --security-policy "" --global

Delete the Cloud Armor security policy

Deleting the security policy will automatically delete the associated rules.

gcloud -q compute security-policies delete block-with-modsec-crs

Delete the load balancer resources

These Load Balancer resources to be deleted include the forwarding rule, target-http-proxies, url-maps, backend, health-checks, and instance group.

gcloud -q compute forwarding-rules delete juice-shop-rule --global

gcloud -q compute target-http-proxies delete juice-shop-proxy

gcloud -q compute url-maps delete juice-shop-loadbalancer

gcloud -q compute backend-services delete juice-shop-backend \
    --global

gcloud -q compute health-checks delete tcp-port-3000

gcloud -q compute instance-groups unmanaged delete juice-shop-group --zone=us-central1-c

Delete the instance

gcloud -q compute instances delete owasp-juice-shop-app --zone us-central1-c

Delete the firewall rules, subnet and VPC

gcloud -q compute firewall-rules delete allow-health-check
gcloud -q compute firewall-rules delete allow-js-site
gcloud -q compute networks subnets delete ca-lab-subnet --region us-central1
gcloud -q compute networks delete ca-lab-vpc

Congratulations on completing the Cloud Armor preconfigured WAF rules codelab!

What we've covered

  • How to set up an instance group and a global Cloud Load Balancer
  • How to configure Cloud Armor security policies with preconfigured WAF rules to protect against lfi, rce, scanners, protocol attacks, and session fixation
  • How to validate that Cloud Armor mitigated some of the OWASP top 10 attacks via logs

Next steps