gcp-certmanager-sign-cmek-gate
Issue from a KMS-backed CA; the signature is performed by the CAS service agent, not the caller.
match (effective permission)
{
"action": "privateca.certificates.create",
"resource_type": "google.privateca.CaPool"
}
where
principal ALSO has EFFECTIVE privateca.caPools.use on the SAME pool
the target CA is KMS-backed (Enterprise tier over a customer Cloud KMS/HSM key)
emit
| source type | Identity |
|---|---|
| source | <principal> |
| target | <CA pool node (SigningKey)> |
| permissions | privateca.certificates.create privateca.caPools.use |
| conditions | iam_permission key_permission |
| state logic | ACTIVE if the CAS service agent (service-PROJECT_NUMBER@gcp-sa-privateca.iam.gserviceaccount.com) holds cloudkms.cryptoKeyVersions.useToSign on the CA signing key AND the key/version and CA are ENABLED; CONDITIONAL(key_permission) if the service-agent grant or key/CA state is unknown; BLOCKED if the service agent lacks the grant OR the key is DISABLED/DESTROYED OR the CA is DISABLED OR a KMS deny applies. |
Narrative
{principal.name} can issue certificates from KMS-backed CA pool {capool.name} (privateca.certificates.create + caPools.use); the CAS service agent (not the caller) signs via cloudkms.cryptoKeyVersions.useToSign on the CA key - issuance holds while the agent retains that grant and the key/CA is enabled.
Raw rule rules/derived/gcp/certmanager.yaml
id: gcp-certmanager-sign-cmek-gate
emits: CanSignAs
description: Issue from a KMS-backed CA; the signature is performed by the CAS service agent, not the
caller.
match_effective_permission:
action: privateca.certificates.create
resource_type: google.privateca.CaPool
where:
- principal ALSO has EFFECTIVE privateca.caPools.use on the SAME pool
- the target CA is KMS-backed (Enterprise tier over a customer Cloud KMS/HSM key)
emit:
source_type: Identity
source: <principal>
target: <CA pool node (SigningKey)>
permissions:
- privateca.certificates.create
- privateca.caPools.use
conditions:
- iam_permission
- key_permission
state_logic: ACTIVE if the CAS service agent (service-PROJECT_NUMBER@gcp-sa-privateca.iam.gserviceaccount.com)
holds cloudkms.cryptoKeyVersions.useToSign on the CA signing key AND the key/version and CA are ENABLED;
CONDITIONAL(key_permission) if the service-agent grant or key/CA state is unknown; BLOCKED if the
service agent lacks the grant OR the key is DISABLED/DESTROYED OR the CA is DISABLED OR a KMS deny
applies.
confidence: min(contributing_confidences) * 0.93
derived_from:
- <effective privateca.certificates.create grant>
- <effective privateca.caPools.use grant>
- <service-agent KMS useToSign binding>
false_positive_note: "GCP-SPECIFIC: the key_permission gate is on the CAS SERVICE AGENT, NOT the caller.\
\ Do NOT require the caller to hold cloudkms.* \u2014 a caller with certificates.create + caPools.use\
\ issues successfully while the service agent keeps useToSign. Requiring caller KMS access (AWS ACM\
\ PCA style) yields false negatives. Downgrade to CONDITIONAL/BLOCKED only on the SERVICE AGENT's\
\ grant and the key/CA enabled state."
narrative: "{principal.name} can issue certificates from KMS-backed CA pool {capool.name} (privateca.certificates.create\
\ + caPools.use); the CAS service agent (not the caller) signs via cloudkms.cryptoKeyVersions.useToSign\
\ on the CA key \u2014 issuance holds while the agent retains that grant and the key/CA is enabled."