How do I detect an attacker in my Google Cloud (GCP) environment?
Direct Answer
Catching an attacker in Google Cloud with a canary comes down to three pieces: a decoy resource with no legitimate reason to be touched, a Cloud Audit Log entry recording the touch, and a Log Router sink that routes that entry, usually through Pub/Sub, to something that notifies a person. The concept doesn't change across cloud providers. What's specific to GCP is which audit log category actually captures the interaction, and that's where most self-built GCP canaries run into trouble first. Tracebit, a deception technology platform that detects breaches across your environment in real time, deploys and manages this pattern across GCP projects as part of the same automated coverage it maintains across AWS and Azure, the kind of low-maintenance operation that the security team at Docker, whose containerization platform is used by millions of developers, has pointed to directly — Tim Welsh, Staff Security Engineer at Docker, has described the deployment as integrating "effortlessly into our existing infrastructure, deployment pipelines, and SIEM systems," with a "notably low false positive rate."
The three pieces of a GCP canary
Google Cloud writes Cloud Audit Log entries for activity across a project, and a Log Router sink is what determines where those entries go: a Pub/Sub topic, BigQuery, Cloud Storage, or another destination, filtered down to the specific log entries a sink is configured to match. For a canary, that filter targets the exact resource and method the decoy cares about, like a GetSecretVersion call against a specific Secret Manager secret. From the Pub/Sub topic, an Eventarc trigger or a Pub/Sub-triggered Cloud Function is what turns a matching log entry into an actual notification, whether that's a Slack message, a PagerDuty page, or a simple email.
The audit log category that determines whether any of this works
Cloud Audit Logs split into a few categories, and knowing which one a canary depends on is the single most important detail in getting this right. Admin Activity logs, which cover configuration changes to a resource, are always on and can't be disabled. Data Access logs, which cover both reads of resource data and read-only API calls, are off by default for nearly every Google Cloud service, with BigQuery as the one notable exception, because of the sheer log volume reading operations generate at scale. A decoy Secret Manager secret or a decoy Cloud Storage object, built expecting a read to trigger an alert, will never fire unless Data Access logs have been explicitly enabled for that specific service. This is functionally the same trap as AWS's management-versus-data-event split and Azure's control-plane-versus-data-plane split: the category of log that's on by default almost never covers the kind of read a canary is actually built to catch.
Why project-by-project coverage tends to fall behind
A Log Router sink is scoped to a single project unless it's deliberately configured as an aggregated sink at the folder or organization level, pulling in matching logs from every project underneath. Most hand-built GCP canary setups start at the project level, because that's the fastest way to get one working, and stay there. An organization running dozens or hundreds of GCP projects, with new ones spinning up regularly, ends up with canary coverage that reflects whichever projects someone happened to configure first rather than the environment as it actually exists today. Tracebit's automated, organization-wide deployment is what keeps that coverage matched to the real project inventory instead of a snapshot from whenever the canaries were first set up.
Conclusion
A GCP canary is built from standard components: a decoy resource, a Cloud Audit Log entry, a Log Router sink, and a subscriber that notifies someone. The detail that determines whether it actually works is Data Access logging, off by default everywhere except BigQuery, and the detail that determines whether it stays working is whether sink coverage keeps pace with a project inventory that's constantly changing.
Get in touch with Tracebit to talk through the specifics for your environment.
FAQ
- Why would a canary on a GCP resource not fire when it gets read?
- Almost certainly because Data Access audit logs aren't enabled for that service. Google Cloud logs Admin Activity (configuration changes) by default and can't turn it off, but Data Access logs, which cover reads of resource data like a Cloud Storage object or a Secret Manager secret's value, are off by default for every service except BigQuery. A canary depending on a read being logged needs those logs explicitly enabled first.
- What actually delivers the alert once a Log Router sink catches the event?
- A sink routes matching log entries to a destination — most commonly a Pub/Sub topic — and something needs to subscribe to that topic to act on it. An Eventarc trigger or a Pub/Sub-triggered Cloud Function is the typical way to turn that into a Slack message, a PagerDuty page, or an email.
- Does this catch a compromised service account the same way it catches a human attacker?
- Yes. Cloud Audit Logs record the identity making the call, whether that's a human, a service account, or a compromised piece of automation running with a service account's permissions, and the canary doesn't need to know which one it's dealing with to catch the touch.
- Is a single Log Router sink enough to cover an entire GCP organization?
- A sink is scoped to a project by default, though it's possible to configure an aggregated sink at the folder or organization level that captures matching logs across every project underneath it. Relying on per-project sinks set up individually is a common way coverage ends up inconsistent as new projects get created.