gcp-bigtable-read-data-cmek

bigtable.tables.readRows on a CMEK-encrypted cluster; the Bigtable service agent (not the caller) decrypts via Cloud KMS envelope encryption - CanReadData is CONDITIONAL(key_permission) on the service agent's KMS grant and the key's ENABLED state.

derived gcp emits CanReadData

match (effective permission)

{ "action": "bigtable.tables.readRows", "resource_type": "google.bigtable.admin.v2.Table" }

where

cluster.encryptionConfig.kmsKeyName NOT_EQUALS NULL

emit

source typeIdentity
target typeNoSQLDatabase
source<principal>
target<CMEK Bigtable Table / NoSQLDatabase node>
permissionsbigtable.tables.readRows
conditionsiam_permission resource_policy key_permission
state logicACTIVE if the Bigtable service agent (service-PROJECT_NUMBER@gcp-sa-bigtable.iam.gserviceaccount.com) holds cloudkms.cryptoKeyVersions.useToEncryptDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter) on the cluster's encryptionConfig.kmsKeyName AND the key version is ENABLED; CONDITIONAL(key_permission) if the service-agent grant or key state is unknown or unconfirmed by the collector; BLOCKED if the service agent lacks the grant, OR the KMS key is in DISABLED or DESTROYED state (table rows become inaccessible even with valid IAM). This state is on the SERVICE AGENT's KMS access, never on the caller's own cloudkms.* permissions.

Narrative

{principal.name} holds bigtable.tables.readRows on CMEK table {table.name} in cluster {cluster.name} (kmsKeyName: {key.name}); the Bigtable service agent - not the caller - decrypts rows using cloudkms.cryptoKeyEncrypterDecrypter on {key.name}. Access is ACTIVE while the service agent retains that grant and the key is enabled.

Raw rule rules/derived/gcp/bigtable.yaml

id: gcp-bigtable-read-data-cmek
emits: CanReadData
description: "bigtable.tables.readRows on a CMEK-encrypted cluster; the Bigtable service agent (not the\
  \ caller) decrypts via Cloud KMS envelope encryption \u2014 CanReadData is CONDITIONAL(key_permission)\
  \ on the service agent's KMS grant and the key's ENABLED state."
match_effective_permission:
  action: bigtable.tables.readRows
  resource_type: google.bigtable.admin.v2.Table
where:
- cluster.encryptionConfig.kmsKeyName NOT_EQUALS NULL
emit:
  source_type: Identity
  target_type: NoSQLDatabase
  source: <principal>
  target: <CMEK Bigtable Table / NoSQLDatabase node>
  permissions:
  - bigtable.tables.readRows
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  state_logic: ACTIVE if the Bigtable service agent (service-PROJECT_NUMBER@gcp-sa-bigtable.iam.gserviceaccount.com)
    holds cloudkms.cryptoKeyVersions.useToEncryptDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter)
    on the cluster's encryptionConfig.kmsKeyName AND the key version is ENABLED; CONDITIONAL(key_permission)
    if the service-agent grant or key state is unknown or unconfirmed by the collector; BLOCKED if the
    service agent lacks the grant, OR the KMS key is in DISABLED or DESTROYED state (table rows become
    inaccessible even with valid IAM). This state is on the SERVICE AGENT's KMS access, never on the caller's
    own cloudkms.* permissions.
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - bigtable.tables.readRows effective permission on CMEK Table
  - Bigtable service-agent KMS binding on cluster.encryptionConfig.kmsKeyName
  false_positive_note: "GCP-SPECIFIC: the key_permission gate is on the BIGTABLE SERVICE AGENT, not the\
    \ calling principal. A caller with only bigtable.tables.readRows reads CMEK data while the service\
    \ agent holds useToEncryptDecrypt. Do NOT require the caller to hold cloudkms.* permissions \u2014\
    \ requiring caller KMS decrypt (AWS-style) produces false negatives. Downgrade to CONDITIONAL(key_permission)\
    \ or BLOCKED only based on the service agent's grant state and the KMS key's enabled status. The credential-chains.yaml\
    \ decrypt-gates-secret-read linchpin upgrades the read when the service-agent gate is confirmed (collector\
    \ verifies service-agent IAM binding on the KMS key). Backups inherit the cluster's CMEK encryption\
    \ \u2014 a restored table in a different cluster uses THAT cluster's CMEK key."
  narrative: "{principal.name} holds bigtable.tables.readRows on CMEK table {table.name} in cluster {cluster.name}\
    \ (kmsKeyName: {key.name}); the Bigtable service agent \u2014 not the caller \u2014 decrypts rows\
    \ using cloudkms.cryptoKeyEncrypterDecrypter on {key.name}. Access is ACTIVE while the service agent\
    \ retains that grant and the key is enabled."
move · open · esc close