Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(iam): no trust policy added for cross-account roles #5812

Closed
wants to merge 1 commit into from

Conversation

rix0rrr
Copy link
Contributor

@rix0rrr rix0rrr commented Jan 15, 2020

Trust policies should be added to resources when trying to grant
permissions to a principal in a different account. Typically this is a
Service Principal, but it could also be an imported Role from a
different account (and the same should not be done for a Role in the
same account).

Our previous API (addToPolicy returning true|false) was designed for
in-account Roles and cross-account ServicePrincipals, but was not rich
enough to make the distinction that Roles could be cross-account.

Add a function to IPrincipal to ask it whether it is in the same or a
different account from a given scope, and have Grant respect that.

Also in this commit: a bugfix for ImmutableRole to make it work
correctly with Grant

Also in this commit: parseArn() used to behave the same for
parseArn(TOKEN) and parseArn("arn:{TOKEN}:svc:1234:...").
Make it (opportunistically) do the right thing in the latter case,
parsing out the concrete components and the TOKEN components
separately. This makes it possible to parse out the concrete
account number from an ARN that contains tokenized parts we don't
care about.

Fixes #5740, #2807.


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

Trust policies should be added to resources when trying to grant
permissions to a principal in a different account.  Typically this is a
Service Principal, but it could also be an imported Role from a
different account (and the same should not be done for a Role in the
same account).

Our previous API (`addToPolicy` returning `true|false`) was designed for
in-account Roles and cross-account ServicePrincipals, but was not rich
enough to make the distinction that Roles could be cross-account.

Add a function to `IPrincipal` to ask it whether it is in the same or a
different account from a given scope, and have `Grant` respect that.

Also in this commit: a bugfix for `ImmutableRole` to make it work
correctly with `Grant`

Also in this commit: `parseArn()` used to behave the same for
`parseArn(TOKEN)` and `parseArn("arn:{TOKEN}:svc:1234:...")`.
Make it (opportunistically) do the right thing in the latter case,
parsing out the concrete components and the TOKEN components
separately. This makes it possible to parse out the concrete
account number from an ARN that contains tokenized parts we don't
care about.

Fixes #5740.
@rix0rrr rix0rrr self-assigned this Jan 15, 2020
@rix0rrr
Copy link
Contributor Author

rix0rrr commented Jan 15, 2020

This change might be contentious, in the addition of sameAccount()? and the behavior change of parseArn() (both of which I feel are defensible but also a reasonable person might disagree with the choices made).

Would appreciate some advice here.

Also see the set of test cases for coverage.

@rix0rrr rix0rrr requested a review from nija-at January 15, 2020 16:20
@mergify mergify bot added the contribution/core This is a PR that came from AWS. label Jan 15, 2020
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@skinny85 skinny85 added the pr/do-not-merge This PR should not be merged at this time. label Jan 15, 2020
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

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

I am OK with this.

Adding the 'do-not-merge' label so that others can chime in as well.

@@ -341,7 +341,8 @@ describe('IAM polocy document', () => {
get grantPrincipal() { return this; },
assumeRoleAction: 'sts:AssumeRole',
policyFragment: new PrincipalPolicyFragment({ AWS: ['foo', 'bar'] }),
addToPolicy() { return false; }
addToPolicy() { return false; },
sameAccount() { return false; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Trailing comma, to avoid the diff problem in the future? :)

Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

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

I've actually changed my mind on this, and I want to explore an alternative solution. I think we should develop something here that's usable across all construct libraries, not specific to IAM. We have a related issue about this around the necessity of having access to account and region on a resource level, instead of just stack level, but I can't find it :/.

I didn't have time yet to dive deep into this issue, but I plan on doing it.

@rix0rrr are you OK not merging this for the time being, and giving me some time to work on it?

@rix0rrr
Copy link
Contributor Author

rix0rrr commented Apr 9, 2020

I've actually changed my mind on this, and I want to explore an alternative solution. I think we should develop something here that's usable across all construct libraries, not specific to IAM.

I'm always in favor of better solutions for anything. As long as we can solve the same issue as mentioned here, go ahead.

We have a related issue about this around the necessity of having access to account and region on a resource level, instead of just stack level, but I can't find it :/.

I guess because of the nature of our deployment mechanism:

  • Environment of "owned" resources == environment of containing Stack
  • Environment of "imported" resources == ¯\(ツ)/¯ lol can be anything look at the ARN

That seems to be the crux of it, and you're right it holds for all imported resources, not just roles.

@rix0rrr are you OK not merging this for the time being, and giving me some time to work on it?

As long as you incorporate the tests of this PR into whatever you come up with, sure!

@@ -77,5 +77,5 @@ export class ScopedAws {
}

function pseudoString(name: string): string {
return Token.asString({ Ref: name }, { displayHint: name });
return Token.asString({ Ref: name }, { displayHint: name.replace('::', '.') });
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this needed?

* Returns undefined if one is agnostic and the other one isn't.
*/
export function sameAccount(account1: string | undefined, account2: string | undefined): boolean | undefined {
// Both agnostic in 99% of cases means they will be deployed to the same environment,
Copy link
Contributor

Choose a reason for hiding this comment

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

Mmmmmokay...

// so treat as the same.
if (Token.isUnresolved(account1) && Token.isUnresolved(account2)) { return true; }

// One agnostic and the other one not means "shug".
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// One agnostic and the other one not means "shug".
// One agnostic and the other one not means "shrug".

@@ -43,4 +43,8 @@ export class UnknownPrincipal implements IPrincipal {
this.resource.node.addWarning(`Add statement to this resource's role: ${repr}`);
return true; // Pretend we did the work. The human will do it for us, eventually.
}

public sameAccount(_scope: IConstruct): boolean | undefined {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe return an enum instead of using undefined as a tri-state so this will be easier to reason about and avoid mistakes like (!sameAccount())).

*
* Returns `undefined` if it is unknown whether the accounts are the same.
*/
sameAccount(scope: cdk.IConstruct): boolean | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. I feel that this API should accept a cdk.Stack and not a cdk.IConstruct. It will make this API much clearer.
  2. Would it make sense to make this _internal? It's used only inside this module and TBH it feels like it's polluting the interface.

@skinny85
Copy link
Contributor

I've actually changed my mind on this, and I want to explore an alternative solution. I think we should develop something here that's usable across all construct libraries, not specific to IAM.

I'm always in favor of better solutions for anything. As long as we can solve the same issue as mentioned here, go ahead.

We have a related issue about this around the necessity of having access to account and region on a resource level, instead of just stack level, but I can't find it :/.

I guess because of the nature of our deployment mechanism:

  • Environment of "owned" resources == environment of containing Stack
  • Environment of "imported" resources == ¯_(ツ)_/¯ lol can be anything look at the ARN

That seems to be the crux of it, and you're right it holds for all imported resources, not just roles.

@rix0rrr are you OK not merging this for the time being, and giving me some time to work on it?

As long as you incorporate the tests of this PR into whatever you come up with, sure!

@rix0rrr sorry this took so long, but this was a tricky one! I've opened #8280 with my proposed solution. It includes your tests from this PR.

I would appreciate if you could take a look, and let me know what you think!

@rix0rrr rix0rrr closed this Jul 9, 2020
@rix0rrr rix0rrr deleted the huijbers/import-role-policy branch July 9, 2020 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution/core This is a PR that came from AWS. pr/do-not-merge This PR should not be merged at this time.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

S3 Bucket grant* stopped working in 1.20.0
4 participants