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

(aws-s3): bucket.grantRead to an organization principal grants public read access #32756

Closed
1 task
ehiggins0 opened this issue Jan 6, 2025 · 9 comments · Fixed by #33555
Closed
1 task
Assignees
Labels
@aws-cdk/aws-s3 Related to Amazon S3 bug This issue is a bug. effort/small Small work item – less than a day of effort p1

Comments

@ehiggins0
Copy link

Describe the bug

When using bucket.grantRead(org), the generated policy allows access to the bucket for all AWS accounts without a condition.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Version

No response

Expected Behavior

The policy should have a condition:

  "bucketPolicy": {
   "Type": "AWS::S3::BucketPolicy",
   "Properties": {
    "Bucket": {
     "Ref": "bucket"
    },
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "s3:GetBucket*",
        "s3:GetObject*",
        "s3:List*"
       ],
       "Condition": {
           "StringEquals": {
               "aws:PrincipalOrgID": "o-yyyyyyyyyy"
           },
       },
       "Effect": "Allow",
       "Principal": {
        "AWS": "*"
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "bucket",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "bucket",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      }
     ],
     "Version": "2012-10-17"
    }
   },
   "Metadata": {
    "aws:cdk:path": "Stack/bucket/Policy/Resource"
   }
  }

Current Behavior

This policy gets generated:

  "bucketPolicy": {
   "Type": "AWS::S3::BucketPolicy",
   "Properties": {
    "Bucket": {
     "Ref": "bucket"
    },
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "s3:GetBucket*",
        "s3:GetObject*",
        "s3:List*"
       ],
       "Condition": {
        "StringEquals": {}
       },
       "Effect": "Allow",
       "Principal": {
        "AWS": "*"
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "bucket",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "bucket",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      }
     ],
     "Version": "2012-10-17"
    }
   },
   "Metadata": {
    "aws:cdk:path": "Stack/bucket/Policy/Resource"
   }
  }

Reproduction Steps

    const org = new iam.OrganizationPrincipal(orgName);
    const bucket = new s3.Bucket(this, "bucket", {...});
    bucket.grantRead(org);

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.150.0

Framework Version

No response

Node.js Version

18.18.2

OS

Ubuntu 24.04

Language

TypeScript

Language Version

No response

Other information

No response

@ehiggins0 ehiggins0 added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 6, 2025
@github-actions github-actions bot added the @aws-cdk/aws-s3 Related to Amazon S3 label Jan 6, 2025
@khushail khushail added needs-cfn This issue is waiting on changes to CloudFormation before it can be addressed. needs-reproduction This issue needs reproduction. p2 and removed needs-cfn This issue is waiting on changes to CloudFormation before it can be addressed. needs-triage This issue or PR still needs to be triaged. labels Jan 6, 2025
@khushail khushail self-assigned this Jan 6, 2025
@ehiggins0
Copy link
Author

Turns out while trying to come up with a workaround that this is not an issue with aws-s3, but is instead a silent failure when trying to add permissions to iam.OrganizationPrincipal(undefined). It still grants the "*" access, but the condition is not added due to the orgId being undefined. Not sure if this should be addressed as it could cause security issues to deployed S3 resources, but feel free to close if not.

@khushail khushail added investigating This issue is being investigated and/or work is in progress to resolve the issue. and removed needs-reproduction This issue needs reproduction. labels Jan 10, 2025
@khushail
Copy link
Contributor

@ehiggins0 , thanks for reporting this. I see that the implementation of iam.OrgranizationPrincipal -

