Cloud Security Wire
AWS Azure GCP RSS
GCP Hardening Guide medium

GCP Organization Policy Baseline: Block Service Account Keys and Public Buckets Before They Exist

Organization Policy constraints stop risky GCP resources from being created at all. Which constraints are enforced by default, which are not, and the gcloud commands to audit and set them.

By Cloud Security Wire · ·
#GCP#Organization Policy#service account keys#Cloud Storage#public access prevention#gcloud#guardrails

Why preventive controls come first

Detection tools find a public bucket or a leaked service account key after it exists. Google Cloud Organization Policy constraints can refuse the action instead. A policy set at the organization or a folder is inherited by every project below it, so one change covers many projects. This guide covers four constraints worth checking in any GCP organization, and how to roll them out without breaking pipelines. Nothing here was tested in a live account; validate in a non-production folder first.

What new organizations get by default

Google’s secure-by-default page states that organizations created on or after 3 May 2024 have seven constraints enforced by default, including iam.managed.disableServiceAccountKeyCreation, iam.disableServiceAccountKeyUpload, iam.allowedPolicyMemberDomains and storage.uniformBucketLevelAccess. It adds that some organizations created between February and April 2024 may also have them.

Two consequences follow. Organizations created before that date may have none of these defaults. And storage.publicAccessPrevention is not in the default list we read, so even a new organization does not block public buckets unless you enforce it. Do not assume; check.

The constraints

ConstraintTypeEffect (per Google’s constraint reference)
iam.managed.disableServiceAccountKeyCreation (legacy: iam.disableServiceAccountKeyCreation)BooleanBlocks creation of service account keys and Cloud Storage HMAC keys
iam.managed.disableServiceAccountKeyUpload (legacy: iam.disableServiceAccountKeyUpload)BooleanBlocks uploading public keys to service accounts
storage.publicAccessPreventionBooleanBlocks ACLs and IAM bindings that grant access to allUsers and allAuthenticatedUsers; it overrides existing public permissions
iam.allowedPolicyMemberDomainsListLimits which organizations or Workspace customer IDs can appear in IAM policies

Key creation enforcement is not retroactive, so existing keys keep working. A blocked attempt returns FAILED_PRECONDITION: Key creation is not allowed on this service account.

Audit what is in effect

Check the effective policy on the organization and on a sample project:

gcloud org-policies describe iam.managed.disableServiceAccountKeyCreation \
  --effective --organization=ORGANIZATION_ID

gcloud org-policies describe storage.publicAccessPrevention \
  --effective --project=PROJECT_ID

A policy that is enforced shows enforce: true under spec.rules. Then list the user-managed keys that already exist, since the constraint will not remove them:

gcloud iam service-accounts keys list \
  --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
  --managed-by=user \
  --created-before=2026-05-05T00:00:00Z   # example date; use your own cutoff

Loop that over your service accounts and review anything old. For each key, find the workload that uses it and move it to Workload Identity Federation or short-lived credentials, both of which Google lists as alternatives.

Set the policy, dry run first

Policy file for a boolean constraint, following the format in Google’s documentation:

name: organizations/ORGANIZATION_ID/policies/storage.publicAccessPrevention
spec:
  rules:
  - enforce: true

Apply it with gcloud org-policies set-policy policy.yaml. The caller needs roles/orgpolicy.policyAdmin, and Google notes changes can take up to 15 minutes to take full effect.

Google also supports a dry-run mode, where violations are logged but not blocked. Add a dryRunSpec block to the file and apply it with --update-mask. One discrepancy: the usage guide shows --update-mask=dryRunSpec, while the set-policy command reference lists policy.dry_run_spec as the accepted value. Use the one your installed gcloud accepts, and check the help output. After a clean dry-run period, enforce with --update-mask=spec. Google’s guide also describes gcloud policy-intelligence simulate orgpolicy for previewing impact; we did not run it.

For a single bucket, public access prevention can be set directly:

gcloud storage buckets update gs://BUCKET_NAME --public-access-prevention
gcloud storage buckets describe gs://BUCKET_NAME \
  --format="default(public_access_prevention)"

Google’s page says a bucket set to enforced keeps the protection even if a higher-level policy is later switched off. Our assessment: set it directly on buckets holding sensitive data as a second layer.

Rollout notes

  • Enforce at a test folder first. Key creation blocks will break any deployment pipeline that still mints keys.
  • Plan exceptions deliberately. Google documents tag conditions for service-account constraints generally and excludes only iam.disableCrossProjectServiceAccountUsage, but it does not say specifically that they apply to the managed key-creation constraint. Test tag conditions before relying on them, or use a dedicated folder with its own policy.
  • Where keys are unavoidable, Google documents iam.serviceAccountKeyExpiryHours; it warns that expiry can cause outages, so review it before enabling.
  • Alert on SetOrgPolicy changes in Cloud Audit Logs (our assessment: an attacker with policy admin rights would loosen these first; we did not verify the exact log method name).

Checklist

  1. Run the effective-policy commands above on the organization and every top-level folder.
  2. Enforce key creation and key upload blocks; migrate remaining keys.
  3. Enforce storage.publicAccessPrevention after a dry run.
  4. Limit roles/orgpolicy.policyAdmin to a small group.

References

← All Analysis Subscribe via RSS