Skip to content

0013 - Use an Unconditional Public Object-Reader Binding

Status: Accepted Date: 2026-08-11 Author: Manuel Nucci Supersedes: ADR 0011's public-bucket IAM mechanism only

Context

ADR 0011 keeps a dedicated CDN deferred and serves immutable, non-confidential team logos directly from the public-assets Cloud Storage bucket. Its proposed conditional allUsers binding cannot be applied: Cloud Storage rejects IAM Conditions on a binding for the public principal.

The public access boundary remains narrow. A known logo URL may be fetched, but the bucket must not be listable and private profile media must remain unreachable anonymously. Uniform bucket-level access remains enabled, and the project-level Domain Restricted Sharing exception remains limited to the FPC projects.

Google Cloud Storage documents roles/storage.legacyObjectReader as the IAM equivalent of the object READER role under uniform bucket-level access. It grants storage.objects.get only. In contrast, roles/storage.objectViewer also grants storage.objects.list and would expose bucket enumeration. Cloud Storage IAM roles and uniform bucket-level access document these permissions and the role mapping.

Decision

  • Grant allUsers the unconditional bucket-level roles/storage.legacyObjectReader role on public-assets.
  • Keep uniform bucket-level access enabled and grant no role that includes storage.objects.list.
  • Keep the project-level Domain Restricted Sharing exception required for the public-principal binding. Do not relax the inherited organization policy.
  • Keep private-media separate with public access prevention enforced and no public IAM binding.
  • Keep opaque, immutable logo keys and Cache-Control: public, max-age=31536000, immutable. Keep the dedicated CDN, external Application Load Balancer, and custom media hostname deferred under ADR 0011's reconsideration criteria.

This supersedes only ADR 0011's conditional public-bucket binding. ADR 0011's direct-delivery, caching, discovery-authorization, known-object-read, private-media, and DRS-scope decisions remain in force.

Consequences

Positive

  • Known public logo objects are readable anonymously without allowing bucket listing.
  • The IAM policy uses a documented Cloud Storage role rather than a custom role with equivalent permissions.
  • The deployment mechanism is compatible with Cloud Storage's public-principal IAM constraint.

Negative

  • The bucket-level grant applies to every object in public-assets; access cannot be narrowed further with an IAM Condition on allUsers.
  • Anyone who obtains a logo URL can retrieve its non-confidential contents.

Mitigations

  • Store only non-confidential team logos in public-assets; keep profile media in private-media with public access prevention enforced.
  • Use opaque, versioned object keys and avoid confidential data in object paths.
  • Test anonymous known-object reads, anonymous bucket-list denial, and anonymous private-media denial whenever the bucket IAM policy changes.