1. Introduction
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
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.
2. Setup and Requirements
Self-paced environment setup
- 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.
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
.
- 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:
It should only take a few moments to provision and connect to the environment. When it is finished, you should see something like this:
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
3. Create the VPC network
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
4. Set up the test application
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!
We're now ready to explore the Juice Shop vulnerabilities and how to protect against them with Cloud Armor WAF rule sets.
5. Demonstrate known vulnerabilities
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 <...>
6. Define Cloud Armor WAF rules
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.
7. Observe Cloud Armor protection with OWASP ModSecurity Core Rule Set
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 <..>
8. Review Cloud Armor Security Rules
Now that we've created the security policy, let's take a look at exactly what rules have been configured.
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.
9. Observe Cloud Armor security policy logs
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
RCE log
Scanner detection log
Protocol attack log
Session fixation log
10. Lab Cleanup
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
11. Congratulations!
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
- Protect your application against the OWASP Top 10 vulnerabilities with Cloud Armor Preconfigured WAF rules
- Fine tune rules based on sensitivity levels
- Use the custom rules language reference for more specific security enforcement.