export class OrganizationPrincipal extends PrincipalBase {

and this is how grant() is implemented invoked by grantRead(..)

public grantRead(identity: iam.IGrantable, objectsKeyPattern: any = '*') {

public dedupeString(): string | undefined {

Looks like there should be a check added before granting access to * in case of organization principal.

@khushail khushail added p1 effort/small Small work item – less than a day of effort and removed investigating This issue is being investigated and/or work is in progress to resolve the issue. p2 labels Jan 10, 2025
@IkeNefcy
Copy link
Contributor

I'm not able to reproduce this.
I'm using

const org = new OrganizationPrincipal('o-xxxxxxxxxxxx');
const bucket = new Bucket(this, 'bucket', {});
bucket.grantRead(org);

and it's working fine. I'm using 2.178.2.
Perhaps something changed since this issue was opened? @khushail are you able to reconfirm if you can reproduce this?
I understand your deep dive and why it may lead to an issue, but I'm not understanding how iam.OrganizationPrincipal(undefined) is possible, I get a type error if I don't include a string here, and following the steps from OrganizationPrincipal -> iam.IGrantable -> grantRead -> grant I see no where that dedupeString is being used, and this is not used in the example anywhere either.. so not sure if this is related.

@khushail
Copy link
Contributor

Thanks @IkeNefcy for pointing this out.

I tried to repro the issue with this sample code -

    const org = new OrganizationPrincipal('');
    const bucket = new s3.Bucket(this, 'bucket', {});
    bucket.grantRead(org);

and got this generated template -

"bucketPolicy638F945D": {
   "Type": "AWS::S3::BucketPolicy",
   "Properties": {
    "Bucket": {
     "Ref": "bucket43879C71"
    },
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "s3:GetBucket*",
        "s3:GetObject*",
        "s3:List*"
       ],
       "Condition": {
        "StringEquals": {
         "aws:PrincipalOrgID": ""
        }
       },
       "Effect": "Allow",
       "Principal": {
        "AWS": "*"
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "bucket43879C71",
          "Arn"
         ]
        },

Though You are right in saying that undefined can't be given and would not be possible.

@khushail khushail added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Feb 21, 2025
@IkeNefcy
Copy link
Contributor

Copy, yeah in the OG example there is no condition key at all

      "Condition": {
        "StringEquals": {}
       },

where your current is like what mine does

       "Condition": {
        "StringEquals": {
         "aws:PrincipalOrgID": ""
        }
       },

I can add behavior to check that the organization name is not blank, and that should help with specifically the "" condition. But otherwise I think the Condition statement itself is working okay

@IkeNefcy
Copy link
Contributor

@khushail
I think the best change here would simply be to add the expected regex that organization IDs are allowed to be, it should solve since there is a 10 character minimum
https://docs.aws.amazon.com/organizations/latest/APIReference/API_Organization.html#API_Organization_Contents

Pattern; ^o-[a-z0-9]{10,32}$

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Feb 22, 2025
@khushail
Copy link
Contributor

Thanks @IkeNefcy for submitting a PR. Appreciate your efforts.

@khushail khushail removed their assignment Feb 24, 2025
@IkeNefcy
Copy link
Contributor

For sure!

mergify bot pushed a commit that referenced this issue Feb 26, 2025
### Issue #

Closes #32756


### Reason for this change

The original issue was related to over permissive s3 permissions. Which originally was being caused by what seems to be something related to an undefined `iam.OrgranizationPrincipal` being allowed. However when using 2.178.2, I'm not seeing this particular issue, but the policy that is generated could still be incorrectly created by leaving a blank string. 
`iam.OrgranizationPrincipal('')`
This can be avoided with a simple check. Although this is not a golden solution since it's not able to check if that organization exists, but for the use case it's better than nothing. 


### Description of changes

Adding a regex check that matches the Organization ID regex pattern in the docs; 
https://docs.aws.amazon.com/organizations/latest/APIReference/API_Organization.html

```
    if (!organizationId.match(/^o-[a-z0-9]{10,32}$/)) {
      throw new Error(`Expected Organization ID must match regex pattern ^o-[a-z0-9]{10,32}$, received ${organizationId}`);
    }
```


### Description of how you validated changes

Added a test for bad names 

```
test('throw error when Organization ID does not match regex pattern', () => {
  // GIVEN
  const shortOrgId = 'o-shortname';
  const noOOrgName = 'no-o-name';
  const longOrgName = 'o-thisnameistoooooooooooooooooolong';

  // THEN
  expect(() => new iam.OrganizationPrincipal(shortOrgId)).toThrow(`Expected Organization ID must match regex pattern ^o-[a-z0-9]{10,32}$, received ${shortOrgId}`);
  expect(() => new iam.OrganizationPrincipal(noOOrgName)).toThrow(`Expected Organization ID must match regex pattern ^o-[a-z0-9]{10,32}$, received ${noOOrgName}`);
  expect(() => new iam.OrganizationPrincipal(longOrgName)).toThrow(`Expected Organization ID must match regex pattern ^o-[a-z0-9]{10,32}$, received ${longOrgName}`);
});
```

### Checklist
- [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

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

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 26, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
@aws-cdk/aws-s3 Related to Amazon S3 bug This issue is a bug. effort/small Small work item – less than a day of effort p1
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants