DevSecOps: Security Command Center Auto-Remediation
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.
- 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"
