Skip to content

fix(iam): use tags for granting cross-account access of KMS & S3#38254

Open
faridnsh wants to merge 1 commit into
aws:mainfrom
faridnsh:fix/kms-cross-account-grant-principal-tag
Open

fix(iam): use tags for granting cross-account access of KMS & S3#38254
faridnsh wants to merge 1 commit into
aws:mainfrom
faridnsh:fix/kms-cross-account-grant-principal-tag

Conversation

@faridnsh

@faridnsh faridnsh commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Issues

Closes #24050
Closes #24051

I am not sure if @rix0rrr finds this fix to be good enough for those issues he created.

Reason for this change

When a bucket or key grant targets a CDK-owned principal in another account, the resource policy references the role by its ARN. S3 and KMS validate that every principal in a resource policy exists at the time the policy is deployed, so unless the principal's stack happens to deploy first, the first deployment fails with "Invalid principal in policy" (S3) or "Policy contains a statement with one or more invalid principals" (KMS). The most common way to hit this is a cross-account + cross-region CodePipeline: the action role lives in a support stack with no deploy-ordering guarantee relative to the bucket/key stack, so there is no deploy order that works on the first attempt.

ecr.Repository.grant() already solves this exact problem: when the grantee is a CDK-owned resource in another account and the resource's stack does not depend on the grantee's stack, tag the grantee (aws-cdk:id) and trust the grantee's account root scoped by an aws:PrincipalTag condition instead of the bare role ARN. Account-root principals and condition values are not existence-checked, so deploy order no longer matters, while the tag keeps the trust scoped to the single intended role.

Description of changes

This change extracts the ECR implementation into a shared private helper (aws-iam/lib/private/cross-account-grants.ts) and applies it to S3 bucket grants and KMS key grants. ECR is refactored to use the helper with no behavior change. Imported principals, same-account grants, grants where the resource stack already depends on the principal stack, and the KMS trustAccountIdentities are not affected.

The @aws-cdk/aws-iam:crossAccountGrantsViaPrincipalTag flag gates the S3 and KMS behavior and is off by default for existing apps. It acknowledges that tag-scoped account-root trust survives role recreation and can be spoofed in-account by principals allowed to tag roles.

Downsides and alternatives!

Here's some discussion points and things to consider before merging this.

  1. When an IAM role is recreated, the policy that referenced it as principal doesn't trust the new role. This is a security feature that is being removed here from KMS and S3 grants behind a flag that's going to be default for new projects.
  2. We can also fix this by adding a dependency to ensure stack deployment orders. However, CDK stack dependencies are only useful for users that use CDK deploy or CDK pipelines. Also adding dependenices blindly could result in risk of producing a cyclic dependency. Alternative here is that we could (lazily) attempt to set dependency between stacks, if it resulted in a cyclic depdency then we can do this tag based.
  3. The flag only controls the behavior of S3 and KMS but not ECR. It might be a bit confusing for consumers. Also should we add this to other resources that support resource based policies in the future or should we do it now? If we do it in the future will they get a new flag? Let me know if we should do this for other resources in this PR.
  4. Another option we here is to allow this behaivior of tags based conditions for princpal vs dependency as part of the grant options and the user could determine which is the better way. It's a bug in codepipeline when CDK deploy or CDK pipelines are used so feature flag could be limited to that use case and other use cases could use .grant options to specify the behaviour.
  5. aws-cdk:id depends on the logical path of the resource. A refactor of the path results in it being different and when one stack is deployed, until the other stack is deployed the permission will not be there. For example I had implicitly given Codebuild cross stack access to an ECR repo, same account, different region via this aws-cdk:id condition automatically by CDK. I deployed the codebuild stack after a refactor and until the ECR stack was deployed the codebuild couldn't push. Not a big deal in my case, but some cases may not want interruptions. Another option is to use PrincipalArn as condition as mentioned in the comment in codepipeline: x-account AND x-region deployments are missing stack dependencies #24050 (comment) . If the IAM role name is not generated and doesn't depend on the path it wouldn't be affected by a refactor and no downtime. Anyway, I went with aws-cdk:id to match the ECR behaviour and be consistent. But a new better behaviour is possible with a flag.

Describe any new or updated permissions being added

S3 and KMS cross-stack grants when there's no dependency with the new flag make the statement to set the root IAM account as principal and add conditions to make sure we are only giving access to the particular IAM role.

Description of how you validated changes

Unit test added, but I also tested it by deploying a cross-account CodePipeline that was failing to deploy before the fix, and now it works.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@github-actions github-actions Bot added beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. effort/large Large work item – several weeks of effort p2 labels Jul 5, 2026
@aws-cdk-automation
aws-cdk-automation requested a review from a team July 5, 2026 11:49

@aws-cdk-automation aws-cdk-automation left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This review is outdated)

@aws-cdk-automation aws-cdk-automation added the pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. label Jul 5, 2026
@faridnsh
faridnsh force-pushed the fix/kms-cross-account-grant-principal-tag branch from fe17b2c to 1a166d2 Compare July 6, 2026 09:17
@faridnsh faridnsh changed the title fix(s3,kms): use tags for granting cross-account fix(iam): use tags for granting cross-account Jul 6, 2026
@faridnsh faridnsh changed the title fix(iam): use tags for granting cross-account fix(iam): use tags for granting cross-account access of KMS & S3 Jul 6, 2026
@aws-cdk-automation
aws-cdk-automation dismissed their stale review July 6, 2026 09:19

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Jul 6, 2026
When a bucket or key grant targets a CDK-owned principal in another
account, the resource policy referenced the role by its ARN. S3 and KMS
validate that every principal in a resource policy exists at the time
the policy is deployed, so unless the principal's stack happens to
deploy first, the first deployment fails with "Invalid principal in
policy" (S3) or "Policy contains a statement with one or more invalid
principals" (KMS). The most common way to hit this is a cross-account +
cross-region CodePipeline: the action role lives in a support stack
with no deploy-ordering guarantee relative to the bucket/key stack, so
there is no deploy order that works on the first attempt.

ecr.Repository.grant() already solves this exact problem: when the
grantee is a CDK-owned resource in another account and the resource's
stack does not depend on the grantee's stack, tag the grantee
(aws-cdk:id) and trust the grantee's account root scoped by an
aws:PrincipalTag condition instead of the bare role ARN. Account-root
principals and condition values are not existence-checked, so deploy
order no longer matters, while the tag keeps the trust scoped to the
single intended role.

This change extracts the ECR implementation into a shared private
helper (aws-iam/lib/private/cross-account-grants.ts) and applies it to
S3 bucket grants and KMS key grants. ECR is refactored to use the
helper with no behavior change. Imported principals, same-account
grants, grants where the resource stack already depends on the principal
stack, and the KMS trustAccountIdentities are not affected.

The @aws-cdk/aws-iam:crossAccountGrantsViaPrincipalTag flag gates the S3
and KMS behavior and is off by default for existing apps. It acknowledges
that tag-scoped account-root trust survives role recreation and can be
spoofed in-account by principals allowed to tag roles.
@faridnsh
faridnsh force-pushed the fix/kms-cross-account-grant-principal-tag branch from 1a166d2 to 63c80dc Compare July 6, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. effort/large Large work item – several weeks of effort p2 pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

codepipeline: x-account AND x-region deployments are a mess codepipeline: x-account AND x-region deployments are missing stack dependencies

2 participants