Building a GCP Security Auditing Framework: Lessons from Spotify's Deprecated Tool
Hook
Spotify deprecated this tool years ago, yet its rule engine architecture teaches us more about building security auditors than most actively maintained projects.
Context
In the mid-2010s, as organizations rushed to adopt Google Cloud Platform, they faced a critical challenge: how do you ensure hundreds of GCP projects maintain consistent security postures? Manual reviews don't scale, and Google's native tooling was limited. Engineers needed to answer questions like "Which CloudSQL instances are publicly accessible?" or "Do any storage buckets lack encryption?" across entire organizations.
Spotify built gcp-audit to solve this at scale. Rather than writing custom Python scripts for each security check, they created a declarative rule engine where security policies are expressed in YAML. The tool queries GCP APIs, collects resource configurations, and evaluates them against rules—flagging violations without requiring code changes. While the project is now deprecated in favor of Forseti Security (and later, Google's Security Command Center), its architecture remains a masterclass in building extensible auditing systems.
Technical Insight
The brilliance of gcp-audit lies in its separation of concerns: data collection happens through Google's API clients, while rule evaluation uses a custom domain-specific language. This means adding new security checks doesn't require touching Python code—you simply write more rules.
Rules are defined with a match_type field that determines comparison logic. Here's a real example checking for publicly accessible CloudSQL instances:
rules:
- name: sql_no_public_ip
resource: sqladmin.instances
match_type: partial
match:
settings:
ipConfiguration:
authorizedNetworks:
- value: "0.0.0.0/0"
message: "CloudSQL instance allows public access"
This rule navigates nested API response objects using dot notation. The partial match type checks if any element in authorizedNetworks contains the specified value. Other match types include exact (deep equality), regex (pattern matching), numeric (threshold comparisons), and count (list length checks). This flexibility handles everything from simple string matching to complex policy validation.
The evaluation engine recursively traverses API response structures. When it encounters a list, it applies rules to each element. Boolean operators (all, any, none) combine multiple conditions. Here's a more sophisticated rule:
rules:
- name: storage_bucket_security
resource: storage.buckets
match_type: all
conditions:
- match_type: exact
match:
versioning:
enabled: true
- match_type: none
match:
iamConfiguration:
publicAccessPrevention: "inherited"
This checks that buckets have versioning enabled AND don't inherit public access settings—demonstrating how complex policies emerge from composable primitives.
The data collection architecture uses Google's discovery-based API clients. For each resource type (like sqladmin.instances), the tool dynamically constructs the appropriate API call. This pattern made it straightforward to add new GCP services:
def fetch_resources(service_name, resource_type, project_id):
service = build(service_name, 'v1', credentials=creds)
resource = service
for part in resource_type.split('.'):
resource = getattr(resource, part)()
return resource.list(project=project_id).execute()
This abstraction meant Spotify engineers could audit new GCP services by adding rule files—no Python expertise required. The tool's reports output JSON with violating resources, making it pipeline-friendly for CI/CD integration or Slack notifications.
One particularly clever design choice: rules reference API response structures directly, not abstract schemas. This means you write rules by examining actual API responses, then pattern-matching against them. It's documentation-as-code—the rules themselves teach you what the API returns.
Gotcha
The tool's greatest strength—direct API response matching—is also its Achilles' heel. When Google changes API response structures (renaming fields, nesting objects differently), rules break silently. You won't get errors; checks simply stop matching. This brittleness makes maintenance expensive at scale.
More critically, gcp-audit is explicitly unmaintained. The repository README directs users to Forseti Security, which itself is now archived in favor of Security Command Center. The codebase targets Python 2.7-era dependencies and lacks support for modern GCP services like GKE cluster configurations, Cloud Run security settings, or IAM policy analysis. You'd need significant work to audit contemporary GCP environments. The rule syntax also becomes unwieldy for deeply nested objects—some production rules at Spotify reportedly exceeded 100 lines of YAML, becoming unreadable and hard to debug. There's no rule testing framework, making it difficult to validate that your security policies actually catch violations.
Verdict
Use if: You're building a custom security auditing framework (not auditing GCP itself) and want architectural inspiration. The rule engine design—separating data collection from evaluation, using match types for flexible pattern matching, and enabling declarative policy definition—is worth studying and adapting for other domains. It's also useful if you're reverse-engineering Spotify's historical infrastructure practices.
Skip if: You need to actually audit GCP projects. Use Google Security Command Center for native integration, ScoutSuite for open-source multi-cloud auditing with active maintenance, or cloud-custodian for policy-as-code with 400+ GCP checks. This codebase serves history, not production. Even for learning purposes, examine Forseti Security's architecture first—it evolved from the same problem space with more sophisticated approaches to IAM analysis and real-time monitoring.