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-cloudfront: cloudfront:ListDistributions does not support resource-level #33249

Open
1 task
steven10172 opened this issue Jan 31, 2025 · 1 comment
Open
1 task
Labels
@aws-cdk/aws-cloudfront Related to Amazon CloudFront bug This issue is a bug. effort/small Small work item – less than a day of effort p2

Comments

@steven10172
Copy link

Describe the bug

When using the grantXXX command on a resource it applies the actions against the ARN of the current resources. While this is often what you want, some AWS services and actions do not support resource-level protection and require the use of wildcards. For example, when specifying cloudfront:ListDistributions the resource must be * otherwise it will fail.

To assist users in this process the grantXXX function for CloudFront should be updated to use * when cloudfront:ListDistributions is present within the action list.

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html

Regression Issue

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

Last Known Working CDK Version

No response

Expected Behavior

When using the grantXXX function for CloudFront with cloudfront:ListDistributions the resource field should be *

Current Behavior

The resource field is the ARN of the distribution which fails.

Reproduction Steps

Create a test role that uses cloudfront:ListDistributions and set the resource to a specific distribution id and then run the list command, it will fail stating that permissions are lacking.

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.160.0

Framework Version

No response

Node.js Version

20.11.1

OS

Mac 13.7.2

Language

TypeScript

Language Version

No response

Other information

No response

@steven10172 steven10172 added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 31, 2025
@github-actions github-actions bot added the @aws-cdk/aws-cloudfront Related to Amazon CloudFront label Jan 31, 2025
@ashishdhingra ashishdhingra self-assigned this Jan 31, 2025
@ashishdhingra ashishdhingra added p2 investigating This issue is being investigated and/or work is in progress to resolve the issue. and removed needs-triage This issue or PR still needs to be triaged. labels Jan 31, 2025
@ashishdhingra
Copy link
Contributor

Reproducible using code below:
lib/cdktest-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'
import * as cloudfrontOrigins from 'aws-cdk-lib/aws-cloudfront-origins'
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as pythonlambda from '@aws-cdk/aws-lambda-python-alpha';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class CdktestStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const s3bucket = new s3.Bucket(this, "myBucket", {
      bucketName: `${cdk.Stack.of(this).stackName.toLowerCase()}-oacbucket`,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      accessControl: s3.BucketAccessControl.PRIVATE,
      enforceSSL: true,
    });

    const cloudfrontDistribution = new cloudfront.Distribution(this, 'TestDistribution', {
      defaultBehavior: {
        origin: cloudfrontOrigins.S3BucketOrigin.withOriginAccessControl(s3bucket)
      }
    });

    const role = new iam.Role(this, 'TestRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),

    });

    cloudfrontDistribution.grant(role, 'cloudfront:ListDistributions');

    new pythonlambda.PythonFunction(this, 'MyFunction', {
      entry: 'lib/lambda/python',
      runtime: lambda.Runtime.PYTHON_3_10,
      index: 'testlambda.py',
      handler: 'lambda_handler',
      role: role
    });
  }
}

lib/lambda/python/testlambda.py

import boto3
import botocore

def lambda_handler(event, context):
   print(f'boto3 version: {boto3.__version__}')
   print(f'botocore version: {botocore.__version__}')

   cloudfront_client = boto3.client('cloudfront')
   distributions = cloudfront_client.list_distributions()
   if distributions["DistributionList"]["Quantity"] > 0:
      print("No CloudFront distributions detected.")

bib/cdktest.ts

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';import { CdktestStack } from '../lib/cdktest-stack';

const app = new cdk.App();

const cdkteststack = new CdktestStack(app, 'CdktestStack', {
  env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});

const assembly = app.synth();

Running cdk synth produces below output:

