BrighterCommand / BrighterCommand/Brighter

GCP Pub/Sub: creating a DLQ-backed channel requires project IAM admin and hard-fails without it

Open
#4,354 0 comments 0 reactions 1 assignee Claimed by @iancooper View on GitHub
Dominant language
C#
Stars
2.5k
Forks
296
Avg merge
1d 11h
Merged PRs (30d)
21

Description

## Summary

Creating a Pub/Sub channel whose subscription carries a `DeadLetterPolicy` unconditionally reads and
writes **project-level IAM**. If the caller's principal cannot do that, channel creation throws and the
consumer never starts — even when the dead-letter topic and its IAM binding already exist.

That makes Brighter require permissions an application service account usually does not have, because
the DLQ is normally provisioned by infrastructure-as-code rather than by the app at startup.

## The path

`GcpPubSubMessageGateway.EnsureSubscriptionExistsAsync` (`:235`) calls
`UpdateIAmRoleForDeadLetterAsync` (`:481`) whenever `DeadLetter != null`. That method makes two calls,
neither of which is guarded or optional:

1. `ProjectsClient.GetProjectAsync` — **Cloud Resource Manager**, used to derive the default Pub/Sub
service account when `DeadLetterPolicy.PublisherMember` is unset. Needs `resourcemanager.projects.get`.
2. `PublisherServiceApiClient.IAMPolicyClient.GetIamPolicyAsync` (then `SetIamPolicy` when the binding
is missing) on the dead-letter topic. Needs `pubsub.topics.getIamPolicy` / `setIamPolicy`.

Any failure propagates out of `CreateSyncChannel` / `CreateAsyncChannelAsync`.

## How it showed up

Running the FR-23 conformance behaviour (#4240 / #4297) against the Pub/Sub emulator
(`gcr.io/google.com/cloudsdktool/cloud-sdk:emulators`, `PUBSUB_EMULATOR_HOST=localhost:8085`), all
eight tests — four GCP configurations × both variants — fail in ~4 s during arrange, before any pump
runs:

| call | result on the emulator |
|---|---|
| `ProjectsClient.GetProjectAsync` | `Unauthenticated: Request had invalid authentication credentials` — the request **leaves for real GCP**. Resource Manager is a different service from Pub/Sub, so `PUBSUB_EMULATOR_HOST` cannot redirect it |
| `IAMPolicyClient.GetIamPolicyAsync` | `Unimplemented` — the emulator has no IAM surface at all |

The second was isolated rather than inferred: setting `DeadLetterPolicy.PublisherMember` explicitly
skips the Resource Manager call, and the run then fails one line later on `GetIamPolicy`.

## Suggested fix

Tolerate `Unimplemented` and `PermissionDenied` from both calls — log and continue rather than throw.
The binding may already exist, and a principal that cannot read the policy also cannot tell. Optionally
add an opt-out on `DeadLetterPolicy` for callers who provision IAM out of band.

## Impact

- **Production**: any application whose service account lacks project IAM admin cannot create a
DLQ-backed channel, even against a correctly provisioned topic.
- **Testing**: no GCP behaviour that needs a DLQ can be exercised against the Pub/Sub emulator, so the
four GCP FR-23 conformance cells can only ever be verified by `gcp-ci` against a real project.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.