aws-samples / aws-samples/sample-autonomous-cloud-coding-agents

fix(bootstrap): least-privilege policies deny all nested-stack resources — default-on Agent Registry cannot deploy without AdministratorAccess

Aperta
#865 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
146
Fork
46
Merge medio
3g 10h
PR unite (30g)
24

Descrizione

**Component:** cdk (bootstrap policies)

## Describe the bug

The least-privilege bootstrap policies scope every resource ARN to the **parent stack's** name prefix (`backgroundagent-dev-*`). Resources created inside **nested** stacks do not carry that prefix in their generated physical names, so the CFN execution role is denied on all of them.

The Agent Registry is **default-on** (`enableAgentRegistry !== false`, `cdk/src/stacks/agent.ts:115-128`) and contributes two nested stacks — `AgentRegistryStack` and `RegistryApi`. So on a clean account with default context, `mise //cdk:deploy` **cannot succeed** with the repo's own least-privilege bootstrap.

This is not one missing action; it is a whole class of denials.

## Expected behavior

A default-context deploy (Agent Registry enabled) succeeds against the least-privilege bootstrap, with **no** `AdministratorAccess` attached to `cdk-hnb659fds-cfn-exec-role-*`.

## Current behavior

Two distinct denials, hit in sequence:

**1. `iam:PassRole` on the exec role itself.** Creating any nested stack makes CloudFormation pass the execution role to itself. No statement permits this:

```
User: arn:aws:sts:::assumed-role/cdk-hnb659fds-cfn-exec-role--us-east-1/AWSCloudFormation
is not authorized to perform: iam:PassRole on resource:
arn:aws:iam:::role/cdk-hnb659fds-cfn-exec-role--us-east-1
because no identity-based policy allows the iam:PassRole action
```

The existing `IAMPassRole` statement scopes to `arn:aws:iam::*:role/backgroundagent-dev-*` and conditions on `iam:PassedToService`, which does not cover this case.

**2. Resource-name mismatch inside nested stacks.** After granting the above, the next failure:

```
User: ...AWSCloudFormation is not authorized to perform: states:CreateStateMachine on resource:
arn:aws:states:us-east-1::stateMachine:AgentRegistryProviderwaiterstatemachineE27177B2-arZJxYWqV4wX
because no identity-based policy allows the states:CreateStateMachine action
```

The action **is** granted; the `StepFunctions` statement scopes it to `arn:aws:states:*:*:stateMachine:backgroundagent-dev-*`. The waiter state machine is named `AgentRegistryProviderwaiterstatemachine…` — no parent-stack prefix — so the ARN never matches.

The same prefix assumption appears across the generated policies (SNS, CloudFront, Lambda, IAM roles/policies …), so fixing only `states:*` would just surface the next resource type. Patching one action at a time is not viable — each cycle is an ~8-minute deploy.

## Reproduction steps

1. Fresh account; bootstrap with the least-privilege template (see #864 for the size workaround needed to do this at all).
2. Do **not** attach `AdministratorAccess` to the cfn-exec-role.
3. `export MISE_EXPERIMENTAL=1 && mise run //cdk:deploy -- --require-approval never` with default context.
4. Deploy reaches changeset creation, then fails on `iam:PassRole`. Grant that, redeploy, and it fails on `states:CreateStateMachine` inside the nested stack.

## Possible solution

1. **Allow the exec role to pass itself**, scoped to exactly that role ARN, for nested-stack creation.
2. **Stop assuming the parent-stack prefix.** Nested-stack physical names are not prefixed. Either:
- widen the affected `Resource` patterns to also admit nested-stack-generated names, or
- drive scoping off a stable tag/condition (e.g. `aws:ResourceTag/aws:cloudformation:stack-id`) rather than a name prefix, or
- give nested-stack constructs explicit physical names carrying the prefix, so the existing scoping holds.

The tag-condition route is the most robust — it stays genuinely least-privilege without depending on CDK's name generation, which is not a stable contract.

3. **Add coverage.** A synth/policy test asserting every resource ARN in the synthesized template (including nested templates) is authorized by the generated policies would catch this class before deploy. Note `cdk/test/bootstrap/synth-coverage.test.ts` already exists and did not catch it.

**Workaround used to get unblocked:** attached `AdministratorAccess` to the cfn-exec-role plus an inline `iam:PassRole` policy. That defeats the purpose of the custom bootstrap and should not be the documented path.

## Environment

- Node: v22.23.2 (mise) · mise: 2026.7.0 macos-arm64 · Region: us-east-1
- Bootstrap: `BootstrapVariant = "ABCA: Least-Privilege Bootstrap"`, version 32, `ComputeTypes=agentcore`
- Commit: `6e865f2d`
- Related: #120 (least-privilege bootstrap policies as code — this is a gap in that design), #350 (prior stale-policy AccessDenied), #864 (blocks reaching this step at all)

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Inizia da cdk/src/stacks/agent.ts:115-128 e cdk/test/bootstrap/synth-coverage.test.ts, quindi riproduci il deploy nel contesto predefinito usando il bootstrap con il minimo privilegio e senza AdministratorAccess. Traccia le policy generate e i template annidati per coprire il caso PassRole dell’exec role e i nomi delle risorse dei nested stack. Il lavoro è completato quando il deploy predefinito di Agent Registry riesce, il ruolo cfn-exec non dispone di AdministratorAccess e la copertura di synth impedisce le regressioni.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
aws, typescript
Ambito
cloud, devops, security
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.