GCP security collector
om scan gcp requires GCP_PROJECT_ID. Clients use Application Default
Credentials (gcloud auth application-default login,
GOOGLE_APPLICATION_CREDENTIALS, or the metadata server). The Compute client
requests the cloud-platform scope. One scan covers one project.
export GCP_PROJECT_ID=my-project
gcloud auth application-default login
./om scan gcp
./om rules run GCP_REGION (default us-central1) is the region segment on service
account node ids and on the id of each bucket. Instance ids use the zone from the aggregated
list. The bucket node’s region field is the bucket location, which can differ
from the id segment.
Resources
| API | Graph |
|---|---|
Compute instances.aggregatedList | Workload. Properties: resource_id (self link), public_ip (NAT IP, or empty), status. A NAT IP or a public load balancer adds REACHABLE from internet:global when a firewall allow is not covered by a higher-priority deny. The firewall is a Network node. Each attached service account gets an ASSUMES edge from the instance. |
| Storage bucket list, then each bucket’s IAM policy | Datastore with resource_id (bucket name), public_access, and sensitivity when a bucket label names it. Bucket IAM members that can read objects get CAN_ACCESS to that bucket. |
| IAM service accounts, then the project IAM policy | Identity named by email, with email and admin_access. Project bindings that grant object access add CAN_ACCESS to every bucket in the project. |
| Cloud SQL instances | Datastore with resource_id (connection name), service: cloudsql, public_access, and sensitivity when a user label names it. A workload gets CAN_ACCESS when it shares the instance’s private-IP network, or its public IP is in an authorized network. |
Logging entries.list for the Admin Activity log, last 24 hours | Up to five audit_events on the identity and the resource the event names. See below. |
Node ids are gcp:{project}:{zone-or-region}:{kind}:{resource}. Instance
resources use the instance name. Service account resources use uniqueId, so the
displayed name (the email) can change without a new id.
When a bucket is public
public_access is true when the bucket IAM policy grants
allUsers or allAuthenticatedUsers one of these roles:
roles/storage.admin,roles/storage.objectAdmin,roles/storage.objectViewer- legacy bucket owner, legacy object owner, legacy object reader
- primitive owner, editor, or viewer
Public access prevention is not read. A bucket can have prevention unset and still be stored
as private when those members are absent. allUsers on a role that cannot read
objects, such as roles/storage.legacyBucketReader, does not set
public_access. A label named sensitivity or data-class
is stored as sensitivity. sensitivity wins when both are set, and a
blank value is omitted. Cloud SQL copies the same property from the instance’s user labels.
Service accounts and admin
admin_access comes from project IAM, not from the display name or email.
roles/owner, roles/editor, and
roles/resourcemanager.projectIamAdmin are admin. A custom role is admin when its
included permissions contain resourcemanager.projects.setIamPolicy. A binding
with a condition does not set the flag and does not add CAN_ACCESS.
Known service accounts use uniqueId as the node id. An instance service account
that is not in the project list uses the email as the id. Users, groups, and Workload
Identity principals are not inventoried.
Edges
ASSUMESfrom the instance to each service account inserviceAccounts. The workload is the source.CAN_ACCESSfrom a service account to a bucket when bucket IAM or project IAM grants object access. A bucket binding covers that bucket. A project binding covers every bucket in the scan.
Object access is roles/owner, roles/editor, the storage admin and
object roles (objectAdmin, objectViewer, objectUser,
and the legacy object reader/owner roles), or a custom role with
storage.objects.get. roles/viewer is not object access. A public
bucket with no such binding has no CAN_ACCESS edge, and a private bucket does
when IAM grants it. An instance with a NAT IP is not linked to buckets unless its service
account is. The bucket IAM read uses the v1 policy, so conditional bucket bindings are not
included. A bucket policy that cannot be read is stored as not public and adds no
CAN_ACCESS edges. The scan continues.
Cloud Audit Logs
After inventory is built, the scan lists Admin Activity log entries for the project from the
last 24 hours. The call is Logging entries.list on
projects/GCP_PROJECT_ID, filtered to
cloudaudit.googleapis.com/activity, newest first, and it stops after four pages.
A failed lookup omits audit_events and does not fail the scan.
An event is kept when its method is on a fixed admin list (for example
v1.compute.instances.insert, storage.buckets.update,
v1.compute.firewalls.patch, SetIamPolicy,
cloudsql.instances.update) and its principal email matches an identity already
in the batch. The resource has to sit on an exposed path: an internet-reachable workload, a
node that workload assumes or can access, a network that workload affects, a public
datastore, or an identity that can access a public datastore. Instance and firewall names
match the self link stored on the node. A bucket event uses
projects/_/buckets/NAME. A service-account key walks up to that identity. A
project SetIamPolicy event is stored on the identity, because the project is
not a node. The same event is stored on the identity and the resource, newest first, at
most five per node.
GET /v1/graph/query and om paths run list those events in
audits when the resource node is on the returned path. The console prints the
same lines under the path. Data Access logs, including object reads, are a different log, so
this slice does not collect them. CloudTrail events are collected by
om scan aws. Activity Log events are collected by
om scan azure.
Read access
The scan lists instances, firewalls, load-balancer pieces, buckets, bucket IAM, service
accounts, the project IAM policy, Cloud SQL instances, and Admin Activity log entries.
Listing those entries needs logging.logEntries.list. Logs Viewer
(roles/logging.viewer) includes it. The collector does not call mutating APIs
and does not request Data Access logs.
Copyright © 2026 OpenSourceOM. Licensed under Apache-2.0.