gcp-firestore-read-data-cmek
datastore.entities.get on a CMEK Firestore database; the Firestore Service Agent (not the caller) decrypts using its KMS grant. CanReadData state is ACTIVE if the service agent's KMS grant is confirmed, CONDITIONAL(key_permission) if the grant state is unknown, and BLOCKED if the grant is denied or the key is disabled.
match (effective permission)
{
"action": "datastore.entities.get",
"resource_type": "google.firestore.Database"
}
where
database IS CMEK-encrypted (cmekConfig.kmsKeyName is set)
emit
| source type | Identity |
|---|---|
| target type | Data |
| source | <principal> |
| target | <CMEK Firestore Database / NoSQLDatabase node> |
| permissions | datastore.entities.get |
| conditions | iam_permission resource_policy key_permission |
| state logic | ACTIVE if the Firestore Service Agent (service-PROJECT_NUMBER@gcp-sa-firestore.iam.gserviceaccount.com) holds cloudkms.cryptoKeyVersions.useToDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter) on the cmekConfig.kmsKeyName AND the key version is ENABLED; CONDITIONAL(key_permission) if the service-agent grant or key state is unknown; BLOCKED if the service agent lacks the grant, OR the key is DISABLED/DESTROYED, OR a KMS deny policy targets the service agent's decrypt (data operations then return KMS errors). |
Narrative
{principal.name} holds datastore.entities.get on CMEK database {database.name} (cmekConfig.kmsKeyName: {key.name}); the Firestore Service Agent - not the caller - decrypts documents 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/firestore.yaml
id: gcp-firestore-read-data-cmek
emits: CanReadData
description: datastore.entities.get on a CMEK Firestore database; the Firestore Service Agent (not the
caller) decrypts using its KMS grant. CanReadData state is ACTIVE if the service agent's KMS grant is
confirmed, CONDITIONAL(key_permission) if the grant state is unknown, and BLOCKED if the grant is denied
or the key is disabled.
match_effective_permission:
action: datastore.entities.get
resource_type: google.firestore.Database
where:
- database IS CMEK-encrypted (cmekConfig.kmsKeyName is set)
emit:
source_type: Identity
target_type: Data
source: <principal>
target: <CMEK Firestore Database / NoSQLDatabase node>
permissions:
- datastore.entities.get
conditions:
- iam_permission
- resource_policy
- key_permission
state_logic: ACTIVE if the Firestore Service Agent (service-PROJECT_NUMBER@gcp-sa-firestore.iam.gserviceaccount.com)
holds cloudkms.cryptoKeyVersions.useToDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter) on the
cmekConfig.kmsKeyName AND the key version is ENABLED; CONDITIONAL(key_permission) if the service-agent
grant or key state is unknown; BLOCKED if the service agent lacks the grant, OR the key is DISABLED/DESTROYED,
OR a KMS deny policy targets the service agent's decrypt (data operations then return KMS errors).
confidence: min(contributing_confidences) * 0.93
derived_from:
- datastore.entities.get effective permission on CMEK Firestore Database
- Firestore Service Agent KMS binding on cmekConfig.kmsKeyName
false_positive_note: "GCP-SPECIFIC: the key_permission gate is on the FIRESTORE SERVICE AGENT, not the\
\ caller. Do NOT require the accessing principal to hold cloudkms.* \u2014 a caller with only datastore.entities.get\
\ reads CMEK documents while the service agent keeps useToDecrypt (same pattern as GCS CMEK and Secret\
\ Manager CMEK). Requiring caller KMS decrypt (AWS-style) produces false negatives. Downgrade to CONDITIONAL/BLOCKED\
\ based solely on the service agent's grant state and the key's enabled/disabled status. The KMS key\
\ is specified in cmekConfig.kmsKeyName on the Database resource; verify via firestore.projects.databases.get."
narrative: "{principal.name} holds datastore.entities.get on CMEK database {database.name} (cmekConfig.kmsKeyName:\
\ {key.name}); the Firestore Service Agent \u2014 not the caller \u2014 decrypts documents using cloudkms.cryptoKeyEncrypterDecrypter\
\ on {key.name}. Access is ACTIVE while the service agent retains that grant and the key is enabled."