gcp-kms-service-agent-cmek-gate

A CMEK-encrypted Secret Manager secret is readable while the service agent holds useToDecrypt on the key.

derived gcp emits CanReadSecret

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanReadSecret {'secret': None}

where

?secret is CMEK-encrypted (customerManagedEncryption.kmsKeyName references a Cloud KMS key ?key) the CONSUMING service agent (e.g. service-PROJECT_NUMBER@gcp-sa-secretmanager.iam.gserviceaccount.com) holds cloudkms.cryptoKeyVersions.useToDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter) on ?key

emit

source<principal>
target<CMEK-encrypted Secret>
permissionssecretmanager.versions.access cloudkms.cryptoKeyVersions.useToDecrypt
conditionsiam_permission resource_policy key_permission service_state
state logicACTIVE if the caller holds versions.access AND the SERVICE AGENT holds useToDecrypt on ?key AND >=1 key version 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 applies (versions.access then errors).

Narrative

{principal.name} can read CMEK secret {secret.name}; the consuming service agent (not the caller) decrypts it via cryptoKeyEncrypterDecrypter on {key.name} - access holds while the agent retains that grant and the key is enabled.

Raw rule rules/derived/gcp/kms.yaml

id: gcp-kms-service-agent-cmek-gate
emits: CanReadSecret
description: A CMEK-encrypted Secret Manager secret is readable while the service agent holds useToDecrypt
  on the key.
match:
- - principal: null
  - CanReadSecret
  - secret: null
where:
- ?secret is CMEK-encrypted (customerManagedEncryption.kmsKeyName references a Cloud KMS key ?key)
- the CONSUMING service agent (e.g. service-PROJECT_NUMBER@gcp-sa-secretmanager.iam.gserviceaccount.com)
  holds cloudkms.cryptoKeyVersions.useToDecrypt (roles/cloudkms.cryptoKeyEncrypterDecrypter) on ?key
emit:
  source: <principal>
  target: <CMEK-encrypted Secret>
  permissions:
  - secretmanager.versions.access
  - cloudkms.cryptoKeyVersions.useToDecrypt
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  - service_state
  state_logic: ACTIVE if the caller holds versions.access AND the SERVICE AGENT holds useToDecrypt on
    ?key AND >=1 key version 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 applies (versions.access then errors).
  confidence: min(contributing_confidences) * 0.93
  derived_from:
  - <CanReadSecret edge_id>
  - <service-agent useToDecrypt binding on ?key>
  false_positive_note: 'GCP-SPECIFIC: the key_permission gate is on the SERVICE AGENT, NOT the caller.
    A caller with only secretmanager.versions.access reads a CMEK secret while the service agent keeps
    useToDecrypt. Requiring caller KMS decrypt (AWS Secrets Manager model) produces false negatives. Only
    downgrade to CONDITIONAL/BLOCKED based on the SERVICE AGENT''s grant and the key''s enabled state.
    This rule REFINES an existing CanReadSecret from Secret Manager; it does not invent a new secret read
    from KMS (KMS holds no secret payloads).'
  narrative: "{principal.name} can read CMEK secret {secret.name}; the consuming service agent (not the\
    \ caller) decrypts it via cryptoKeyEncrypterDecrypter on {key.name} \u2014 access holds while the\
    \ agent retains that grant and the key is enabled."
move · open · esc close