LAB46_GCP_Data Exfiltration Prevention: VPC Service Controls

Data Exfiltration Prevention: VPC Service Controls

☁ GCP 🔥 Advanced VPC-SC Security Perimeter Zero Trust

Scenario

Your organization handles highly sensitive PII data in Cloud Storage and BigQuery. You are worried that a malicious insider with `roles/storage.admin` could simply download the data to their personal laptop over the public internet. IAM is not enough to stop this. You must deploy VPC Service Controls (VPC-SC) to draw a hardened Zero-Trust API perimeter around your GCP project.

Your Objectives
  • Provision a google_access_context_manager_access_policy at the Organization level.
  • Provision a google_access_context_manager_service_perimeter wrapping your project.
  • Configure the perimeter to explicitly block the storage.googleapis.com and bigquery.googleapis.com restricted services.
  • Provision an Access Level (google_access_context_manager_access_level) that pokes a tiny hole in the perimeter only for users connecting from a specific Corporate IP address.

Additional Context

How VPC-SC Works: VPC-SC operates at the Google API edge. When you put a project into a perimeter and restrict `storage.googleapis.com`, GCP immediately blocks all API traffic to Cloud Storage for that project that originates from outside the perimeter (i.e. the public internet or other GCP projects). Even an Organization Administrator will receive an `HTTP 403 VPC Service Controls: Request is prohibited by organization's policy` error.

Access Context Manager (ACM): VPC-SC boundaries are defined using ACM. ACM lives at the Organization level, not the project level.

Dry Run Mode: Because VPC-SC can instantly break production applications, it is heavily recommended to deploy perimeters in `DRY_RUN` mode first. It logs violations to Cloud Audit Logs without actually interrupting traffic, allowing you to build your Access Levels carefully before switching to `ENFORCED`.

variables.tf

variable "org_id"              { type = string }
variable "protected_project_number" { type = string; description = "The numeric Project ID" }
variable "corp_ip_cidr"        { type = string; default = "203.0.113.0/24" }

main.tf

provider "google" {
  # No default project, everything here is Org level
}

# 1. Access Context Manager Policy (Only one can exist per Org!)
resource "google_access_context_manager_access_policy" "acm_policy" {
  parent = "organizations/${var.org_id}"
  title  = "Corporate Security Policy"
}

# 2. Access Level (The "Hole" in the firewall)
resource "google_access_context_manager_access_level" "corporate_ip_access" {
  parent      = "accessPolicies/${google_access_context_manager_access_policy.acm_policy.name}"
  name        = "accessPolicies/${google_access_context_manager_access_policy.acm_policy.name}/accessLevels/corp_net"
  title       = "Corporate Network"
  description = "Allows access only from approved Corporate offices"

  basic {
    conditions {
      ip_subnetworks = [var.corp_ip_cidr]
    }
  }
}

# 3. The Service Perimeter
resource "google_access_context_manager_service_perimeter" "secure_perimeter" {
  parent = "accessPolicies/${google_access_context_manager_access_policy.acm_policy.name}"
  name   = "accessPolicies/${google_access_context_manager_access_policy.acm_policy.name}/servicePerimeters/secure_data_zone"
  title  = "Secure Data Zone"

  # We use the status block to enforce the policy. Use 'spec' for dry runs.
  status {
    # 3a. Restrict these specific APIs 
    restricted_services = [
      "storage.googleapis.com",
      "bigquery.googleapis.com"
    ]

    # 3b. Which GCP Projects are inside the castle walls
    resources = [
      "projects/${var.protected_project_number}"
    ]

    # 3c. Attach the Access Level specifying who can bridge the moat
    access_levels = [
      google_access_context_manager_access_level.corporate_ip_access.name
    ]
  }
}

Workflow Commands

terraform init
terraform apply \
  -var="org_id=123456789012" \
  -var="protected_project_number=987654321098"

# If you run `gsutil ls` from a laptop not matching the corp_ip_cidr,
# you will receive a VPC-SC 403 error.

terraform destroy \
  -var="org_id=123456789012" \
  -var="protected_project_number=987654321098"