LAB36_GCP_DevSecOps: Security Command Center Auto-Remediation

DevSecOps: Security Command Center Auto-Remediation

☁ GCP 🔥 Intermediate SCC Security Pub/Sub Cloud Functions

Scenario

Your organization uses Security Command Center (SCC) to detect misconfigurations. You want to implement Auto-Remediation. When a developer accidentally makes a Cloud Storage bucket public, SCC detects it and publishes a finding. You will route this finding via an SCC Notification Config to a Pub/Sub topic, which triggers a Cloud Function to automatically revoke the public access.

Your Objectives
  • Create the destination google_pubsub_topic for SCC alerts.
  • Create a google_scc_notification_config at the Organization level that streams "PUBLIC_BUCKET_ACL" findings to the Pub/Sub topic natively.
  • Create a google_cloudfunctions2_function triggered by the Pub/Sub topic natively to act as the remediator bot.

Additional Context

SCC Tier: Security Command Center has a Standard (free) tier and a Premium tier. Continuous Exports and Notification Configs work on the Standard tier, but the specific finding categories (like detailed IAM misconfigurations) vary by tier.

Organization Level: SCC lives primarily at the Organization level, not the Project level. Creating an SCC Notification config requires `roles/securitycenter.notificationConfigEditor` on the Organization, and you must specify the Organization ID in Terraform.

The Cloud Function Bot: The python script in this lab is a mocked stand-in. A real script would parse the pubsub `data` block, extract the offending `resourceName`, and issue a GCP API call to strip `allUsers` from that bucket's IAM policy.

variables.tf

variable "project_id" { type = string }
variable "org_id"     { type = string; description = "The 12-digit numeric GCP Organization ID" }

main.tf

provider "google" {
  project = var.project_id
  region  = "us-central1"
}

# 1. Pub/Sub Topic to receive SCC findings
resource "google_pubsub_topic" "scc_alerts" {
  name = "scc-remediation-topic"
}

# 2. Give SCC permission to publish to the topic
# SCC uses a dedicated service account at the Org level
data "google_scc_project_security_health_analytics_custom_module" "dummy" {
  # This is a hacky way to force the SCC service agent to instantiate if it hasn't,
  # but assuming the SCC agent exists:
}

resource "google_pubsub_topic_iam_member" "scc_publisher" {
  topic  = google_pubsub_topic.scc_alerts.id
  role    = "roles/pubsub.publisher"
  
  # The SCC service account format (ensure you replace 'org_id' correctly)
  member = "serviceAccount:service-org-${var.org_id}@security-center-api.iam.gserviceaccount.com"
}

# 3. SCC stream config (Sends specific alerts to Pub/Sub)
resource "google_scc_notification_config" "public_bucket_alert" {
  config_id    = "alert-public-buckets"
  organization = var.org_id
  description  = "Stream public bucket alerts to pub/sub for auto-remediation"
  
  pubsub_topic = google_pubsub_topic.scc_alerts.id

  streaming_config {
    # Filter using CEL (Common Expression Language) for exact finding category
    filter = "category=\"PUBLIC_BUCKET_ACL\" AND state=\"ACTIVE\""
  }
  
  depends_on = [google_pubsub_topic_iam_member.scc_publisher]
}

# 4. Dummy zip block for the remediation Cloud Function
data "archive_file" "bot_src" {
  type        = "zip"
  output_path = "bot.zip"
  source {
    content  = "import base64\n\ndef auto_remediate(event, context):\n    print('Fixing bucket...')"
    filename = "main.py"
  }
}

resource "random_id" "bkt" { byte_length = 4 }

resource "google_storage_bucket" "src_bucket" {
  name     = "remediator-src-${random_id.bkt.hex}"
  location = "US"
}
resource "google_storage_bucket_object" "zip" {
  name   = "bot.zip"
  bucket = google_storage_bucket.src_bucket.name
  source = data.archive_file.bot_src.output_path
}

# 5. The Remediation Cloud Function
resource "google_cloudfunctions2_function" "remediator" {
  name        = "scc-remediation-bot"
  location    = "us-central1"
  description = "Triggered by SCC to remediate public buckets"

  build_config {
    runtime     = "python311"
    entry_point = "auto_remediate"
    source {
      storage_source {
        bucket = google_storage_bucket.src_bucket.name
        object = google_storage_bucket_object.zip.name
      }
    }
  }

  service_config {
    available_memory = "256M"
    timeout_seconds  = 60
    # Must run as a SA that actually has the IAM permissions to remove `allUsers` from buckets!
  }

  # Native Pub/Sub triggering
  event_trigger {
    event_type     = "google.cloud.pubsub.topic.v1.messagePublished"
    pubsub_topic   = google_pubsub_topic.scc_alerts.id
    retry_policy   = "RETRY_POLICY_DO_NOT_RETRY"
  }
}

Workflow Commands

terraform init
# Requires Organization Admin privileges over var.org_id
terraform apply -var="project_id=my-gcp-project-123" -var="org_id=123456789012"

# To test, manually make a dummy GCS bucket and grant allUsers roles/storage.objectViewer.
# Within minutes, SCC detects it, posts to the Topic, and execution logs will appear on the Cloud Function.

terraform destroy -var="project_id=my-gcp-project-123" -var="org_id=123456789012"