gcp-spanner-read-data
spanner.databases.read (or spanner.databases.select) + spanner.sessions.create together constitute effective data-plane read access: the caller can open a session and execute SQL to read arbitrary rows from any table in the database.
match (effective permission)
{
"action": "spanner.databases.read",
"resource_type": "google.spanner.admin.database.v1.Database"
}
where
?principal ALSO has EFFECTIVE spanner.sessions.create on the same database or instance scope
database.state == READY
emit
| source type | Identity |
|---|---|
| target type | RelationalDatabase |
| source | <principal> |
| target | <Spanner Database node> |
| permissions | spanner.databases.read spanner.sessions.create |
| conditions | iam_permission |
| state logic | ACTIVE when both spanner.databases.read (or spanner.databases.select) and spanner.sessions.create are confirmed ACTIVE effective permissions on the database or enclosing instance/project scope AND the database state is READY AND no VPC-SC perimeter blocks the Spanner API for the caller's network context. CONDITIONAL(iam_permission) when sessions.create is uncertain or held only at a broader scope that may be capped by an IAM deny policy; BLOCKED when a VPC-SC perimeter enforces and the caller is outside the perimeter. NOTE: If a database is encrypted with Cloud KMS CMEK and the CMEK key is DISABLED or DESTROYED (not just inaccessible to the caller), the database becomes unavailable and should emit BLOCKED(key_permission) with denied_by = <KMS key resource name>. If the CMEK key is ENABLED and operational, the Spanner SERVICE AGENT (not the caller) holds the KMS decrypt rights — the caller's CanReadData is ACTIVE if their IAM permissions are satisfied. The credential-chains.yaml decrypt-gates-secret-read linchpin does NOT apply to this rule. |
Narrative
{principal.name} holds spanner.databases.read + spanner.sessions.create on database {database.name}; it can open a Spanner session and execute SQL to read arbitrary rows from any table in the database.
Raw rule rules/derived/gcp/spanner.yaml
id: gcp-spanner-read-data
emits: CanReadData
description: 'spanner.databases.read (or spanner.databases.select) + spanner.sessions.create together
constitute effective data-plane read access: the caller can open a session and execute SQL to read arbitrary
rows from any table in the database.'
match_effective_permission:
action: spanner.databases.read
resource_type: google.spanner.admin.database.v1.Database
where:
- ?principal ALSO has EFFECTIVE spanner.sessions.create on the same database or instance scope
- database.state == READY
emit:
source_type: Identity
target_type: RelationalDatabase
source: <principal>
target: <Spanner Database node>
permissions:
- spanner.databases.read
- spanner.sessions.create
conditions:
- iam_permission
state_logic: "ACTIVE when both spanner.databases.read (or spanner.databases.select) and spanner.sessions.create\
\ are confirmed ACTIVE effective permissions on the database or enclosing instance/project scope AND\
\ the database state is READY AND no VPC-SC perimeter blocks the Spanner API for the caller's network\
\ context. CONDITIONAL(iam_permission) when sessions.create is uncertain or held only at a broader\
\ scope that may be capped by an IAM deny policy; BLOCKED when a VPC-SC perimeter enforces and the\
\ caller is outside the perimeter. NOTE: If a database is encrypted with Cloud KMS CMEK and the CMEK\
\ key is DISABLED or DESTROYED (not just inaccessible to the caller), the database becomes unavailable\
\ and should emit BLOCKED(key_permission) with denied_by = <KMS key resource name>. If the CMEK key\
\ is ENABLED and operational, the Spanner SERVICE AGENT (not the caller) holds the KMS decrypt rights\
\ \u2014 the caller's CanReadData is ACTIVE if their IAM permissions are satisfied. The credential-chains.yaml\
\ decrypt-gates-secret-read linchpin does NOT apply to this rule."
false_positive_note: "BOTH permissions are required: spanner.databases.read alone does not allow executing\
\ queries without a session (sessions.create), and spanner.sessions.create alone does not grant data\
\ access without read permissions. The permission evaluator must resolve BOTH at the effective (post-hierarchy)\
\ level: a project-level binding of roles/spanner.databaseReader grants spanner.databases.select +\
\ spanner.sessions.create (NOT spanner.databases.read). A project-level binding of roles/spanner.databaseUser\
\ (or higher) grants spanner.databases.read + spanner.sessions.create. Do NOT conflate spanner.databases.read\
\ (in roles/spanner.databaseUser and above) with spanner.databases.select (read-only; in roles/spanner.databaseReader).\
\ Both grant data read; the effective evaluator should emit CanReadData for either when combined with\
\ sessions.create. For CMEK-encrypted databases: it is the Spanner SERVICE AGENT (service-PROJECT_NUMBER@gcp-sa-spanner.iam.gserviceaccount.com)\
\ that must hold cloudkms.cryptoKeyVersions.useToDecrypt on the CMEK key, not the caller. The caller's\
\ IAM permissions determine their own data-plane access. If the CMEK key is disabled or destroyed,\
\ the database becomes inaccessible \u2014 emit BLOCKED with denied_by set to the key resource. If\
\ the CMEK key is operational, emit CanReadData as ACTIVE (the service agent's decrypt is transparent\
\ to the caller). VPC-SC perimeters (from accesscontextmanager.yaml) that include the Spanner API\
\ block callers outside the perimeter regardless of IAM \u2014 emit BLOCKED in that case."
narrative: '{principal.name} holds spanner.databases.read + spanner.sessions.create on database {database.name};
it can open a Spanner session and execute SQL to read arbitrary rows from any table in the database.'
derived_from:
- spanner.databases.read (or spanner.databases.select) effective permission on Database
- spanner.sessions.create effective permission on same scope