Cloud Security

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.

September 15, 20258 min readShipSafer Team

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.

GCP
Google Cloud
security checklist
org policies
VPC Service Controls
Workload Identity

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.