GCP provider
Scans a live GCP project via the google-cloud-* client libraries.
Requires pip install pipeline-check[gcp] and Application Default
Credentials (gcloud auth application-default login).
Producer workflow
pipeline_check --pipeline gcp --gcp-project my-project-id
pipeline_check --pipeline gcp --gcp-project $GCP_PROJECT
Covered services
| Service | Prefix | Rules |
|---|---|---|
| IAM | GCIAM- | Service account admin roles, user-managed keys, impersonation |
| Cloud Storage | GCS- | Public buckets, uniform access, versioning |
| Cloud KMS | GCKMS- | Key rotation, public access, HSM protection |
| Artifact Registry | GAR- | Vulnerability scanning, public repos, cleanup policies |
| Cloud Logging | GCLOG- | Audit log config, log sinks, retention |
What it covers
50 checks · 0 have an autofix patch (--fix).
| Check | Title | Severity | Fix |
|---|---|---|---|
| GAR-001 | Artifact Registry repository has no vulnerability scanning | HIGH | |
| GAR-002 | Artifact Registry repository is publicly readable | HIGH | |
| GAR-003 | Artifact Registry has no cleanup policy | MEDIUM | |
| GCCE-001 | Compute instance does not have Shielded VM enabled | MEDIUM | |
| GCCE-002 | Compute instance does not have OS Login enabled | MEDIUM | |
| GCCE-003 | Compute instance has serial port access enabled | MEDIUM | |
| GCCE-004 | Compute instance has an external IP address | HIGH | |
| GCCE-005 | Instance does not block project-wide SSH keys | MEDIUM | |
| GCIAM-001 | Service account has Owner or Editor role on project | CRITICAL | |
| GCIAM-002 | Service account has user-managed key | HIGH | |
| GCIAM-003 | Service account token creator granted without constraint | HIGH | |
| GCIAM-004 | Compute instance uses default service account | HIGH | |
| GCIAM-005 | Domain-restricted sharing constraint not enforced | MEDIUM | |
| GCIAM-006 | Service account key older than 90 days | HIGH | |
| GCKMS-001 | KMS key rotation period exceeds 365 days | MEDIUM | |
| GCKMS-002 | KMS key IAM policy grants public access | HIGH | |
| GCKMS-003 | KMS key not using HSM protection level | LOW | |
| GCKMS-004 | KMS key ring IAM has overly broad bindings | HIGH | |
| GCKMS-005 | KMS key has primary version scheduled for destruction | MEDIUM | |
| GCKMS-006 | KMS key uses imported (external) key material | LOW | |
| GCLOG-001 | Cloud Audit Logs not enabled for all services | HIGH | |
| GCLOG-002 | No log sink configured for audit logs | MEDIUM | |
| GCLOG-003 | Log bucket retention less than 365 days | MEDIUM | |
| GCLOG-004 | VPC Flow Logs not enabled on subnet | MEDIUM | |
| GCLOG-005 | Firewall rule logging not enabled | MEDIUM | |
| GCLOG-006 | Critical service missing Data Access audit log types | MEDIUM | |
| GCLOG-007 | No log metric filter for IAM policy changes | MEDIUM | |
| GCLOG-008 | No log metric filter for firewall rule changes | MEDIUM | |
| GCLOG-009 | No log metric filter for route changes | MEDIUM | |
| GCLOG-010 | No log metric filter for Cloud SQL config changes | MEDIUM | |
| GCLOG-011 | No log metric filter for custom role changes | MEDIUM | |
| GCNET-001 | Default VPC network exists in project | MEDIUM | |
| GCNET-002 | No default-deny ingress firewall rule configured | MEDIUM | |
| GCNET-003 | Firewall allows SSH or RDP from the internet | CRITICAL | |
| GCNET-004 | Subnet does not have Private Google Access enabled | MEDIUM | |
| GCNET-005 | No Cloud NAT gateway configured | LOW | |
| GCRUN-001 | Cloud Run service allows unauthenticated access | HIGH | |
| GCRUN-002 | Cloud Run service or function uses default compute SA | HIGH | |
| GCRUN-003 | Cloud Run service has zero minimum instances | LOW | |
| GCRUN-004 | Cloud Run service does not use a VPC connector | MEDIUM | |
| GCS-001 | Cloud Storage bucket is publicly accessible | HIGH | |
| GCS-002 | Bucket does not enforce uniform bucket-level access | MEDIUM | |
| GCS-003 | Bucket versioning not enabled | MEDIUM | |
| GCS-004 | Cloud Storage bucket not encrypted with CMEK | MEDIUM | |
| GCS-005 | Cloud Storage bucket access logging not enabled | MEDIUM | |
| GCSQL-001 | Cloud SQL instance has a public IP address | HIGH | |
| GCSQL-002 | Cloud SQL instance does not have automated backups enabled | MEDIUM | |
| GCSQL-003 | Cloud SQL instance does not require SSL connections | HIGH | |
| GCSQL-004 | Cloud SQL instance does not have IAM authentication enabled | MEDIUM | |
| GCSQL-005 | Cloud SQL instance does not have point-in-time recovery enabled | MEDIUM |
GAR-001: Artifact Registry repository has no vulnerability scanning
Without vulnerability scanning, container images with known CVEs pass through the artifact store without detection.
Recommended action
Enable vulnerability scanning on the repository by configuring the Container Analysis / On-Demand Scanning API. Set the scanning config to STANDARD or enable Artifact Analysis at the project level.
GAR-002: Artifact Registry repository is publicly readable
A publicly readable repository allows anyone to pull images. Internal images may contain proprietary code, configuration, or embedded credentials.
Recommended action
Remove allUsers and allAuthenticatedUsers from the repository's IAM policy. Use service accounts with artifactregistry.reader for authenticated access.
GAR-003: Artifact Registry has no cleanup policy
Without a cleanup policy, old image tags accumulate indefinitely. Stale images may contain known vulnerabilities and remain pullable.
Recommended action
Configure a cleanup policy on the repository to automatically delete old or unused artifacts. This reduces storage costs and limits the window in which a compromised old image can be pulled.
GCCE-001: Compute instance does not have Shielded VM enabled
Shielded VM uses Secure Boot, vTPM, and integrity monitoring to defend against boot-level and kernel-level malware. Without it, an attacker who gains root can install a persistent rootkit that survives reboots.
Recommended action
Enable Shielded VM with both vTPM and integrity monitoring on all Compute Engine instances. Shielded VM verifies the boot chain and detects boot-level rootkits.
GCCE-002: Compute instance does not have OS Login enabled
OS Login uses IAM to manage SSH access to instances instead of SSH keys stored in project or instance metadata. Without it, anyone who can edit metadata can inject an SSH key and gain shell access.
Recommended action
Set the metadata key 'enable-oslogin' to 'TRUE' on every instance (or at the project level). OS Login ties SSH access to IAM, removing the need to manage SSH keys.
GCCE-003: Compute instance has serial port access enabled
Enabling the interactive serial console (serial-port-enable) allows anyone with the compute.instances.getSerialPortOutput permission to read console output, which may contain boot logs, kernel messages, or application secrets.
Recommended action
Set the metadata key 'serial-port-enable' to 'false' (or remove it) on every instance. Use the Cloud Console or gcloud SSH instead for debugging.
GCCE-004: Compute instance has an external IP address
An external IP makes the instance directly addressable from the internet. Combined with a permissive firewall rule, this exposes the instance to scanning, brute-force attacks, and exploitation of any listening service.
Recommended action
Remove external IP addresses from instances that do not need direct internet access. Use Cloud NAT for outbound connectivity and IAP TCP forwarding for administrative access.
GCCE-005: Instance does not block project-wide SSH keys
Project-wide SSH keys are propagated to every instance that does not explicitly block them. An attacker who can edit project metadata can inject an SSH key and access all instances that accept project keys.
Recommended action
Set the metadata key 'block-project-ssh-keys' to 'TRUE' on instances that should not accept project-level SSH keys. This limits SSH access to keys defined on the instance itself or via OS Login.
GCIAM-001: Service account has Owner or Editor role on project
The basic roles (Owner, Editor) predate IAM and grant extremely broad access. A compromised service account with roles/owner can modify IAM policies, delete resources, and exfiltrate data across the entire project.
Recommended action
Replace the Owner/Editor binding with a scoped predefined or custom role that grants only the permissions the service account needs. roles/owner and roles/editor grant full or near-full access to every resource in the project.
GCIAM-002: Service account has user-managed key
User-managed service account keys are JSON files that act as permanent credentials. They don't expire by default, can be downloaded by anyone with the right IAM role, and are the most common GCP credential found in public leaks.
Recommended action
Delete user-managed keys and use workload identity federation, attached service accounts, or the metadata server instead. User-managed keys are long-lived credentials that cannot be automatically rotated by GCP.
GCIAM-003: Service account token creator granted without constraint
roles/iam.serviceAccountTokenCreator allows a principal to mint OAuth2 tokens and sign JWTs as any service account in the project. A project-level grant without a condition is effectively a privilege-escalation vector.
Recommended action
Restrict iam.serviceAccountTokenCreator bindings to specific service accounts using IAM conditions (resource.name == 'projects/-/serviceAccounts/TARGET'). Avoid project-level grants of this role.
GCIAM-004: Compute instance uses default service account
The Compute Engine default service account (*-compute@developer.gserviceaccount.com) is automatically granted the Editor role on the project. Any workload running under it inherits near-full access to every resource.
Recommended action
Create a dedicated service account with minimum required permissions for each workload. Replace the default compute service account on every instance.
GCIAM-005: Domain-restricted sharing constraint not enforced
Without the domain-restricted sharing constraint, any GCP user with sufficient IAM permissions can grant access to arbitrary external Google accounts, enabling data exfiltration or persistence by outside parties.
Recommended action
Set the iam.allowedPolicyMemberDomains organization policy constraint to limit IAM bindings to your corporate domain(s). This prevents accidental or malicious grants to external accounts.
GCIAM-006: Service account key older than 90 days
Long-lived service account keys increase the blast radius of a credential leak. CIS GCP Foundations recommends rotating user-managed keys at most every 90 days.
Recommended action
Rotate or delete user-managed service account keys older than 90 days. Prefer workload identity federation to eliminate long-lived keys entirely.
GCKMS-001: KMS key rotation period exceeds 365 days
Regular key rotation limits the window of exposure if a key version is compromised. CIS GCP Foundations requires rotation within 365 days for symmetric encryption keys.
Recommended action
Set the rotation period to 365 days or less. GCP automatically creates a new key version when the rotation period elapses.
GCKMS-002: KMS key IAM policy grants public access
A KMS key with allUsers access allows anyone on the internet to encrypt, decrypt, or sign data with the key, depending on the granted role.
Recommended action
Remove allUsers and allAuthenticatedUsers from the key's IAM policy. KMS keys should only be accessible to service accounts that need them.
GCKMS-003: KMS key not using HSM protection level
SOFTWARE protection level keys are managed in software; HSM protection level keys are backed by Cloud HSM (FIPS 140-2 Level 3). HSM adds defense against certain insider and physical-access threats.
Recommended action
Use HSM (Hardware Security Module) protection level for keys that protect sensitive data. HSM keys never leave the hardware boundary.
GCKMS-004: KMS key ring IAM has overly broad bindings
KMS key ring IAM policies govern access to every key in the ring. An overly broad binding (allUsers, allAuthenticatedUsers) grants the entire internet access to encrypt, decrypt, or manage keys.
Recommended action
Remove allUsers, allAuthenticatedUsers, and overly broad domain-scoped members from KMS key ring IAM policies. Restrict key access to specific service accounts that need encrypt/decrypt/sign operations.
GCKMS-005: KMS key has primary version scheduled for destruction
A key version scheduled for destruction will become permanently unavailable after the scheduled destroy time. Any data encrypted with that version becomes unrecoverable. This check flags keys where the primary version is pending destruction.
Recommended action
Review keys with DESTROY_SCHEDULED primary versions. If the key is still in use, cancel the destruction. If intentional, ensure all dependent services have migrated to a new key before the destruction window closes.
GCKMS-006: KMS key uses imported (external) key material
Keys with EXTERNAL or EXTERNAL_VPC protection level use key material imported from outside GCP. The security of these keys depends on the external key management infrastructure, which is outside GCP's control.
Recommended action
Document the key material import process and ensure the external key material is stored securely. Consider using GCP-generated key material (SOFTWARE or HSM protection level) unless regulatory requirements mandate external key management.
GCLOG-001: Cloud Audit Logs not enabled for all services
Admin Activity logs are always on, but Data Access logs (reads and writes to user data) must be explicitly enabled. Without them, access to Cloud Storage objects, BigQuery datasets, and other data resources is invisible.
Recommended action
Configure the project IAM policy's auditConfigs to enable Data Access audit logs for allServices. At minimum, enable ADMIN_READ and DATA_WRITE log types.
GCLOG-002: No log sink configured for audit logs
Cloud Logging retains logs for a limited period (30 days by default for _Default bucket). A log sink exports logs to a destination with configurable retention, enabling forensic analysis months after an incident.
Recommended action
Create a log sink that exports audit logs to a durable destination (Cloud Storage, BigQuery, or Pub/Sub) for long-term retention and analysis.
GCLOG-003: Log bucket retention less than 365 days
The default _Default log bucket retains logs for 30 days. Many compliance frameworks require at least one year of audit log retention.
Recommended action
Set the log bucket retention period to at least 365 days. For the _Default bucket, update the retention via gcloud logging buckets update.
GCLOG-004: VPC Flow Logs not enabled on subnet
VPC Flow Logs record a sample of network flows sent from and received by VM instances. Without them, lateral movement and data exfiltration over the network are invisible to security monitoring.
Recommended action
Enable VPC Flow Logs on all subnets. Flow logs capture a sample of network flows, enabling threat detection, traffic analysis, and compliance evidence.
GCLOG-005: Firewall rule logging not enabled
Without firewall rule logging, allowed and denied connection attempts are invisible. Enabling logs on every rule creates an audit trail for traffic flowing through VPC firewalls.
Recommended action
Enable logging on all firewall rules. Firewall logs record connections allowed and denied by each rule, supporting incident investigation and compliance evidence.
GCLOG-006: Critical service missing Data Access audit log types
While enabling allServices audit logging is a good baseline, critical services like Storage, IAM, and Compute should have explicit per-service audit configs to ensure visibility is not accidentally removed by a broad policy change.
Recommended action
Configure per-service audit log configs for storage.googleapis.com, iam.googleapis.com, and compute.googleapis.com to include all three log types: ADMIN_READ (1), DATA_WRITE (2), and DATA_READ (3).
GCLOG-007: No log metric filter for IAM policy changes
IAM policy changes are high-impact actions. A log-based metric and alert ensures that unexpected privilege escalation or access grants trigger an immediate notification rather than going unnoticed in the audit log.
Recommended action
Create a log-based metric with a filter matching IAM policy changes (e.g. resource.type="project" AND protoPayload.methodName="SetIamPolicy") and configure an alerting policy on it.
GCLOG-008: No log metric filter for firewall rule changes
Firewall rule changes can open unexpected ingress paths. A log-based metric and alert for firewall mutations catches accidental or malicious network policy changes in real time.
Recommended action
Create a log-based metric with a filter matching firewall rule changes (e.g. resource.type="gce_firewall_rule") and configure an alerting policy on it.
GCLOG-009: No log metric filter for route changes
Route changes can redirect network traffic through attacker-controlled instances. A log-based metric and alert for route mutations catches unauthorized traffic redirection attempts in real time.
Recommended action
Create a log-based metric with a filter matching route changes (e.g. resource.type="gce_route") and configure an alerting policy on it.
GCLOG-010: No log metric filter for Cloud SQL config changes
Cloud SQL configuration changes (disabling SSL, enabling public IP, modifying database flags) can weaken security. A log-based metric and alert for these mutations catches unauthorized database configuration changes.
Recommended action
Create a log-based metric with a filter matching Cloud SQL configuration changes (e.g. protoPayload.methodName="cloudsql.instances.update") and configure an alerting policy on it.
GCLOG-011: No log metric filter for custom role changes
Custom role changes can grant new permissions or weaken existing access controls. A log-based metric and alert for custom role mutations catches privilege escalation via role modification.
Recommended action
Create a log-based metric with a filter matching custom role changes (e.g. resource.type="iam_role") and configure an alerting policy on it.
GCNET-001: Default VPC network exists in project
Every new GCP project is created with a default network that includes auto-created subnets in every region and permissive firewall rules (allow SSH, RDP, ICMP from anywhere). Deleting it forces teams to create purpose-built networks.
Recommended action
Delete the default VPC network and create custom networks with explicitly defined subnets and firewall rules. The default network includes pre-populated firewall rules that allow broad internal traffic.
GCNET-002: No default-deny ingress firewall rule configured
GCP's implied firewall rules deny all ingress and allow all egress by default, but auto-created rules in the default network override this. An explicit deny-all ingress rule at low priority makes the deny posture visible and auditable.
Recommended action
Create a low-priority (e.g. 65534) INGRESS DENY ALL rule for 0.0.0.0/0 on each VPC network. This ensures that only explicitly allowed traffic reaches instances.
GCNET-003: Firewall allows SSH or RDP from the internet
Firewall rules allowing SSH or RDP from 0.0.0.0/0 expose instances to brute-force attacks and credential-stuffing from the entire internet. This is the most common initial access vector for cloud-hosted VMs.
Recommended action
Restrict SSH (tcp:22) and RDP (tcp:3389) firewall rules to specific source CIDR ranges (e.g. corporate VPN IPs). Use IAP TCP forwarding or OS Login instead of direct internet access.
GCNET-004: Subnet does not have Private Google Access enabled
Without Private Google Access, instances that lack an external IP cannot reach Google APIs (Cloud Storage, BigQuery, etc.). Enabling it lets private instances access these services without exposing them to the internet.
Recommended action
Enable Private Google Access on all subnets so that instances without external IPs can still reach Google APIs and services over Google's internal network.
GCNET-005: No Cloud NAT gateway configured
Cloud NAT provides outbound internet connectivity for instances without external IPs. Without it, private instances are cut off from external repositories, update servers, and third-party APIs unless a proxy is configured.
Recommended action
Configure a Cloud NAT gateway on at least one Cloud Router so that instances without external IPs can reach the internet for updates and package downloads without being directly addressable.
GCRUN-001: Cloud Run service allows unauthenticated access
A Cloud Run service with INGRESS_TRAFFIC_ALL allows any internet user to invoke it. If the service does not implement its own authentication, it is fully exposed.
Recommended action
Set the Cloud Run service ingress to INGRESS_TRAFFIC_INTERNAL_ONLY or INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER, or require authentication via IAM invoker bindings. Public services should be behind a load balancer with IAP or API Gateway.
GCRUN-002: Cloud Run service or function uses default compute SA
Cloud Run services and Cloud Functions default to the Compute Engine default service account, which usually holds the Editor role. A compromised function running under this SA can access nearly every resource in the project.
Recommended action
Assign a dedicated service account with minimum required permissions to every Cloud Run service and Cloud Function. The default compute SA typically has the Editor role.
GCRUN-003: Cloud Run service has zero minimum instances
A minimum instance count of zero means the service scales to zero when idle. The first request after idle incurs a cold start delay. For security-sensitive services (auth endpoints, webhook receivers), cold starts can cause timeouts that mask availability.
Recommended action
Set min_instance_count to at least 1 for latency-sensitive services. Zero minimum instances cause cold starts on the first request after an idle period.
GCRUN-004: Cloud Run service does not use a VPC connector
Without a VPC connector, Cloud Run services route egress traffic through the public internet. Services that access databases, caches, or internal APIs over the internet increase their attack surface.
Recommended action
Configure a Serverless VPC Access connector on Cloud Run services that access internal resources. This routes egress traffic through the VPC, enabling private IP connectivity and firewall enforcement.
GCS-001: Cloud Storage bucket is publicly accessible
A bucket with allUsers or allAuthenticatedUsers in its IAM policy is accessible to the internet. Build artifacts, Terraform state files, and deployment manifests stored in public buckets are trivially discoverable.
Recommended action
Remove allUsers and allAuthenticatedUsers members from the bucket's IAM policy. Use signed URLs or IAM-authenticated access for legitimate public-facing content.
GCS-002: Bucket does not enforce uniform bucket-level access
Without uniform bucket-level access, objects can have individual ACLs that override bucket-level IAM policies. This creates an unauditable surface: a single object can be made public without changing the bucket policy.
Recommended action
Enable uniform bucket-level access on the bucket. This disables object-level ACLs and enforces access exclusively through IAM, simplifying policy management and auditing.
GCS-003: Bucket versioning not enabled
Without versioning, overwritten or deleted objects are permanently lost. Versioning makes every write and delete recoverable, protecting against accidental or malicious artifact replacement.
Recommended action
Enable object versioning on the bucket. Combine with a lifecycle rule to delete old versions after a retention period to control storage costs.
GCS-004: Cloud Storage bucket not encrypted with CMEK
By default GCP encrypts bucket data with Google-managed keys. CMEK adds an additional layer of control: you can revoke access to stored data by disabling the key, and key usage appears in Cloud Audit Logs.
Recommended action
Set a default Cloud KMS key on the bucket to use customer-managed encryption keys (CMEK). CMEK gives you control over the key lifecycle and access policy.
GCS-005: Cloud Storage bucket access logging not enabled
Cloud Storage access logs capture object-level operations that Cloud Audit Logs may not cover in detail. Without access logging, it is difficult to determine who accessed or modified specific objects after a security incident.
Recommended action
Enable access logging on the bucket by setting a log bucket destination. Access logs record every read and write, supporting forensic analysis and compliance audits.
GCSQL-001: Cloud SQL instance has a public IP address
A Cloud SQL instance with a public IP is directly reachable from the internet. Even with authorized networks configured, the attack surface is larger than a private-IP-only setup behind a VPC.
Recommended action
Disable the public IP on the Cloud SQL instance and use private IP with VPC peering or the Cloud SQL Auth Proxy for connectivity. If a public IP is required, restrict authorized networks to specific CIDR ranges.
GCSQL-002: Cloud SQL instance does not have automated backups enabled
Without automated backups, a destructive action (accidental DROP TABLE, ransomware, or a rogue admin) can cause permanent data loss. Automated backups provide a recovery point within the configured retention window.
Recommended action
Enable automated backups on every Cloud SQL instance. Automated backups protect against data loss from accidental deletion, corruption, or ransomware.
GCSQL-003: Cloud SQL instance does not require SSL connections
Without SSL enforcement, database connections can be intercepted on the network. An attacker with network access can capture credentials and query results in plaintext.
Recommended action
Set requireSsl to true in the Cloud SQL instance's ipConfiguration. This ensures all client connections are encrypted with TLS.
GCSQL-004: Cloud SQL instance does not have IAM authentication enabled
IAM database authentication ties database access to centrally managed IAM identities. Without it, database credentials are managed separately, increasing the risk of stale or shared passwords.
Recommended action
Enable IAM database authentication by setting the cloudsql.iam_authentication database flag to 'on'. This allows using IAM-managed identities instead of built-in database passwords.
GCSQL-005: Cloud SQL instance does not have point-in-time recovery enabled
Automated backups alone only allow recovery to the latest backup. Point-in-time recovery extends this to any second within the log retention window, reducing the recovery point objective (RPO) from hours to seconds.
Recommended action
Enable point-in-time recovery (PITR) on every Cloud SQL instance. PITR uses write-ahead logs to allow recovery to any point within the retention window, minimizing data loss.
Adding a new GCP check
- Create a new module at
pipeline_check/core/checks/gcp/rules/NNN_<name>.pyexporting a top-levelRULE = Rule(...)and acheck(path, doc) -> Findingfunction. The orchestrator auto-discoversRULEand callscheckwith the parsed YAML document. - Add a mapping for the new ID in
pipeline_check/core/standards/data/owasp_cicd_top_10.py(and any other standard that applies). - Drop unsafe/safe snippets at
tests/fixtures/per_check/gcp/-NNN.{unsafe,safe}.ymland add aCheckCaseentry intests/test_per_check_real_examples.py::CASES. - Regenerate this doc: