# GCP Security Lab: Shielding Your Web Apps with Cloud Armor WAF

## Disclaimers & Personal Context

* **My Views:** This project and the views expressed in this blog post are my own and do not necessarily reflect the official stance or opinions of Google Cloud or any other entity.
    
* **Learning Journey:** This lab is another opportunity for me to expand my self-learning journey across various cloud providers. I want to recognize that Google Cloud Platform has phenomenal, expertly built courses. If you're looking for structured, official training, check out [**Cloud Skills Boost**](https://www.cloudskillsboost.google) – it's a fantastic resource!
    
* **Lab Environment:** This lab is for educational purposes only. All activities are simulated within my dedicated lab project.
    
* **Cost & Cleanup:** I'm using a fresh GCP account, similar to what a new user might experience. New GCP sign-ups typically come with a generous `$300 in free credits`, which should be more than enough to complete this lab without incurring significant costs. I'll provide a comprehensive cleanup section at the very end of this guide to help you remove all created resources and avoid any unexpected billing.
    
* **Crucial Tip:** Always perform cloud labs in a dedicated, isolated project to avoid impacting production environments or existing resources. Ask me how I know – I may or may not have broken things by testing in production before... and learned the hard way!
    

## Introduction

Welcome back, Amigos! In the digital world, web applications are often the primary gateway for users to interact with services. Unfortunately, this also makes them prime targets for a wide array of cyberattacks, from Distributed Denial of Service (DDoS) attempts to sophisticated Web Application Attacks (like SQL injection or Cross-Site Scripting).

This is where a Web Application Firewall (WAF) comes in. A WAF acts as a shield, inspecting incoming traffic to your web application and blocking malicious requests before they even reach your servers. In GCP, **Cloud Armor** provides WAF capabilities, offering robust protection at the network edge.

This post is part of an ongoing **GCP Cybersecurity Lab Series**, where I explore various security tools and practices in Google Cloud through hands-on labs. In *this* lab, I'm here to explore Cloud Armor with a practical walkthrough. By the end, our goal is to set up a simple web application, protect it using Cloud Armor policies, and then verify that Cloud Armor successfully blocks simulated malicious traffic.

**Be Prepared: This is a Comprehensive Lab!** This guide covers a lot of ground and involves many steps. Depending on your experience and how many breaks you take, this lab could easily take **2-4 hours (or more)** to complete from start to finish. Feel free to complete it in multiple sittings!

This lab is designed to be flexible: you can choose your preferred way to follow along:

* **Command Line Interface (CLI) Enthusiasts:** Copy-paste the provided `gcloud CLI` commands directly into Cloud Shell or your local terminal. This is often faster and more repeatable.
    
* **Console Explorers:** For many steps, I'll also provide instructions on how to achieve the same results by clicking your way through the intuitive Google Cloud Console. This is great for visual learners and understanding where things live.
    
    * *Note for Console users:* When following Console instructions, you won't be running the `gcloud CLI` commands. This means you'll need to manually retrieve details like internal VM IP addresses or Load Balancer IPs from the GCP Console UI when prompted.
        

I recommend using **Google Cloud Shell** for this lab. It comes with the `gcloud` CLI pre-installed and authenticated, saving you setup time. To access Cloud Shell, simply click the **rectangle icon with** `>_` (typically located at the top-right of the GCP Console window).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1753653067810/78e9391b-2c8f-4a3c-833d-900b0cc3bd7d.png align="center")

Let's get started!

## **Phase 0: Prerequisites & Environment Setup**

This initial phase ensures my GCP project is properly configured and ready to host my Cloud Armor lab.

**1\. Create or Select My Dedicated GCP Project**

* **Why a dedicated project?** Isolation is key for security labs. A dedicated project makes it easy to track resources, manage permissions, and clean up completely afterward.
    
