Back to Checks
Project members are not assigned both Cloud KMS Admin and CryptoKey Encrypter/Decrypter roles
iam_role_kms_enforce_separation_of_duties
Service: iamSeverity: medium
OfficialBuilt-inVerified
Categories:identity-accessencryption
Resource Type:cloudresourcemanager.googleapis.com/Project
Project IAM assignments are analyzed for Cloud KMS separation of duties: principals simultaneously granted roles/cloudkms.admin and any of roles/cloudkms.cryptoKeyEncrypterDecrypter, roles/cloudkms.cryptoKeyEncrypter, or roles/cloudkms.cryptoKeyDecrypter.
Risk
Combining key management and key usage undermines confidentiality, integrity, and availability:
- Unauthorized decryption of sensitive data
- Tampering with policies or rotation to conceal access
- Disabling or destroying keys, causing outages
This concentration of power reduces oversight and auditability.
Run this check with Prowler
Run in Prowler CloudRun
prowler gcp --checks iam_role_kms_enforce_separation_of_dutiesRecommendation
Apply least privilege and separation of duties:
- Never combine
roles/cloudkms.adminwith anyroles/cloudkms.cryptoKey* - Isolate key management and usage in dedicated projects
- Require approvals, log all key access, and monitor
- Avoid broad
roles/owneron key scopes
Remediation
CLI
gcloud projects remove-iam-policy-binding <PROJECT_ID> --member=<MEMBER> --role=roles/cloudkms.adminTerraform
resource "google_project_iam_binding" "<example_resource_name>" {
project = "<PROJECT_ID>"
role = "roles/cloudkms.admin" # Critical: ensure the offending principal is NOT bound as KMS Admin
members = [
"user:<ALLOWED_MEMBER_EMAIL>" # Critical: exclude any member who also has CryptoKey* roles to enforce separation of duties
]
}Other
1In Google Cloud Console, go to IAM & Admin > IAM
2Locate the principal listed in the finding and click Edit principal
3Remove either "Cloud KMS Admin" or any of the "Cloud KMS CryptoKey Encrypter/Decrypter" roles from the project
4Click Save
Check Code
from prowler.lib.check.models import Check, Check_Report_GCP
from prowler.providers.gcp.services.cloudresourcemanager.cloudresourcemanager_client import (
cloudresourcemanager_client,
)
class iam_role_kms_enforce_separation_of_duties(Check):
def execute(self) -> Check_Report_GCP:
findings = []
for project in cloudresourcemanager_client.project_ids:
non_compliant_members = []
kms_admin_members = []
report = Check_Report_GCP(
metadata=self.metadata(),
resource=cloudresourcemanager_client.projects[project],
project_id=project,
location=cloudresourcemanager_client.region,
resource_name=(
cloudresourcemanager_client.projects[project].name
if cloudresourcemanager_client.projects[project].name
else "GCP Project"
),
)
report.status = "PASS"
report.status_extended = f"Principle of separation of duties was enforced for KMS-Related Roles in project {project}."
for binding in cloudresourcemanager_client.bindings:
if binding.project_id == project:
if "roles/cloudkms.admin" in binding.role:
kms_admin_members.extend(binding.members)
if (
"roles/cloudkms.cryptoKeyEncrypterDecrypter" in binding.role
or "roles/cloudkms.cryptoKeyEncrypter" in binding.role
or "roles/cloudkms.cryptoKeyDecrypter" in binding.role
):
for member in binding.members:
if member in kms_admin_members:
non_compliant_members.append(member)
if non_compliant_members:
report.status = "FAIL"
report.status_extended = f"Principle of separation of duties was not enforced for KMS-Related Roles in project {project} in members {','.join(non_compliant_members)}."
findings.append(report)
return findings