GCP Security Checklist: Hardening Google Cloud Projects
Essential security controls for Google Cloud Platform covering org policies, VPC Service Controls, Workload Identity, Cloud Armor, Secret Manager, and audit logs.
Google Cloud Platform's security model differs meaningfully from AWS and Azure in ways that trip up teams migrating from other clouds. The IAM model is more granular, the organization hierarchy is more rigid, and some of the most powerful security controls — VPC Service Controls, org policies, and Workload Identity Federation — have no direct equivalents elsewhere. This checklist covers the controls that matter most across GCP's unique security landscape.
Organization-Level Controls
Enable Organization Policy Service
Org policies enforce constraints across all projects in your organization. These constraints cannot be overridden by project owners, making them essential guardrails. Start with the most impactful:
# Disable public IP on Compute Engine VMs org-wide
gcloud resource-manager org-policies set-policy --organization=<ORG_ID> - <<EOF
name: organizations/<ORG_ID>/policies/compute.vmExternalIpAccess
spec:
rules:
- deny:
all: true
EOF
# Require OS Login for all VMs (replaces SSH key management)
gcloud resource-manager org-policies set-policy --organization=<ORG_ID> - <<EOF
name: organizations/<ORG_ID>/policies/compute.requireOsLogin
spec:
rules:
- enforce: true
EOF
# Restrict allowed external IPs to load balancer resources only
# Restrict public bucket access
gcloud resource-manager org-policies set-policy --organization=<ORG_ID> - <<EOF
name: organizations/<ORG_ID>/policies/storage.publicAccessPrevention
spec:
rules:
- enforce: true
EOF
Structure Projects Using a Folder Hierarchy
Organize projects into folders by environment and business unit:
Organization
├── Production
│ ├── prod-data-project
│ ├── prod-app-project
│ └── prod-network-project
├── Staging
│ └── staging-project
├── Development
│ └── dev-project
└── Security
├── audit-logs-project
└── security-tooling-project
Apply more restrictive org policies at the Production folder level.
Designate a Centralized Audit Logs Project
Aggregate organization-level audit logs to a dedicated project with restricted access. Configure log sinks at the organization level:
gcloud logging sinks create org-audit-sink \
bigquery.googleapis.com/projects/audit-logs-project/datasets/audit_logs \
--organization=<ORG_ID> \
--include-children \
--log-filter='logName=~"cloudaudit.googleapis.com"'
Identity and Access Management
Use Google Workspace Groups for IAM Bindings
Bind IAM roles to Google Workspace groups rather than individual users. This means access is managed in your identity provider and automatically revoked when users leave:
gcloud projects add-iam-policy-binding production-project \
--member="group:developers@yourdomain.com" \
--role="roles/viewer"
Eliminate Basic Roles (Owner/Editor/Viewer) in Production
Basic roles are coarse-grained and grant far more permissions than needed. Replace with predefined or custom roles. Audit basic role bindings:
gcloud projects get-iam-policy production-project \
--format="json" | jq '.bindings[] | select(.role | test("roles/(owner|editor|viewer)"))'
Disable Service Account Key Creation Org-Wide
Service account keys are long-lived, can be exfiltrated, and require manual rotation. Use Workload Identity instead for all GKE and most non-GKE use cases:
gcloud resource-manager org-policies set-policy --organization=<ORG_ID> - <<EOF
name: organizations/<ORG_ID>/policies/iam.disableServiceAccountKeyCreation
spec:
rules:
- enforce: true
EOF
If key creation is needed for specific projects, use an exception at the project level.
Implement Workload Identity Federation
For GKE workloads, Workload Identity binds a Kubernetes service account to a Google service account, eliminating the need for downloadable key files:
# Enable Workload Identity on the cluster
gcloud container clusters update my-cluster \
--workload-pool=<PROJECT_ID>.svc.id.goog
# Annotate the Kubernetes service account
kubectl annotate serviceaccount my-ksa \
iam.gke.io/gcp-service-account=my-gsa@<PROJECT_ID>.iam.gserviceaccount.com
# Allow the KSA to impersonate the GSA
gcloud iam service-accounts add-iam-policy-binding my-gsa@<PROJECT_ID>.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<PROJECT_ID>.svc.id.goog[default/my-ksa]"
Audit Service Account Permissions Regularly
Service accounts accumulate permissions over time. Use IAM Recommender to identify overly permissive bindings:
gcloud recommender recommendations list \
--project=<PROJECT_ID> \
--location=global \
--recommender=google.iam.policy.Recommender \
--format="table(name,description,primaryImpact.securityProjection.details)"
Network Security
Deploy VPC Service Controls
VPC Service Controls create a security perimeter around GCP services, preventing data exfiltration even if IAM credentials are compromised. They enforce that API calls to services like Cloud Storage, BigQuery, and Secret Manager can only come from within the defined perimeter:
# Create an access policy
gcloud access-context-manager policies create \
--organization=<ORG_ID> \
--title="Production Security Policy"
# Create a service perimeter
gcloud access-context-manager perimeters create production-perimeter \
--policy=<POLICY_ID> \
--title="Production Perimeter" \
--resources=projects/<PROJECT_NUMBER> \
--restricted-services=storage.googleapis.com,bigquery.googleapis.com,secretmanager.googleapis.com \
--access-levels=accessPolicies/<POLICY_ID>/accessLevels/corp-network
Use Shared VPC for Network Governance
Shared VPC centralizes network management in a host project. Workload projects connect to the shared VPC but cannot modify network configuration. This prevents rogue subnets or firewall rules being created by individual teams.
Enable VPC Flow Logs
gcloud compute networks subnets update production-subnet \
--region=us-central1 \
--enable-flow-logs \
--logging-aggregation-interval=interval-5-sec \
--logging-flow-sampling=0.5 \
--logging-metadata=include-all
Configure Hierarchical Firewall Policies
Apply deny-all rules at the organization or folder level, then allow specific traffic at the project/VPC level:
gcloud compute firewall-policies create \
--short-name="org-baseline-policy" \
--description="Organization-wide baseline firewall rules" \
--organization=<ORG_ID>
gcloud compute firewall-policies rules create 65534 \
--firewall-policy=org-baseline-policy \
--organization=<ORG_ID> \
--direction=INGRESS \
--action=deny \
--src-ip-ranges=0.0.0.0/0 \
--layer4-configs=tcp:22,tcp:3389 \
--description="Block SSH and RDP from internet"
Deploy Cloud Armor for WAF and DDoS Protection
Cloud Armor protects internet-facing load balancers with OWASP rule sets and rate limiting:
gcloud compute security-policies create production-waf \
--description "Production WAF Policy"
# Enable OWASP Core Rule Set
gcloud compute security-policies rules create 1000 \
--security-policy production-waf \
--expression "evaluatePreconfiguredExpr('xss-stable')" \
--action deny-403 \
--description "Block XSS"
gcloud compute security-policies rules create 1001 \
--security-policy production-waf \
--expression "evaluatePreconfiguredExpr('sqli-stable')" \
--action deny-403 \
--description "Block SQLi"
Use Private Google Access for Workloads
Enable Private Google Access on subnets so VMs without public IPs can still reach Google APIs over RFC 1918 addresses:
gcloud compute networks subnets update production-subnet \
--region=us-central1 \
--enable-private-ip-google-access
Data Protection
Use Secret Manager for All Secrets
No credentials in environment variables, Kubernetes ConfigMaps, or source code. Store all secrets in Secret Manager:
# Create a secret
echo -n "my-api-key" | gcloud secrets create api-key \
--data-file=- \
--replication-policy=automatic
# Grant access to a service account
gcloud secrets add-iam-policy-binding api-key \
--member="serviceAccount:my-app@<PROJECT_ID>.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
Enable Customer-Managed Encryption Keys (CMEK)
For regulated data in Cloud Storage, BigQuery, Cloud SQL, and GKE, use CMEK with keys stored in Cloud KMS:
# Create a key ring and key
gcloud kms keyrings create production-keyring --location=us-central1
gcloud kms keys create data-encryption-key \
--keyring=production-keyring \
--location=us-central1 \
--purpose=encryption
# Apply to a Cloud Storage bucket
gsutil mb -l us-central1 gs://my-encrypted-bucket
gsutil kms authorize -k projects/<PROJECT_ID>/locations/us-central1/keyRings/production-keyring/cryptoKeys/data-encryption-key
gsutil kms encryption -k projects/<PROJECT_ID>/locations/us-central1/keyRings/production-keyring/cryptoKeys/data-encryption-key gs://my-encrypted-bucket
Enable Bucket Uniform Access Control
Uniform bucket-level access disables per-object ACLs and simplifies permission management:
gsutil uniformbucketlevelaccess set on gs://my-bucket
Configure Cloud DLP for Sensitive Data Discovery
Use Cloud Data Loss Prevention to scan Cloud Storage and BigQuery for PII, credentials, and sensitive data:
gcloud dlp jobs create \
--project=<PROJECT_ID> \
--type=INSPECT \
--input-data='{"cloudStorageOptions":{"fileSet":{"url":"gs://my-bucket/**"}}}' \
--inspect-config='{"infoTypes":[{"name":"EMAIL_ADDRESS"},{"name":"PHONE_NUMBER"},{"name":"CREDIT_CARD_NUMBER"},{"name":"US_SOCIAL_SECURITY_NUMBER"}]}' \
--actions='[{"saveFindings":{"outputConfig":{"table":{"projectId":"<PROJECT_ID>","datasetId":"dlp_results","tableId":"findings"}}}}]'
Monitoring and Threat Detection
Enable Cloud Audit Logs for All Services
Three audit log types exist in GCP:
- Admin Activity: Always enabled, cannot be disabled — records resource configuration changes
- Data Access: Must be explicitly enabled — records reads/writes to user data
- System Events: Always enabled — records GCP-initiated changes
Enable Data Access audit logs for all services:
gcloud projects set-iam-policy <PROJECT_ID> - <<EOF
{
"auditConfigs": [
{
"service": "allServices",
"auditLogConfigs": [
{"logType": "ADMIN_READ"},
{"logType": "DATA_READ"},
{"logType": "DATA_WRITE"}
]
}
]
}
EOF
Enable Security Command Center
Security Command Center (SCC) is GCP's built-in CSPM and threat detection service. Premium tier includes Event Threat Detection (behavioral analysis), Container Threat Detection, and Virtual Machine Threat Detection:
gcloud scc settings update \
--organization=<ORG_ID> \
--enable-asset-discovery
Configure Log-Based Alerts
Alert on critical events using log-based metrics and alerting policies:
# Alert on org policy changes
gcloud logging metrics create org-policy-change \
--description="Org policy modifications" \
--log-filter='protoPayload.methodName="SetOrgPolicy" OR protoPayload.methodName="DeleteOrgPolicy"'
gcloud alpha monitoring policies create \
--policy-from-file=alert-policy.json
Enable Chronicle SIEM Integration
For enterprises, route all GCP logs to Google Chronicle for long-term retention, behavioral analytics, and threat hunting at petabyte scale.
GKE-Specific Controls
Enable GKE Autopilot or Harden Standard Clusters
GKE Autopilot enforces security best practices by default (no privileged pods, Workload Identity required, no node access). For Standard clusters:
gcloud container clusters create production-cluster \
--workload-pool=<PROJECT_ID>.svc.id.goog \
--enable-shielded-nodes \
--shielded-secure-boot \
--shielded-integrity-monitoring \
--enable-network-policy \
--no-enable-basic-auth \
--no-issue-client-certificate \
--enable-private-nodes \
--master-ipv4-cidr=172.16.0.0/28 \
--enable-private-endpoint \
--release-channel=regular
Enable Binary Authorization
Binary Authorization enforces that only container images signed by trusted authorities can be deployed to GKE:
gcloud container binauthz policy import policy.yaml
# Policy enforces attestations from your CI/CD pipeline's signing service
The combination of org policies (enforcing broad constraints), VPC Service Controls (preventing data exfiltration), and Workload Identity (eliminating downloadable credentials) forms a defense-in-depth architecture that addresses GCP's most common attack vectors.