* **Option A: Create a New Project (Cloud Console - Recommended):**
    
    1. Open the [GCP Console](https://console.cloud.google.com).
        
    2. At the top of the page, click on the **project selector dropdown**.
        
    3. In the "Select a project" dialog, click **NEW PROJECT** or if you just set this account up you can use the default project.
        
    4. Enter a descriptive **Project name** (e.g., gcp-cloudarmor-lab-jt).
        
    5. Click **CREATE**.
        
    6. Once the project is created, ensure it's selected in the project selector dropdown.
        
* **Option B: Select an Existing Project (gcloud CLI):**
    
    * If you already created the project via the console, you can select it:
        
        ```bash
        # My project ID for this lab will be GCP-CloudArmor-Lab-JT
        gcloud config set project gcp-cloudarmor-lab-jt
        ```
        

**2\. Set Project ID Environment Variable**

* **Why an environment variable?** Using an environment variable for my project ID makes `gcloud` commands cleaner, less prone to typos, and easily adaptable.
    
* **Important Security Note:** While I'm showing my project ID here for demonstration purposes, in real-world scenarios, it's generally good practice to **keep your project IDs private**.
    
* **How to set the variable (Cloud Shell or local terminal):**
    
    * **Crucial:** When you see `YOUR_PROJECT_ID` in `gcloud` commands or Console instructions throughout this lab, **replace it with your actual project ID.** My example project ID for this lab is `gcp-cloudarmor-lab-jt`.
        
    
    ```bash
    # Set my project ID for the lab
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt"
    echo "GCP_PROJECT_ID is set to: $GCP_PROJECT_ID"
    ```
    

**3\. Enable Required GCP APIs**

* **Why enable APIs?** Many GCP services require their specific APIs to be explicitly enabled in your project before you can interact with them. Enabling them now prevents errors later on.
    
* **How to enable (gcloud CLI - Recommended):**
    
    ```bash
    gcloud services enable \
        compute.googleapis.com \
        container.googleapis.com \
        networksecurity.googleapis.com \
        --project=$GCP_PROJECT_ID
    ```
    
    * *(This* *command may take a minute or two to complete as services are activated.)*
        
* **How to enable (Cloud Console - Alternative):**
    
    1. In the GCP Console, navigate to **APIs & Services &gt; Enabled APIs & Services**.
        
    2. Click **\+ ENABLE APIS AND SERVICES**.
        
    3. Search for and enable the following APIs one by one:
        
        * `Compute Engine API`
            
        * `Cloud Load Balancing API` (often part of Compute Engine, but good to check)
            
        * `Cloud Armor API` (search for "Cloud Armor" or "Network Security API")
            

### **Important Note for Cloud Shell Users: Redeclaring Variables**

If you're using Cloud Shell and decide to take a break, close your browser tab, or open a new Cloud Shell session, your shell's environment variables (like `$GCP_PROJECT_ID`, `$REGION`, `$ZONE`, etc.) will **not** persist automatically.

To avoid "command not found" or "Project ID must be specified" errors, it's a good practice to **re-export these variables at the beginning of each phase** when you return to the lab.

Here are the essential variables you'll use throughout the lab. Copy and paste this block if you ever restart your Cloud Shell:

```bash
# Essential Variables to redeclare if your Cloud Shell session restarts
export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
export REGION="us-central1"
export ZONE="${REGION}-a"

# IPs and Names (will be updated as they are created)
# If you restart your session AFTER a resource is created, you'll need to manually set these from the console/gcloud list commands.
export WEB_SERVER_VM_NAME="web-server-vm"
export LB_IP_NAME="web-app-lb-ip"
export CA_POLICY_NAME="web-app-policy"
```

*(When you see variable declarations like this at the start of a new phase, remember to run them if your session is fresh. Sometimes I keep italicized notes in the bottom as well for more information)*

## **Phase 1: Deploying the Simple Web Application**

**Goal:** My goal in this phase is to set up a basic web server on a Compute Engine VM. This VM will host a simple web page that I can then put behind a Load Balancer and protect with Cloud Armor. I'll configure it without an external IP address for security, as all external traffic will flow through the Load Balancer later.

**1\. Set Essential Variables (If Your Cloud Shell Session is New)**

* **Why:** If you're picking up this lab after a break or in a new Cloud Shell session, these variables might be unset. Re-exporting them ensures all subsequent commands work correctly.
    
* **How to set:** Copy and paste this block into your **Cloud Shell**:
    
    ```bash
    # Essential Variables to redeclare if your Cloud Shell session restarts
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
    export REGION="us-central1"
    export ZONE="${REGION}-a"
    
    # Names for resources
    export WEB_SERVER_VM_NAME="web-server-vm"
    ```
    

**2\. Deploy My Web Server VM** (`web-server-vm`)

* **Why:** This will be the backend server that hosts my web application content. I'm keeping it simple with a basic Debian VM and no external IP, as it will sit behind a Load Balancer.
    
* **How to deploy** (`gcloud CLI` - Recommended):
    
    ```bash
    echo "Deploying web server VM: $WEB_SERVER_VM_NAME in zone: $ZONE"
    gcloud compute instances create $WEB_SERVER_VM_NAME \
        --project=$GCP_PROJECT_ID \
        --zone=$ZONE \
        --machine-type=e2-micro \
        --network-interface=network=default,no-address \
        --tags=http-server,ssh \
        --create-disk=auto-delete=yes,boot=yes,device-name=$WEB_SERVER_VM_NAME,image=projects/debian-cloud/global/images/family/debian-12,mode=rw,size=10,type=pd-standard \
        --metadata=startup-script="#! /bin/bash\n# Initial setup will be done manually via SSH" \
        --labels=app=web-server,lab=cloud-armor
    ```
    
    *This command will take a couple of minutes to complete.*
    
* **How to deploy (Cloud Console - Alternative):**
    
    1. Navigate to **Compute Engine &gt; VM instances** in the GCP Console.
        
    2. Click **\+ CREATE INSTANCE**.
        
    3. **Name:** `web-server-vm`
        
    4. **Region:** `us-central1`
        
    5. **Zone:** `us-central1-a`
        
    6. **Machine configuration:** Series `E2`, Type `e2-micro`.
        
    7. **Boot disk:** Click **CHANGE**. Select `Debian GNU/Linux`, `Debian 12 (bookworm)` (or latest stable Debian). Size `10 GB`, `Standard persistent disk`. Click **SELECT**.
        
    8. **Firewall:** Ensure `Allow HTTP traffic` and `Allow HTTPS traffic` are **UNCHECKED**.
        
    9. **Advanced options &gt; Networking, Disks, Security, Management...**
        
        * Go to the **Networking** tab.
            
        * Under **Network interfaces**, click the pencil icon next to `default` (or your VPC network name).
            
            * **External IP:** Select `None`.
                
            * **Network tags:** Type `http-server` and press Enter. Then type `ssh` and press Enter.
                
            * Click **Done**.
                
    10. Click **CREATE**.
        

**3\. Configure Basic Firewall Rules for VM Management**

* **Why:** Even without an external IP, I need to be able to SSH into my VM for installation and configuration. This rule allows SSH access via Google's Identity-Aware Proxy (IAP), which is secure. I'll also add a firewall rule to allow the Load Balancer's health checks later.
    
* **How to configure** (`gcloud CLI` - Recommended):
    
    ```bash
    # Allow SSH access via IAP
    gcloud compute firewall-rules create allow-ssh-iap-web-vm \
        --project=$GCP_PROJECT_ID \
        --network=default \
        --action=ALLOW \
        --direction=INGRESS \
        --rules=tcp:22 \
        --source-ranges=35.235.240.0/20 \
        --target-tags=ssh \
        --description="Allow SSH from IAP to web server VM"
    
    # Allow incoming traffic from Load Balancer health checks and proxies
    # These are specific Google-managed IP ranges
    gcloud compute firewall-rules create allow-lb-health-check \
        --project=$GCP_PROJECT_ID \
        --network=default \
        --action=ALLOW \
        --direction=INGRESS \
        --rules=tcp:80 \
        --source-ranges=130.211.0.0/22,35.191.0.0/16 \
        --target-tags=http-server \
        --description="Allow LB health checks and proxy traffic to web server"
    ```
    
* **How to configure (Cloud Console - Alternative):**
    
    1. Navigate to **VPC Network &gt; Firewall rules** in the GCP Console.
        
    2. Click **\+ CREATE FIREWALL RULE**.
        
    3. **For** `allow-ssh-iap-web-vm`:
        
        * **Name:** `allow-ssh-iap-web-vm`
            
        * **Direction:** Ingress, **Action:** Allow
            
        * **Targets:** Specified target tags, enter `ssh`
            
        * **Source filter:** IPv4 ranges, enter `35.235.240.0/20`
            
        * **Protocols and ports:** Specified, TCP `22`. Click **CREATE**.
            
    4. **For** `allow-lb-health-check`:
        
        * **Name:** `allow-lb-health-check`
            
        * **Direction:** Ingress, **Action:** Allow
            
        * **Targets:** Specified target tags, enter `http-server`
            
        * **Source filter:** IPv4 ranges, enter `130.211.0.0/22,35.191.0.0/16`
            
        * **Protocols and ports:** Specified, TCP `80`. Click **CREATE**.
            

**4\. Enable Outbound Internet Access for VM (Cloud NAT)**

* **Why enable Cloud NAT?** As discussed, my `web-server-vm` has no external IP. To allow `sudo apt update` and `sudo apt install apache2` to work, the VM needs outbound internet access to reach package repositories. Cloud NAT provides this securely, without exposing the VM to unsolicited inbound internet traffic.
    
* **How to enable** (`gcloud CLI` - Recommended):
    
    * **Create a Cloud Router:** This is a prerequisite for a NAT gateway.
        
        ```bash
        export ROUTER_NAME="nat-router-${REGION}"
        export NAT_NAME="nat-gateway-${REGION}"
        export NETWORK_NAME="default" # Assuming your VM is in the 'default' VPC
        
        gcloud compute routers create ${ROUTER_NAME} \
            --project=$GCP_PROJECT_ID \
            --region=${REGION} \
            --network=${NETWORK_NAME} \
            --description="Cloud Router for NAT in ${REGION}"
        ```
        
    * **Create the NAT Gateway:** This connects to the router and provides the NAT functionality for the subnet where your VM lives.
        
        ```bash
        gcloud compute routers nats create ${NAT_NAME} \
            --project=$GCP_PROJECT_ID \
            --router=${ROUTER_NAME} \
            --region=${REGION} \
            --nat-all-subnet-ip-ranges \
            --auto-allocate-nat-external-ips \
            --enable-dynamic-port-allocation \
            --enable-logging \
            --log-filter=ERRORS_ONLY
        ```
        
        *This step may take a few minutes to complete as the NAT gateway provisions.*
        
* **How to enable (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Cloud NAT** in the GCP Console.
        
    2. Click **CREATE NAT GATEWAY**.
        
    3. **Gateway name:** `nat-gateway-us-central1`
        
    4. **VPC network:** `default`
        
    5. **Region:** `us-central1`
        
    6. **Cloud Router:** Select **Create new router**.
        
        * **Name:** `nat-router-us-central1`
            
        * Click **CREATE**.
            
    7. **NAT mapping:** Select **Automatic (recommended)**.
        
    8. **Region subnets:** Ensure your `us-central1` subnet is selected.
        
    9. **NAT IP addresses:** Select **Automatic IP address allocation**.
        
    10. Click **CREATE**.
        

**5\. Install Web Server (Apache2) & Serve Simple Content**

* **Why:** I need a running web server on my VM to test the Load Balancer and Cloud Armor. Apache2 is a common choice. I'll also create a simple `index.html` file that Apache will serve.
    
* **How to install (Inside VM SSH session - Recommended):**
    
    * First, ensure your VM is running and confirm its internal IP (`gcloud compute instances list`).
        
    * Then, SSH into `web-server-vm` from your **Cloud Shell**:
        
        ```bash
        gcloud compute ssh $WEB_SERVER_VM_NAME --zone=$ZONE --project=$GCP_PROJECT_ID
        ```
        
    * **Once inside the** `web-server-vm` SSH session, run the following commands:
        
        ```bash
        # Update package lists (this should now work due to Cloud NAT!)
        sudo apt update -y
        
        # Install Apache2
        sudo apt install apache2 -y
        
        # Verify Apache is running
        sudo systemctl status apache2
        ```
        
        *If Apache is active and running, press* `q` to exit the status view.
        
* **5.1. Create** `index.html` Manually
    
    * **Why:** We need a simple web page for Apache to serve. Manually creating this file using a text editor inside the VM is the most reliable way to ensure its content and formatting are perfect.
        
    * **How to create (Still inside** `web-server-vm` SSH session):
        
        1. Open the `index.html` file for editing using `sudo nano`:
            
            ```bash
            sudo nano /var/www/html/index.html
            ```
            
            *(If* `nano` isn't installed, you might need to install it first: `sudo apt update && sudo apt install nano -y`, but it worked on my machine) ← it works on my machine is one of my favorite subtle jokes… 😂
            
        2. You might see some default Apache HTML content. **Delete all existing content** in the `nano` editor.
            
        3. **Carefully copy and paste the entire HTML content below** into the `nano` editor. Make sure you get all lines and no extra spaces:
            
            ```xml
            <!DOCTYPE html>
            <html>
            <head><title>Cloud Armor Lab</title></head>
            <body>
            <h1>Hello from Cloud Armor Lab!</h1>
            <p>This is my simple web page.</p>
            </body>
            </html>
            ```
            
        4. **Save and Exit:**
            
            * Press `Ctrl+O` (Control + O) to "Write Out" (save).
                
            * Press `Enter` to confirm the filename (`/var/www/html/index.html`).
                
            * Press `Ctrl+X` (Control + X) to exit `nano`.
                
* **5.2. Verify Web Server Content Locally**
    
    * **Why:** Confirm that Apache is now serving the content I just put into `index.html`.
        
    * **How to verify (Still inside** `web-server-vm` SSH session):
        
        ```bash
        curl localhost
        ```
        
    * **Expected Result:** The `curl` `localhost` command output should display the full HTML content of your web page: `<!DOCTYPE html><html><head><title>Cloud Armor Lab</title></head><body><h1>Hello from Cloud Armor Lab!</h1><p>This is my simple web page.</p></body></html>`.
        
    * **After verifying, type** `exit` to close the SSH session and return to Cloud Shell:
        
        ```bash
        exit
        ```
        

## **Phase 2: Setting Up the Global HTTP(S) Load Balancer**

**Goal:** In this phase, I'll set up a Global External HTTP(S) Load Balancer. This Load Balancer will act as the public-facing entry point for my web application, distributing incoming traffic to my `web-server-vm`. It's a critical component for enabling Cloud Armor protection, as Cloud Armor policies attach to Load Balancer backend services.

* **Load Balancer Type:** I'll be setting up a Global External HTTP(S) Load Balancer. This type of Load Balancer is Google's highly scalable, globally distributed proxy that can handle HTTP and HTTPS traffic and route it to backends in different regions.
    

**1\. Set Essential Variables (If Your Cloud Shell Session is New)**

* **Why:** If you're picking up this lab after a break or in a new Cloud Shell session, these variables might be unset. Re-exporting them ensures all subsequent commands work correctly.
    
* **How to set:** Copy and paste this block into your **Cloud Shell**:
    
    ```bash
    # Essential Variables to redeclare if your Cloud Shell session restarts
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
    export REGION="us-central1"
    export ZONE="${REGION}-a"
    
    # Names for resources (from previous phases)
    export WEB_SERVER_VM_NAME="web-server-vm"
    export LB_IP_NAME="web-app-lb-ip"
    export CA_POLICY_NAME="web-app-policy" # Will use this later
    ```
    

**2\. Create an Unmanaged Instance Group**

* **Why:** Load Balancers don't directly target individual VMs. They send traffic to *instance groups*. An unmanaged instance group allows me to explicitly add my `web-server-vm` to it.
    
* **How to create (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating unmanaged instance group..."
    gcloud compute instance-groups unmanaged create web-app-instance-group \
        --zone=$ZONE \
        --project=$GCP_PROJECT_ID
    
    echo "Adding web-server-vm to the instance group..."
    gcloud compute instance-groups unmanaged add-instances web-app-instance-group \
        --instances=$WEB_SERVER_VM_NAME \
        --zone=$ZONE \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to create (Cloud Console - Alternative):**
    
    1. Navigate to **Compute Engine &gt; Instance groups** in the GCP Console.
        
    2. Click **\+ CREATE INSTANCE GROUP**.
        
    3. **Name:** `web-app-instance-group`
        
    4. **Instance group type:** Select `Unmanaged instance group`.
        
    5. **Location:** `Single zone`, select `us-central1-a`.
        
    6. **Network:** `default`.
        
    7. **VM instances:** Click **ADD VM INSTANCES** and select `web-server-vm`.
        
    8. Click **CREATE**.
        

**3\. Create a Health Check**

* **Why:** Load Balancers use health checks to determine if the backend instances are alive and responsive. Traffic is only sent to healthy instances. This is essential for proper load balancing and high availability.
    
* **How to create** (`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating HTTP health check..."
    gcloud compute health-checks create http web-app-health-check \
        --port=80 \
        --check-interval=5s \
        --timeout=5s \
        --unhealthy-threshold=2 \
        --healthy-threshold=2 \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to create (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing** in the GCP Console.
        
    2. In the left navigation, under "Load balancing resources," click **Health checks**.
        
    3. Click **\+ CREATE A HEALTH CHECK**.
        
    4. **Name:** `web-app-health-check`
        
    5. **Protocol:** `HTTP`.
        
    6. **Port:** `80`.
        
    7. Leave other defaults. Click **CREATE**.
        

**4\. Configure a Backend Service**

* **Why:** A Backend Service manages the connections between the Load Balancer and your instance groups. This is where the health check is applied, the Cloud Armor security policy will be attached, and crucially, access logging for requests will be enabled here.
    
* **How to configure** (`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating backend service and enabling access logging..."
    gcloud compute backend-services create web-app-backend-service \
        --protocol=HTTP \
        --port-name=http \
        --health-checks=web-app-health-check \
        --timeout=30s \
        --global \
        --enable-cdn \
        --enable-logging \
        --logging-sample-rate=1.0 \
        --project=$GCP_PROJECT_ID
    
    echo "Adding instance group to backend service..."
    gcloud compute backend-services add-backend web-app-backend-service \
        --instance-group=web-app-instance-group \
        --instance-group-zone=$ZONE \
        --global \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to configure (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing** in the GCP Console.
        
    2. In the left navigation, under "Load balancing resources," click **Backend services**.
        
    3. Click **\+ CREATE A BACKEND SERVICE**.
        
    4. **Name:** `web-app-backend-service`
        
    5. **Protocol:** `HTTP`.
        
    6. **Backend type:** `Instance group`.
        
    7. **Instance group:** Select `web-app-instance-group` and its zone `us-central1-a`.
        
    8. **Health check:** Select `web-app-health-check`.
        
    9. **Advanced configurations (for logging):**
        
        * Under "Cloud CDN", select **Enable Cloud CDN** (even if not using CDN, this is required for logging).
            
        * Under "Logging", select **Enable logging**.
            
        * **Sample rate:** `100` (for 100%).
            
    10. Click **CREATE**.
        

**5\. Reserve a Static External IP Address for the Load Balancer**

* **Why:** A static external IP address provides a permanent, public IP for your Load Balancer. This is necessary for external clients to reach your web application consistently.
    
* **How to reserve** (`gcloud CLI` - Recommended):
    
    ```bash
    echo "Reserving static external IP for Load Balancer..."
    gcloud compute addresses create $LB_IP_NAME \
        --ip-version=IPV4 \
        --global \
        --project=$GCP_PROJECT_ID
    
    # Capture the reserved IP address into a variable for later use
    export LB_IP=$(gcloud compute addresses describe $LB_IP_NAME --format="value(address)" --global --project=$GCP_PROJECT_ID)
    echo "Load Balancer External IP: $LB_IP"
    ```
    
* **How to reserve (Cloud Console - Alternative):**
    
    1. Navigate to **VPC network &gt; IP addresses** in the GCP Console.
        
    2. Click **\+ RESERVE EXTERNAL STATIC ADDRESS**.
        
    3. **Name:** `web-app-lb-ip`
        
    4. **Type:** `Global`.
        
    5. Click **RESERVE**. Note down the reserved IP address.
        

**6\. Create a URL Map**

* **Why:** A URL Map directs incoming requests from the Load Balancer's frontend to the appropriate backend service based on URL paths or hostnames. For a simple app, it just points all traffic to our single backend service.
    
* **How to create (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating URL map..."
    gcloud compute url-maps create web-app-url-map \
        --default-service=web-app-backend-service \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to create (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing** in the GCP Console.
        
    2. In the left navigation, under "Load balancing resources," click **URL maps**.
        
    3. Click **\+ CREATE URL MAP**.
        
    4. **Name:** `web-app-url-map`
        
    5. **Default backend:** Select `web-app-backend-service`.
        
    6. Click **CREATE**.
        

**7\. Configure the Global Forwarding Rule (Frontend)**

* **Why:** The Forwarding Rule defines the external IP address, port, and protocol that the Load Balancer listens on. This is the final piece that exposes your application to the internet.
    
* **How to configure** (`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating global HTTP forwarding rule..."
    gcloud compute target-http-proxies create http-proxy \
        --url-map=web-app-url-map \
        --project=$GCP_PROJECT_ID
    
    gcloud compute forwarding-rules create http-forwarding-rule \
        --address=$LB_IP_NAME \
        --global \
        --target-http-proxy=http-proxy \
        --ports=80 \
        --project=$GCP_PROJECT_ID
    ```
    
    *This step can take several minutes for the Load Balancer to become fully provisioned and for its IP to become publicly accessible. Be patient! (Mostly a reminder for myself 👀)*
    
* **How to configure (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing** in the GCP Console.
        
    2. Click **CREATE LOAD BALANCER**.
        
    3. Select **HTTP(S) Load Balancer**. Click **START CONFIGURATION**.
        
    4. **Internet to your VMs or serverless services.** Click **CONTINUE**.
        
    5. **Global external HTTP(S) Load Balancer.** Click **CONTINUE**.
        
    6. **Backend configuration:**
        
        * Click **Backend services and backend buckets** dropdown, then **Create a backend service**.
            
        * **Name:** `web-app-backend-service` (if not already created).
            
        * **Backend type:** `Instance group`.
            
        * **Instance group:** Select `web-app-instance-group`, `us-central1-a`.
            
        * **Health check:** Select `web-app-health-check`.
            
        * Click **CREATE**.
            
        * Click **OK**.
            
    7. **Routing rules:** Click **Path rules and host rules** dropdown. Ensure `Mode: Simple host and path rule`, `Hosts: Any`, `Paths: Any`, and `Backends: web-app-backend-service`.
        
    8. **Frontend configuration:**
        
        * Click **Add Frontend IP and port**.
            
        * **Name:** `http-frontend`
            
        * **Protocol:** `HTTP`
            
        * **IP address:** Select **Create IP Address**.
            
            * **Name:** `web-app-lb-ip`
                
            * Click **RESERVE**.
                
        * **Port:** `80`.
            
        * Click **DONE**.
            
    9. **Review and finalize:** Click **Review and finalize**.
        
    10. Click **CREATE**.
        
    11. After creation, note down the **IP Address** shown for your new Load Balancer (e.g., `34.X.Y.Z`).
        

**8\. Verify Load Balancer Access**

* **Why:** Before applying Cloud Armor, I need to confirm that my web application is publicly accessible through the Load Balancer's external IP address.
    
* **How to verify (From your browser or Cloud Shell):**
    
    * Go to your **Cloud Shell**. Ensure your `LB_IP` variable is set (if you used CLI to reserve) or manually copy the Load Balancer's external IP.
        
    * In your Cloud Shell, run `echo $LB_IP` to see the IP.
        
    * Now, use `curl` to access the web server through the Load Balancer's external IP:
        
        ```bash
        curl http://$LB_IP
        ```
        
    * **Expected Result:** You should see the HTML content: `<!DOCTYPE html><html><head><title>Cloud Armor Lab</title></head><body><h1>Hello from Cloud Armor Lab!</h1><p>This is my simple web page.</p></body></html>`
        
    * You can also paste `http://YOUR_LOAD_BALANCER_IP` directly into your web browser to confirm.
        
    * *Be patient: It can take up to 5-10 minutes for a newly created Global Load Balancer to become fully active and propagate across Google's network. I am being serious, I literally closed my computer after trying 14 times in a row, walked over to make a cup of coffee and when I came back it started working.*
        

## **Phase 3: Implementing Cloud Armor Basic Protection**

**Goal:** In this phase, I'll create my first Cloud Armor security policy and attach it to my Load Balancer's backend service. This policy will contain a simple rule to block traffic from a specific IP address, demonstrating Cloud Armor's ability to filter malicious requests at the network edge. I'll create multiple rules demonstrating various WAF capabilities, including IP blocking, detecting common web application attacks (SQLi, XSS), and geo-blocking.

**1\. Set Essential Variables (If Your Cloud Shell Session is New)**

* **Why:** If you're picking up this lab after a break or in a new Cloud Shell session, these variables might be unset. Re-exporting them ensures all subsequent commands work correctly.
    
* **How to set:** Copy and paste this block into your **Cloud Shell**:
    
    ```bash
    # Essential Variables to redeclare if your Cloud Shell session restarts
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
    export REGION="us-central1"
    export ZONE="${REGION}-a"
    
    # Names for resources (from previous phases)
    export WEB_SERVER_VM_NAME="web-server-vm"
    export LB_IP_NAME="web-app-lb-ip"
    export CA_POLICY_NAME="web-app-policy" # Name for your Cloud Armor policy
    ```
    
    *You'll need your Load Balancer's External IP (LB\_IP) from Phase 2, Part 5 for testing later. You can re-capture it with* `export LB_IP=$(gcloud compute addresses describe $LB_IP_NAME --format="value(address)" --global --project=$GCP_PROJECT_ID)` if needed.
    

**2\. Create a Cloud Armor Security Policy**

* **Why:** A security policy is a collection of rules that define how Cloud Armor protects your application. I'll create an empty policy first, then add rules to it.
    
* **How to create (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Creating Cloud Armor security policy: $CA_POLICY_NAME..."
    gcloud compute security-policies create $CA_POLICY_NAME \
        --description="Comprehensive Cloud Armor policy for web app protection" \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to create (Cloud Console - Alternative):**
    
    1. Navigate to **Network Security &gt; Cloud Armor** in the GCP Console.
        
    2. Click **CREATE POLICY**.
        
    3. **Policy name:** `web-app-policy`
        
    4. **Policy type:** `Edge security policy`.
        
    5. **Description:** `Comprehensive Cloud Armor policy for web app protection`.
        
    6. Leave other defaults. Click **CREATE POLICY**.
        

**3\. Add Multiple Rules for Different Attack Types**

* **Why:** Here, I'll configure Cloud Armor with several rules to demonstrate its versatility. Each rule will target a different type of threat or traffic filtering:
    
    * **IP Blocking:** Deny traffic from a specific malicious IP.
        
    * **SQL Injection (SQLi) Protection:** Use a preconfigured WAF rule to block common SQLi attack patterns.
        
    * **Cross-Site Scripting (XSS) Protection:** Use a preconfigured WAF rule to block common XSS attack patterns.
        
    * **Geo-Blocking:** Deny traffic from a specific country.
        
* **General Rule Structure:** Each rule has a **priority** (lower numbers are higher priority), a **match condition** (e.g., IP range, WAF expression, geo-location), and an **action** (e.g., `deny-403`). The **default rule** (priority 2147483647, always `allow`) acts as a fallback for traffic not matched by any other rule.
    
* **How to add (**`gcloud CLI` - Recommended):
    
    * **a. Rule: Deny a Specific IP Address (Priority 1000)**
        
        * **Why:** A basic but effective way to block known malicious actors or test specific clients.
            
        * **Action:**
            
            ```bash
            # IMPORTANT: Replace 'YOUR_EXTERNAL_IP_TO_BLOCK' with the actual IP you want Cloud Armor to block!
            # This should be your current home/office IP or a test IP you control.
            export BLOCK_IP_ADDRESS="YOUR_EXTERNAL_IP_TO_BLOCK"
            
            echo "Adding rule: Deny traffic from a specific IP ($BLOCK_IP_ADDRESS)..."
            gcloud compute security-policies rules create 1000 \
                --security-policy=$CA_POLICY_NAME \
                --description="Deny specific test IP" \
                --src-ip-ranges=$BLOCK_IP_ADDRESS \
                --action=deny-403 \
                --project=$GCP_PROJECT_ID
            ```
            
    * **b. Rule: Deny SQL Injection Attacks (Priority 1001)**
        
        * **Why:** Cloud Armor provides preconfigured WAF rules that use ModSecurity Core Rule Set (CRS) signatures to detect common web application attacks like SQLi.
            
        * **Action:**
            
            ```bash
            echo "Adding rule: Deny SQL Injection attacks using preconfigured WAF rule..."
            gcloud compute security-policies rules create 1001 \
                --security-policy=$CA_POLICY_NAME \
                --description="Deny SQL Injection attacks" \
                --expression="evaluatePreconfiguredWaf('sqli-v33-stable')" \
                --action=deny-403 \
                --project=$GCP_PROJECT_ID
            ```
            
            `evaluatePreconfiguredWaf('sqli-v33-stable')` tells Cloud Armor to use its built-in SQLi detection rules. `v33-stable` indicates the version of the rule set.
            
    * **c. Rule: Deny Cross-Site Scripting (XSS) Attacks (Priority 1002)**
        
        * **Why:** Similar to SQLi, preconfigured WAF rules detect XSS attacks, preventing malicious scripts from being injected into your web pages.
            
        * **Action:**
            
            ```bash
            echo "Adding rule: Deny Cross-Site Scripting (XSS) attacks using preconfigured WAF rule..."
            gcloud compute security-policies rules create 1002 \
                --security-policy=$CA_POLICY_NAME \
                --description="Deny Cross-Site Scripting (XSS) attacks" \
                --expression="evaluatePreconfiguredWaf('xss-v33-stable')" \
                --action=deny-403 \
                --project=$GCP_PROJECT_ID
            ```
            
    * **d. Rule: Deny Traffic from a Specific Country (Geo-Blocking) (Priority 1003)**
        
        * **Why:** Geo-blocking allows you to restrict access based on the geographic origin of the request. I'll block traffic from a country like "China" (CN) for demonstration. You can choose any country code (e.g., "RU" for Russia, "KP" for North Korea, etc.).
            
        * **Action:**
            
            ```bash
            echo "Adding rule: Deny traffic from China (CN) using geo-blocking..."
            gcloud compute security-policies rules create 1003 \
                --security-policy=$CA_POLICY_NAME \
                --description="Deny traffic from China (CN)" \
                --expression="origin.region_code == 'CN'" \
                --action=deny-403 \
                --project=$GCP_PROJECT_ID
            ```
            
            *You can replace* `'CN'` with any [ISO 3166-1 alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
            
* **How to add (Cloud Console - Alternative):**
    
    1. Navigate to **Network Security &gt; Cloud Armor** in the GCP Console.
        
    2. Click on your policy name (`web-app-policy`).
        
    3. Go to the **Rules** tab.
        
    4. Click **ADD RULE** for each rule below:
        
        * **For IP Blocking (Priority 1000):**
            
            * **Priority:** `1000`. **Action:** `Deny`, **HTTP response code:** `403 (Forbidden)`.
                
            * **IP addresses:** In **Source IP ranges**, enter your `BLOCK_IP_ADDRESS`.
                
            * **Description:** `Deny specific test IP`. Click **DONE**.
                
        * **For SQLi Protection (Priority 1001):**
            
            * **Priority:** `1001`. **Action:** `Deny`, **HTTP response code:** `403 (Forbidden)`.
                
            * **Condition (using a preconfigured WAF rule):**
                
                * Click `Condition`.
                    
                * Select `Preconfigured WAF rules (OWASP Top 10)`.
                    
                * Select `SQL Injection (SQLi)`.
                    
                * Select `SQLI - Core Rule Set (CRS) v3.3`.
                    
                * Click `Done`.
                    
            * **Description:** `Deny SQL Injection attacks`. Click **DONE**.
                
        * **For XSS Protection (Priority 1002):**
            
            * **Priority:** `1002`. **Action:** `Deny`, **HTTP response code:** `403 (Forbidden)`.
                
            * **Condition (using a preconfigured WAF rule):**
                
                * Click `Condition`.
                    
                * Select `Preconfigured WAF rules (OWASP Top 10)`.
                    
                * Select `Cross-Site Scripting (XSS)`.
                    
                * Select `XSS - Core Rule Set (CRS) v3.3`.
                    
                * Click `Done`.
                    
            * **Description:** `Deny Cross-Site Scripting (XSS) attacks`. Click **DONE**.
                
        * **For Geo-Blocking (Priority 1003):**
            
            * **Priority:** `1003`. **Action:** `Deny`, **HTTP response code:** `403 (Forbidden)`.
                
            * **Condition (using a custom expression):**
                
                * Click `Condition`.
                    
                * In the text box, enter `origin.region_code == 'CN'` (replace 'CN' with your desired country code).
                    
                * Click `Done`.
                    
            * **Description:** `Deny traffic from China (CN)`. Click **DONE**.
                

**5\. Attach the Security Policy to the Load Balancer's Backend Service**

* **Why:** The security policy needs to be explicitly attached to the backend service that serves your web application. This is the integration point where Cloud Armor starts inspecting traffic before it reaches your VM.
    
* **How to attach (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Attaching Cloud Armor policy to Load Balancer backend service..."
    gcloud compute backend-services update web-app-backend-service \
        --security-policy=$CA_POLICY_NAME \
        --global \
        --project=$GCP_PROJECT_ID
    ```
    
* **How to attach (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing** in the GCP Console.
        
    2. In the left navigation, click **Backend services**.
        
    3. Click on your backend service name (`web-app-backend-service`).
        
    4. Click **EDIT**.
        
    5. Scroll down to **Google Cloud Armor security policy**.
        
    6. Select your `web-app-policy` from the dropdown.
        
    7. Click **UPDATE**.
        

## **Phase 4: Simulating Attacks & Verifying Cloud Armor Blocks**

**Goal:** In this phase, I'll actively test my Cloud Armor security policy to confirm that it's correctly blocking various types of traffic I configured. This is where I see my WAF in action!

* **Remember:** Cloud Armor policies, once attached, can take a few minutes to propagate globally across Google's network. If your tests don't work immediately, wait 3-5 minutes and try again.
    

**1\. Set Essential Variables (If Your Cloud Shell Session is New)**

* **Why:** If you're picking up this lab after a break or in a new Cloud Shell session, these variables might be unset. Re-exporting them ensures all subsequent commands work correctly.
    
* **How to set:** Copy and paste this block into your **Cloud Shell**:
    
    ```bash
    # Essential Variables to redeclare if your Cloud Shell session restarts
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
    export REGION="us-central1"
    export ZONE="${REGION}-a"
    
    # Names for resources (from previous phases)
    export WEB_SERVER_VM_NAME="web-server-vm"
    export LB_IP_NAME="web-app-lb-ip"
    export CA_POLICY_NAME="web-app-policy"
    
    # Specific IP to block (from Phase 3)
    export BLOCK_IP_ADDRESS="YOUR_EXTERNAL_IP_TO_BLOCK" # Make sure this is still set to the IP you blocked
    ```
    
    *You'll need your Load Balancer's External IP (*`LB_IP`) for testing. You can re-capture it with `export LB_IP=$(gcloud compute addresses describe $LB_IP_NAME --format="value(address)" --global --project=$GCP_PROJECT_ID)` if needed. Run `echo $LB_IP` to see it.
    

**2\. Verify Normal Access to the Web Application (From an Allowed IP, like Cloud Shell)**

* **Why:** Before trying to block traffic, I need to confirm that my web application is still accessible as normal from an IP address that is *not* in my Cloud Armor deny list. This ensures the Load Balancer and web server are functioning correctly.
    
* **How to verify (From your browser or Cloud Shell):**
    
    * **Determine your current external IP address.** You can use a website like `whatismyip.com` or Google "what is my ip". Make sure this IP is **NOT** the one you configured Cloud Armor to block (`$BLOCK_IP_ADDRESS`). If it is, you'll need to use a different network/client for this test (e.g., your phone's mobile data, a VPN, your Cloud Shell, or a different computer).
        
    * In your Cloud Shell, ensure your `LB_IP` variable is set (from Phase 2, Part 5) or manually copy the Load Balancer's external IP from the Console (VPC network -&gt; IP addresses).
        
    * Now, use `curl` from your Cloud Shell to access the web server through the Load Balancer's external IP:
        
        ```bash
        curl http://$LB_IP
        ```
        
    * **Expected Result:** You should see the HTML content of your web page: `<!DOCTYPE html><html><head><title>Cloud Armor Lab</title></head><body><h1>Hello from Cloud Armor Lab!</h1><p>This is my simple web page.</p></body></html>`.
        
    * You can also paste `http://YOUR_LOAD_BALANCER_IP` directly into your web browser (ensuring your browser's IP is allowed) to confirm.
        

**3\. Simulate Attack: Access from the Blocked IP Address**

* **Why:** This is the core test of Cloud Armor's IP blocking effectiveness. I'll attempt to access the web application from the specific `BLOCK_IP_ADDRESS` that I configured in my Cloud Armor policy. Cloud Armor should intercept and deny this request.
    
* **How to simulate (From a client with the** `BLOCK_IP_ADDRESS`):
    
    * **Important:** You need to perform this test from the actual IP address that you configured in your Cloud Armor policy as `BLOCK_IP_ADDRESS`.
        
        * **If** `BLOCK_IP_ADDRESS` is your current home/office IP: Simply use your web browser or a local `curl`command from your computer.
            
        * **If** `BLOCK_IP_ADDRESS` is a different IP (e.g., a test server you control): You'll need to run the `curl`command from that specific test server.
            
    * Using your browser, try to navigate to `http://YOUR_LOAD_BALANCER_IP`.
        
    * Using `curl` from the `BLOCK_IP_ADDRESS` source:
        
        ```bash
        curl -v http://$LB_IP
        ```
        
* **Expected Result:** You should receive an HTTP `403 Forbidden` response. The browser will likely show a "403 Forbidden" error page. The `curl -v` output will explicitly show:
    
    ```bash
    < HTTP/1.1 403 Forbidden
    ```
    
    This confirms Cloud Armor successfully blocked the request based on the IP address.
    

**4\. Simulate Attack: SQL Injection (SQLi) Protection Test**

* **Why:** Now, let's test Cloud Armor's ability to detect and block common web application attacks using its preconfigured WAF rules. Even though our `index.html` isn't actually vulnerable to SQLi, Cloud Armor will still block requests containing common SQLi payloads.
    
* **How to simulate (From any allowed client):**
    
    * This test can be performed from any client whose IP address is *not* blocked by your policy. You can use your Cloud Shell's `curl`.
        
    * I'll construct a `curl` command that attempts to send a common SQLi payload in the URL path.
        
        ```bash
        echo "Attempting a SQL Injection attack (EXPECTED TO BE BLOCKED)..."
        curl -v "http://$LB_IP/index.html?id=1%20OR%201=1"
        ```
        
        `%20` is the URL-encoded space character.
        
* **Expected Result:** Just like with the IP block, you should receive an HTTP `403 Forbidden` response. The `curl -v`output will show `HTTP/1.1 403 Forbidden`. This confirms Cloud Armor's WAF rule detected the SQLi pattern.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1753835082584/da406455-5116-4c7e-a8ec-01d46edda8f5.png align="center")
    

**5\. Simulate Attack: Cross-Site Scripting (XSS) Protection Test**

* **Why:** Let's test another common web vulnerability – XSS. Cloud Armor has rules to detect XSS payloads that attempt to inject malicious client-side scripts.
    
* **How to simulate (From any allowed client):**
    
    * This test can also be performed from any client whose IP address is *not* blocked.
        
    * I'll send a `curl` command with a common XSS payload in the URL path.
        
        ```bash
        echo "Attempting a Cross-Site Scripting (XSS) attack (EXPECTED TO BE BLOCKED)..."
        curl -v "http://$LB_IP/index.html?query=<script>alert('XSS');</script>"
        ```
        
        *Note the URL-encoded characters like* `%3C` for `<` and `%3E` for `>`.
        
* **Expected Result:** Again, you should receive an HTTP `403 Forbidden` response. This confirms Cloud Armor's WAF rule detected the XSS pattern.
    

**6\. Simulate Attack: Geo-Blocking Test**

* **Why:** I configured Cloud Armor to deny traffic from a specific country (e.g., China - CN). This test verifies if the geo-blocking rule is working.
    
* **How to simulate (From a client in the blocked country - if possible):**
    
    * **This is the trickiest test, as it requires a client with an IP address from the country you chose to block.**
        
        * You might need to use a **VPN service** and connect to a server in that country (e.g., China if you blocked CN).
            
        * Once connected via VPN, ensure your external IP reflects the blocked country.
            
        * Then, open your web browser and navigate to `http://YOUR_LOAD_BALANCER_IP`.
            
    * **If you cannot get an IP from the blocked country:** You can skip this step, but understand that in a real scenario, this is how you'd verify geo-blocking. Cloud Armor's logs (in Phase 5) will still show attempts from the blocked country if they happen organically.
        
* **Expected Result:** If testing from a truly blocked country IP, you should receive an HTTP `403 Forbidden` response.
    

## **Phase 5: Monitoring & Analyzing Cloud Armor Logs**

**Goal:** In this phase, I'll dive into Cloud Logging to find the evidence of Cloud Armor's work. I want to confirm that Cloud Armor correctly logged the request from the blocked IP address and indicated that it took a "deny" action. This is crucial for auditing, incident response, and understanding my security posture.

**1\. Set Essential Variables (If Your Cloud Shell Session is New)**

* **Why:** If you're picking up this lab after a break or in a new Cloud Shell session, these variables might be unset. Re-exporting them ensures all subsequent commands work correctly.
    
* **How to set:** Copy and paste this block into your **Cloud Shell**:
    
    ```bash
    # Essential Variables to redeclare if your Cloud Shell session restarts
    export GCP_PROJECT_ID="gcp-cloudarmor-lab-jt" # Your Project ID
    export REGION="us-central1"
    export ZONE="${REGION}-a"
    
    # Names for resources (from previous phases)
    export WEB_SERVER_VM_NAME="web-server-vm"
    export LB_IP_NAME="web-app-lb-ip"
    export CA_POLICY_NAME="web-app-policy"
    export BLOCK_IP_ADDRESS="YOUR_EXTERNAL_IP_TO_BLOCK" # Make sure this is still set to the IP you blocked
    ```
    
    *You'll also need your Load Balancer's External IP (*`LB_IP`) from Phase 2, Part 5 for reference. You can re-capture it with `export LB_IP=$(gcloud compute addresses describe $LB_IP_NAME --format="value(address)" --global --project=$GCP_PROJECT_ID)` if needed.
    

**2\. Access Cloud Logging (Logs Explorer)**

* **Why:** Cloud Logging is where all audit and activity logs from Cloud Armor are sent. The Logs Explorer interface allows me to query and analyze these logs.
    
* **How to access:**
    
    * Navigate to **Operations &gt; Logging &gt; Logs Explorer** in the GCP Console.
        

**3\. Query for Cloud Armor Block Logs**

* **What I'm looking for:** I want to find the log entry generated by Cloud Armor when it blocked the request from my `BLOCK_IP_ADDRESS`. These logs are associated with the Load Balancer resource type and carry details from the `networksecurity.googleapis.com` service.
    
* **How to find (Logs Explorer Query):**
    
    * In Logs Explorer, **clear any previous text from the main "Query" text box.**
        
    * Then, paste the entire query below into that **main "Query" text box**.
        
    * **Important: Adjust the time range!** Make sure your time range selected at the top of Logs Explorer covers the exact time you performed the attack simulations in Phase 4. Set it to "Last 1 hour" or "Last 30 minutes" for recent events, or "Last 7 days" if you took a break.
        
    * **Note on variables:** Remember to replace `${GCP_PROJECT_ID}` with your actual Project ID, and `YOUR_EXTERNAL_IP_TO_BLOCK` with the actual IP you used.
        
    
    ```bash
    resource.type="http_load_balancer"
    logName="projects/${GCP_PROJECT_ID}/logs/requests"
    jsonPayload.statusDetails="denied_by_security_policy"
    jsonPayload.enforcedSecurityPolicy.name="web-app-policy"
    ```
    
    * Click **Run query**.
        
* **Analyze the Log Entry:**
    
    * **Look for logs with** `jsonPayload.statusDetails` set to `"denied_by_security_policy"`.
        
    * Expand the log entries that are found.
        
    * You should be able to see:
        
        * `jsonPayload.remoteIp`: This shows the IP that was blocked (your IP from your tests).
            
        * `jsonPayload.enforcedSecurityPolicy.outcome`: This will show `DENY`.
            
        * `jsonPayload.enforcedSecurityPolicy.priority`: This will be `1000`, `1001`, `1002`, or `1003` depending on which rule was matched.
            
        * `httpRequest.requestUrl`: This shows the URL that was accessed, which will contain your SQLi or XSS payloads for those tests.
            
    * This log provides a clear audit trail of all the blocked malicious attempts.
        
* **What this means:** This log entry serves as definitive proof that Cloud Armor successfully intercepted and denied the traffic based on your configured policies. In a real-world scenario, this log would be crucial for your security operations center (SOC) to identify and respond to attacks.
    

## **Phase 6: Cleaning Up Your Lab Environment**

* **Why clean up?** This is a critical final step in any cloud lab! To avoid incurring unnecessary costs for resources you're no longer using and to keep your GCP project tidy, it's essential to delete all the resources we created during this lab.
    

I'll provide `gcloud CLI` commands for quick cleanup, and I'll outline the Console steps as well.

**Important Note on Deletion Order:** Resources sometimes have dependencies (e.g., you can't delete a network router if a NAT gateway is using it, or a service account if it's attached to a running VM). I'll provide the commands in a logical order to minimize dependency errors.

**1\. Delete Cloud Armor Security Policy**

* **Why:** The Cloud Armor policy must be detached from the backend service before it can be deleted. This two-step process is crucial.
    
* **How to delete (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Attempting to detach Cloud Armor policy from backend service..."
    gcloud compute backend-services update web-app-backend-service \
        --security-policy="" \
        --global \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting Cloud Armor security policy: $CA_POLICY_NAME..."
    gcloud compute security-policies delete $CA_POLICY_NAME \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    ```
    
* **How to delete (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Load balancing &gt; Backend services** in the GCP Console.
        
    2. Click on your backend service name (`web-app-backend-service`).
        
    3. Click **EDIT**.
        
    4. Scroll down to **Google Cloud Armor security policy** and select `None`. Click **UPDATE**.
        
    5. Now, navigate to **Network Security &gt; Cloud Armor**.
        
    6. Select the checkbox next to `web-app-policy`.
        
    7. Click the **DELETE** button at the top and confirm the deletion.
        

**2\. Delete Load Balancer Components**

* **Why:** Load Balancer components (forwarding rule, proxy, URL map, backend service) must be deleted in a specific order.
    
* **How to delete (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Deleting Load Balancer forwarding rule..."
    gcloud compute forwarding-rules delete http-forwarding-rule \
        --global \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting Load Balancer target HTTP proxy..."
    gcloud compute target-http-proxies delete http-proxy \
        --global \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting Load Balancer URL map..."
    gcloud compute url-maps delete web-app-url-map \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting Load Balancer backend service..."
    gcloud compute backend-services delete web-app-backend-service \
        --global \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    ```
    
* **How to delete (Cloud Console - Alternative):**
    
    1. Go to **Network Services &gt; Load balancing**.
        
    2. In the left menu, navigate to each resource type (e.g., Forwarding Rules, Target Proxies, etc.) and delete them in the order listed above.
        

**3\. Delete the Web Server VM and Related Resources**

* **Why:** This removes the VM, its instance group, and the health check.
    
* **How to delete (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Deleting Load Balancer health check..."
    gcloud compute health-checks delete web-app-health-check \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting unmanaged instance group..."
    gcloud compute instance-groups unmanaged delete web-app-instance-group \
        --zone=$ZONE \
        --project=$GCP_PROJECT_ID \
        --quiet || true
    
    echo "Deleting Web Server VM: $WEB_SERVER_VM_NAME..."
    gcloud compute instances delete $WEB_SERVER_VM_NAME --zone=$ZONE --project=$GCP_PROJECT_ID --quiet || true
    ```
    
* **How to delete (Cloud Console - Alternative):**
    
    1. Navigate to **Compute Engine &gt; VM instances** in the GCP Console.
        
    2. Select the checkbox next to `web-server-vm`.
        
    3. Click the **DELETE** button at the top and confirm the deletion.
        
    4. Then, delete the Health Check and Instance Group from the Load Balancing menu.
        

**4\. Delete Networking and Final Cleanup**

* **Why:** This removes the NAT gateway, Cloud Router, and static IP address, which are all billable resources.
    
* **How to delete (**`gcloud CLI` - Recommended):
    
    ```bash
    echo "Deleting Cloud NAT Gateway..."
    export ROUTER_NAME="nat-router-${REGION}"
    export NAT_NAME="nat-gateway-${REGION}"
    gcloud compute routers nats delete ${NAT_NAME} --router=${ROUTER_NAME} --region=$REGION --project=$GCP_PROJECT_ID --quiet || true
    
    echo "Deleting Cloud Router..."
    gcloud compute routers delete ${ROUTER_NAME} --region=$REGION --project=$GCP_PROJECT_ID --quiet || true
    
    echo "Releasing static external IP address: $LB_IP_NAME..."
    gcloud compute addresses delete $LB_IP_NAME --global --project=$GCP_PROJECT_ID --quiet || true
    ```
    
* **How to delete (Cloud Console - Alternative):**
    
    1. Navigate to **Network Services &gt; Cloud NAT**. Delete the NAT gateway.
        
    2. Navigate to **Network Services &gt; Cloud Routers**. Delete the router.
        
    3. Navigate to **VPC network &gt; IP addresses**. Delete the static IP address.
        

**5\. Delete the Entire GCP Project (Most Comprehensive Cleanup)**

* **Why:** This is the most thorough way to ensure all resources and associated configurations are removed, guaranteeing no further costs.
    
* **How to delete (Cloud Console - Recommended):**
    
    1. Go to **IAM & Admin &gt; Settings** in the GCP Console.
        
    2. Click **SHUT DOWN**.
        
    3. Enter your **Project ID** (`gcp-cloudarmor-lab-jt`) to confirm. *Note: Project deletion can take several days to complete fully.*
        

## **Conclusion & Next Steps**

Phew! If you've made it this far, congratulations! You've successfully navigated a comprehensive GCP cybersecurity lab. You've built a multi-VM environment, simulated various attack scenarios, meticulously enabled logging and monitoring, and then acted as a digital detective to unearth the evidence of those attacks using Cloud Logging and Monitoring.

It's important to note that this lab was simplified for clarity and accessibility. In the real world, detecting a sophisticated threat actor is a far more complex challenge, involving advanced threat intelligence, anomaly detection, security information and event management (SIEM) systems, and deep forensic analysis. However, this lab serves as an excellent foundation and a great way to familiarize yourself with where crucial security signals reside within GCP. Understanding where to look and how logs and metrics behave in a simulated compromise is an invaluable skill.

**Your Challenge:** To deepen your learning, I challenge you to go back to Cloud Logging's Logs Explorer and Cloud Monitoring's Metrics Explorer. Don't just copy-paste my queries. Instead:

* Try to generate the log queries on your own. Experiment with different filters.
    
* Think about what other types of events or metrics you could use to detect these scenarios.
    
* Consider what insights you would genuinely benefit from in a real security operations center (SOC) for each attack type. How would you prioritize the information?
    

**What's Next?** This lab touched upon just a few facets of GCP security. Consider exploring:

* **Security Command Center's** other capabilities (even in the Free Tier).
    
* Setting up **VPC Service Controls** for data perimeter security.
    
* Implementing **Identity-Aware Proxy** for applications, not just SSH.
    
* Diving deeper into **Cloud IAM best practices**.
    

Just like always, the journey of learning cybersecurity never truly ends.

Thanks for making it to the end and thank you for reading. Keep learning!

jt