[+] Building 0.4s (6/6) FINISHED                                                                                                                                                     docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                 0.0s
 => => transferring dockerfile: 1.32kB                                                                                                                                                               0.0s
 => [internal] load metadata for public.ecr.aws/sam/build-python3.10:latest                                                                                                                          0.3s
 => [internal] load .dockerignore                                                                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                                                                      0.0s
 => [1/2] FROM public.ecr.aws/sam/build-python3.10:latest@sha256:1aa2f601f7f2c779cdf375b99c89527e6b815b345fd86de94e09695372a4025e                                                                    0.0s
 => CACHED [2/2] RUN     python -m venv /usr/app/venv &&     mkdir /tmp/pip-cache &&     chmod -R 777 /tmp/pip-cache &&     pip install --upgrade pip &&     mkdir /tmp/poetry-cache &&     chmod -  0.0s
 => exporting to image                                                                                                                                                                               0.0s
 => => exporting layers                                                                                                                                                                              0.0s
 => => writing image sha256:64bea44ea734cb2bd2650422c85c8bdc45e8df62d9e7e4b1a8c5934dcf3f5ecc                                                                                                         0.0s
 => => naming to docker.io/library/cdk-b27f324a33692c8d8c25a4d3e00e2629ef13e1998e9a803da6bf32ddd15bb395                                                                                              0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/wv3wdqnc055b69f484ofs3tfk

