BrighterCommand / BrighterCommand/Brighter
GCP Pub/Sub: creating a DLQ-backed channel requires project IAM admin and hard-fails without it
- 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
Assessment
This issue has not been assessed yet.