CIS Google Cloud Platform Foundations Benchmark
Secure configuration guidelines for Google Cloud Platform
v4.0.0 February 2025Overview
▶This CIS Benchmark provides prescriptive guidance for establishing a secure configuration posture for Google Cloud Platform. It covers identity management, logging, networking, compute, storage, database, and analytics services.
| Section | Area | Focus |
|---|---|---|
| 1 | Identity and Access Management | Service accounts, IAM policies, key management |
| 2 | Logging and Monitoring | Audit logs, sinks, log-based alerts |
| 3 | Networking | Firewall rules, VPC, Private Google Access, DNS |
| 4 | Virtual Machines | Compute Engine, OS login, serial ports, shielded VMs |
| 5 | Storage | Cloud Storage bucket access, encryption, retention |
| 6 | Cloud SQL | Database flags, SSL, public IP, backups |
| 7 | BigQuery | Dataset access, encryption, audit |
Profile Definitions
▶| Profile | Description | Intended Use |
|---|---|---|
| L1 | Level 1 | Practical baseline security for all GCP projects. Minimal operational impact. |
| L2 | Level 2 | Defense-in-depth for security-sensitive workloads. May affect cost or complexity. |
1 — Identity and Access Management
▶Google Cloud IAM configuration covering service accounts, IAM bindings, and key management.
1.1 Service Accounts
▶Service account keys should be rotated every 90 days or less. Stale keys increase the window of exposure if compromised. Prefer Workload Identity Federation over user-managed keys.
Failure to user-Managed Service Account Keys Are Rotated Within 90 Days may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud iam service-accounts keys list --iam-account <sa-email> \ --managed-by user --format="table(name,validAfterTime,validBeforeTime)" # Check if any key's validAfterTime is older than 90 days
# Create new key gcloud iam service-accounts keys create ~/new-key.json \ --iam-account <sa-email> # Update applications, then delete old key gcloud iam service-accounts keys delete <key-id> \ --iam-account <sa-email>
Service accounts should not be granted Owner, Editor, or other broad admin roles. Use least-privilege custom roles or predefined granular roles instead.
Failure to service Account Has No Admin Privileges may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud projects get-iam-policy <project> --format=json | \
jq '.bindings[] | select(.role=="roles/owner" or .role=="roles/editor") |
.members[] | select(startswith("serviceAccount:"))'
# Should return emptygcloud projects remove-iam-policy-binding <project> \ --member='serviceAccount:<sa-email>' --role='roles/editor' # Then assign a least-privilege role
GCP creates default service accounts (Compute Engine default, App Engine default) with the Editor role. These should not be used — create dedicated service accounts with minimal permissions.
If Default Service Account remains used, it presents an unnecessary risk vector that attackers could exploit. Removing or disabling unused components is a fundamental principle of secure system hardening.
gcloud iam service-accounts list --filter="email:*-compute@developer.gserviceaccount.com OR email:*@appspot.gserviceaccount.com" --format="table(email,disabled)" # Default service accounts should be disabled
gcloud iam service-accounts disable <default-sa-email>
1.2 IAM Policies
▶Use managed Cloud Identity or Google Workspace accounts (corporate domain) for GCP access instead of personal Gmail accounts. Corporate accounts provide centralized lifecycle management and policy enforcement.
Failure to corporate Login Credentials Are Used Instead of Gmail Accounts may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud projects get-iam-policy <project> --format=json | \
jq '.bindings[].members[] | select(endswith("@gmail.com"))'
# Should return no gmail.com accountsReplace Gmail-based IAM bindings with managed corporate domain accounts and remove the personal Gmail bindings.
IAM bindings should not include allUsers (anyone on the internet) or allAuthenticatedUsers (any Google account) as members, unless the resource is intentionally public.
Failure to no IAM Bindings Use allUsers or allAuthenticatedUsers may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud projects get-iam-policy <project> --format=json | \
jq '.bindings[] | select(.members[] | contains("allUsers") or contains("allAuthenticatedUsers"))'
# Should return empty for non-public projectsgcloud projects remove-iam-policy-binding <project> \ --member='allUsers' --role='<role>'
2 — Logging and Monitoring
▶Cloud Audit Logs, log sinks, and alert policy configuration for security event detection.
2.1 Audit Logging
▶Enable Data Access audit logs for all services. By default, only Admin Activity logs are enabled. Data Access logs capture read/write operations and should be enabled for security-sensitive services.
Failure to cloud Audit Logging is Configured for All Services and All Users may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud projects get-iam-policy <project> --format=json | \
jq '.auditConfigs[]'
# Should include {"service":"allServices"} with all log types enabledUpdate the project IAM policy to enable DATA_READ, DATA_WRITE, and ADMIN_READ audit log types for allServices with no exempted members.
Create a log sink to export all log entries to a long-term storage destination (Cloud Storage, BigQuery, or Pub/Sub). Default log retention is 30 days — a sink ensures logs are preserved longer.
Failure to log Sinks Are Configured for All Log Entries may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud logging sinks list --project=<project> \ --format="table(name,destination,filter)" # Should have at least one sink with no filter (captures all logs)
gcloud logging sinks create <sink-name> \ storage.googleapis.com/<bucket> --project=<project>
2.2 Log-Based Alerts
▶Create a log-based metric and alert for project ownership assignment changes. Ownership changes can grant full control of the project to unauthorized users.
Failure to log Metric Filter and Alert Exist for Project Ownership Changes may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud logging metrics list --project=<project> --format="table(name,filter)" # Look for metric with filter: # (protoPayload.serviceName="cloudresourcemanager.googleapis.com") AND # (ProjectOwnership OR projectOwnerInvitee) OR # (protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner")
Create a log-based metric with the appropriate filter, then create a Monitoring alert policy that triggers on the metric.
Monitor changes to audit configuration (disabling logs, adding exemptions). Attackers may disable audit logging to cover their tracks.
Failure to log Metric Filter and Alert Exist for Audit Configuration Changes may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Look for metric with filter: # protoPayload.methodName="SetIamPolicy" AND # protoPayload.serviceData.policyDelta.auditConfigDeltas:* gcloud logging metrics list --project=<project>
Create a log-based metric for IAM audit config changes and configure a Monitoring alert policy.
Monitor VPC firewall rule creation, modification, and deletion. Unauthorized firewall changes can expose resources to the internet.
Failure to log Metric Filter and Alert Exist for VPC Firewall Rule Changes may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Look for metric with filter: # resource.type="gce_firewall_rule" AND # (protoPayload.methodName:"compute.firewalls.insert" OR # protoPayload.methodName:"compute.firewalls.patch" OR # protoPayload.methodName:"compute.firewalls.delete") gcloud logging metrics list --project=<project>
Create a log-based metric for firewall rule changes and configure a Monitoring alert policy.
3 — Networking
▶VPC firewall rules, DNS, SSL policies, and Private Google Access configuration.
3.1 Firewall Rules
▶Delete the default VPC network in every project. The default network includes pre-populated firewall rules that allow broad access. Create custom VPCs with restrictive rules instead.
The absence of the Default Network Does Not leaves the Google Cloud Platform cloud platform without an important security control. Verifying its presence ensures the system meets the minimum security baseline required by the CIS benchmark.
gcloud compute networks list --project=<project> --filter="name=default" --format="table(name)" # Should return empty
# Delete all firewall rules first
gcloud compute firewall-rules list --filter="network:default" --format="value(name)" | \
xargs -I {} gcloud compute firewall-rules delete {} --quiet
# Then delete the network
gcloud compute networks delete default --quietNo VPC firewall rule should allow unrestricted SSH access from 0.0.0.0/0. Use Identity-Aware Proxy (IAP) TCP forwarding or OS Login with specific source ranges.
Failure to no Firewall Rule Allows Ingress from 0.0.0.0/0 to SSH (port 22) may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute firewall-rules list --filter="direction=INGRESS AND allowed[].ports:22 AND sourceRanges:0.0.0.0/0" \ --format="table(name,network,allowed,sourceRanges)" # Should return empty
gcloud compute firewall-rules update <rule-name> \ --source-ranges=<trusted-cidr> # Or use IAP: gcloud compute firewall-rules create allow-ssh-iap \ --allow tcp:22 --source-ranges 35.235.240.0/20 --direction INGRESS
No VPC firewall rule should allow unrestricted RDP access from 0.0.0.0/0. Restrict to specific source ranges or use IAP TCP forwarding.
Failure to no Firewall Rule Allows Ingress from 0.0.0.0/0 to RDP (port 3389) may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute firewall-rules list --filter="direction=INGRESS AND allowed[].ports:3389 AND sourceRanges:0.0.0.0/0" \ --format="table(name,network,allowed,sourceRanges)" # Should return empty
Update firewall rules to restrict RDP to trusted source IP ranges or use IAP TCP forwarding.
3.2 VPC & DNS
▶Enable Private Google Access on VPC subnets so instances without external IP addresses can reach Google APIs and services via internal IP addresses.
Failure to private Google Access is Enabled on Subnets may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute networks subnets list --format="table(name,region,privateIpGoogleAccess)" # privateIpGoogleAccess should be True for all subnets
gcloud compute networks subnets update <subnet> --region=<region> \ --enable-private-ip-google-access
Enable DNSSEC on Cloud DNS managed zones to provide authentication of DNS responses and protect against DNS spoofing attacks.
Failure to dNSSEC is Enabled for Cloud DNS Managed Zones may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud dns managed-zones list --format="table(name,dnsName,dnssecConfig.state)" # dnssecConfig.state should be "on" for all public zones
gcloud dns managed-zones update <zone> --dnssec-state on
4 — Virtual Machines
▶4.1 Compute Engine
▶Enable OS Login at the project level to manage SSH keys via IAM instead of metadata SSH keys. OS Login provides centralized key management, automatic key lifecycle management, and 2FA support.
Failure to oS Login is Enabled for Compute Instances may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute project-info describe --format="value(commonInstanceMetadata.items[key='enable-oslogin'].value)" # Should be TRUE
gcloud compute project-info add-metadata --metadata enable-oslogin=TRUE
Enable Shielded VM features (Secure Boot, vTPM, Integrity Monitoring) for all Compute Engine instances to protect against rootkits and bootkits.
Failure to shielded VM is Enabled for Compute Instances may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute instances list --format="table(name,shieldedInstanceConfig.enableSecureBoot,shieldedInstanceConfig.enableVtpm,shieldedInstanceConfig.enableIntegrityMonitoring)"
gcloud compute instances update <instance> --zone=<zone> \ --shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring
Disable interactive serial console access to VM instances. The serial console does not support IP-based access restrictions and could provide an additional attack surface if credentials are compromised.
Leaving VM Serial Port Logging enabled when it is not required unnecessarily expands the attack surface. An attacker could leverage this feature to gain unauthorized access or escalate privileges on the Google Cloud Platform cloud platform.
gcloud compute instances describe <instance> --zone=<zone> \ --format="value(metadata.items[key='serial-port-enable'].value)" # Should be empty or FALSE
# Disable at org policy level gcloud resource-manager org-policies enable-enforce \ compute.disableSerialPortAccess --project=<project>
Compute instances should not have external (public) IP addresses unless required. Use Cloud NAT for outbound internet access and IAP TCP forwarding or load balancers for inbound access.
Failure to compute Instances Do Not Have Public IP Addresses may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud compute instances list --format="table(name,zone,networkInterfaces[0].accessConfigs[0].natIP)" # natIP column should be empty for instances that don't need public access
Remove the external IP from instances: gcloud compute instances delete-access-config <instance> --access-config-name "External NAT" --zone=<zone>
5 — Storage
▶5.1 Cloud Storage
▶Ensure buckets do not grant access to allUsers or allAuthenticatedUsers. Public buckets can lead to data exposure. Enable the organization policy constraints/storage.publicAccessPrevention.
Failure to cloud Storage Buckets Are Not Publicly Accessible may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gsutil iam get gs://<bucket> | \
jq '.bindings[] | select(.members[] | contains("allUsers") or contains("allAuthenticatedUsers"))'
# Should return emptygsutil iam ch -d allUsers gs://<bucket> gsutil iam ch -d allAuthenticatedUsers gs://<bucket>
Enable Uniform bucket-level access (formerly Bucket Policy Only) to disable per-object ACLs. This simplifies access management by using only IAM policies for all access control.
Without Uniform Bucket-Level Access enabled, the Google Cloud Platform cloud platform may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.
gsutil uniformbucketlevelaccess get gs://<bucket> # Enabled value should be True
gsutil uniformbucketlevelaccess set on gs://<bucket>
6 — Cloud SQL
▶6.1 Database Instances
▶Cloud SQL instances should use private IP addresses only. Public IP addresses expose the database to potential internet-based attacks even with authorized networks configured.
Failure to cloud SQL Does Not Have a Public IP may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud sql instances list --format="table(name,ipAddresses)" # Should not have type:PRIMARY external IP addresses
Configure private IP for the instance, update applications to connect via private IP, then remove the public IP address.
Configure Cloud SQL instances to require SSL/TLS for all connections. This prevents data interception and ensures encrypted communication between applications and the database.
Failure to cloud SQL Instances Require SSL Connections may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud sql instances describe <instance> --format="value(settings.ipConfiguration.requireSsl)" # Should be True
gcloud sql instances patch <instance> --require-ssl
Enable automated backups for all Cloud SQL instances to ensure data recovery in case of accidental deletion, corruption, or security incidents.
Failure to automated Backups Are Configured for Cloud SQL may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
gcloud sql instances describe <instance> \ --format="value(settings.backupConfiguration.enabled,settings.backupConfiguration.pointInTimeRecoveryEnabled)" # Both should be True
gcloud sql instances patch <instance> \ --backup-start-time 02:00 --enable-point-in-time-recovery
7 — BigQuery
▶7.1 Datasets
▶BigQuery datasets should not be shared with allUsers or allAuthenticatedUsers. Public datasets can expose sensitive analytical data.
Failure to bigQuery Datasets Are Not Publicly Accessible may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
bq show --format=prettyjson <project:dataset> | \ jq '.access[] | select(.specialGroup=="allAuthenticatedUsers" or .iamMember=="allUsers")' # Should return empty
Remove public access from the dataset using the BigQuery console or bq update command.
Use Customer-Managed Encryption Keys (CMEK) from Cloud KMS for BigQuery datasets instead of Google-managed encryption. CMEK provides control over encryption key lifecycle and access auditing.
Failure to bigQuery Datasets Use Customer-Managed Encryption Keys may leave the Google Cloud Platform cloud platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
bq show --format=prettyjson <project:dataset> | jq '.defaultEncryptionConfiguration' # Should show a kmsKeyName
bq update --default_kms_key <kms-key-name> <project:dataset>