What's next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview 
Resources:
  myBucket5AF9C99B:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName: cdkteststack-oacbucket
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: CdktestStack/myBucket/Resource
  myBucketPolicyDAC36B74:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: myBucket5AF9C99B
      PolicyDocument:
        Statement:
          - Action: s3:*
            Condition:
              Bool:
                aws:SecureTransport: "false"
            Effect: Deny
            Principal:
              AWS: "*"
            Resource:
              - Fn::GetAtt:
                  - myBucket5AF9C99B
                  - Arn
              - Fn::Join:
                  - ""
                  - - Fn::GetAtt:
                        - myBucket5AF9C99B
                        - Arn
                    - /*
          - Action: s3:GetObject
            Condition:
              StringEquals:
                AWS:SourceArn:
                  Fn::Join:
                    - ""
                    - - "arn:"
                      - Ref: AWS::Partition
                      - ":cloudfront::"
                      - Ref: AWS::AccountId
                      - :distribution/
                      - Ref: TestDistribution94EC811C
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Resource:
              Fn::Join:
                - ""
                - - Fn::GetAtt:
                      - myBucket5AF9C99B
                      - Arn
                  - /*
        Version: "2012-10-17"
    Metadata:
      aws:cdk:path: CdktestStack/myBucket/Policy/Resource
  TestDistributionOrigin1S3OriginAccessControl1514A8C2:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: CdktestStackTestDistributionOrigin1S3OriginAccessControlFFD7D062
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
    Metadata:
      aws:cdk:path: CdktestStack/TestDistribution/Origin1/S3OriginAccessControl/Resource
  TestDistribution94EC811C:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        DefaultCacheBehavior:
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
          Compress: true
          TargetOriginId: CdktestStackTestDistributionOrigin1403E98FC
          ViewerProtocolPolicy: allow-all
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: true
        Origins:
          - DomainName:
              Fn::GetAtt:
                - myBucket5AF9C99B
                - RegionalDomainName
            Id: CdktestStackTestDistributionOrigin1403E98FC
            OriginAccessControlId:
              Fn::GetAtt:
                - TestDistributionOrigin1S3OriginAccessControl1514A8C2
                - Id
            S3OriginConfig:
              OriginAccessIdentity: ""
    Metadata:
      aws:cdk:path: CdktestStack/TestDistribution/Resource
  TestRole6C9272DF:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: "2012-10-17"
    Metadata:
      aws:cdk:path: CdktestStack/TestRole/Resource
  TestRoleDefaultPolicyD1C92014:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action: cloudfront:ListDistributions
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - arn:aws:cloudfront::<<ACCOUNT-ID>>:distribution/
                  - Ref: TestDistribution94EC811C
        Version: "2012-10-17"
      PolicyName: TestRoleDefaultPolicyD1C92014
      Roles:
        - Ref: TestRole6C9272DF
    Metadata:
      aws:cdk:path: CdktestStack/TestRole/DefaultPolicy/Resource
  MyFunction3BAA72D1:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: cdk-hnb659fds-assets-<<ACCOUNT-ID>>-us-east-2
        S3Key: 627e659b62b97fcc847b5808af156a0e022e4eb45b041a94c3c502e5d9ffa0fa.zip
      Handler: testlambda.lambda_handler
      Role:
        Fn::GetAtt:
          - TestRole6C9272DF
          - Arn
      Runtime: python3.10
    DependsOn:
      - TestRoleDefaultPolicyD1C92014
      - TestRole6C9272DF
    Metadata:
      aws:cdk:path: CdktestStack/MyFunction/Resource
      aws:asset:path: asset.627e659b62b97fcc847b5808af156a0e022e4eb45b041a94c3c502e5d9ffa0fa
      aws:asset:is-bundled: true
      aws:asset:property: Code
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAA/3WQwW6DMAyGn4V7cGmZ1B3XMu26Ch6gCiFQl5BUcbKqinj3CRKJXXb6/NvOJysH2B+PEIqMPykX3ZgrbCE0jouR8SddA5UQzl6M0rGq16mKuBiF4rW1U47hzEnOTCjju94a7SB8IjmLrXdoNGvKb4sD6pMQkqgy2lmjFtU/7b+PZ4Z8glAbJZfRyu2WWM2Myisnko7gtIApPrUdh6rXX16L6KklGW+FZOtK4/iAelidaTCzIufqceNQZB/pi3YLoy1/vNzN6LRyWcNm16aTcKfdz/4dDgW8ZXdCzK3XDicJdeQvKcBcmIEBAAA=
    Metadata:
      aws:cdk:path: CdktestStack/CDKMetadata/Default
Parameters:
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /cdk-bootstrap/hnb659fds/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]

Deploying it creates the role with below policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "cloudfront:ListDistributions",
            "Resource": "arn:aws:cloudfront::<<ACCOUNT-ID>>:distribution/EC4J6JULS7OEV",
            "Effect": "Allow"
        }
    ]
}

NOTE: If we try to modify role permissions policy (let's say adding a space or adding other action) with the same Resource via AWS IAM console, it displays message This policy does not grant any permissions. To grant access, policies must have an action that has an applicable resource or condition.

Testing the Lambda function via AWS console gives the error:

{
  "errorMessage": "An error occurred (AccessDenied) when calling the ListDistributions operation: User: arn:aws:sts::<<ACCOUNT-ID>>:assumed-role/CdktestStack-TestRole6C9272DF-tWzuCaW9VFvW/CdktestStack-MyFunction3BAA72D1-kEMsifQVjuhZ is not authorized to perform: cloudfront:ListDistributions because no identity-based policy allows the cloudfront:ListDistributions action",
  "errorType": "AccessDenied",
  "requestId": "816fe7ad-eac7-4b3a-8999-49fe7448da3b",
  "stackTrace": [
    "  File \"/var/task/testlambda.py\", line 9, in lambda_handler\n    distributions = cloudfront_client.list_distributions()\n",
    "  File \"/var/runtime/botocore/client.py\", line 565, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
    "  File \"/var/runtime/botocore/client.py\", line 1021, in _make_api_call\n    raise error_class(parsed_response, operation_name)\n"
  ]
}

If we manually modify the role permissions policy to change the Resource to *, then Lambda function works fine.

IAM Policies do not allow restriction of access to specific CloudFront distributions. The work around is to use a wildcard for the resource, instead of only referencing a specific CloudFront resource. Also refer Actions, resources, and condition keys for Amazon CloudFront.

@ashishdhingra ashishdhingra added 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. labels Jan 31, 2025
@ashishdhingra ashishdhingra removed their assignment Jan 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-cloudfront Related to Amazon CloudFront bug This issue is a bug. effort/small Small work item – less than a day of effort p2
Projects
None yet
Development

No branches or pull requests

2 participants