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

feat(cloudfront): vpc origins #33318

Merged
merged 27 commits into from
Feb 18, 2025
Merged

feat(cloudfront): vpc origins #33318

merged 27 commits into from
Feb 18, 2025

Conversation

Tietew
Copy link
Contributor

@Tietew Tietew commented Feb 6, 2025

Issue # (if applicable)

Closes #32396.

Reason for this change

VPC origins has been added to CloudFront and now CloudFormation supports it.
For details, see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-vpc-origins.html

Description of changes

Added an L2 construct cloudfront.VpcOrigin for AWS::CloudFront::VpcOrigin.
It will be created implicitly by origin class described below.
You can create it explicitly to share VPC origins between distributions.

import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';

// Create a VPC origin resource
const vpcOrigin = new cloudfront.VpcOrigin(this, 'VpcOrigin', {
  // An EC2 instance endpoint
  endpoint: cloudfront.VpcOriginEndpoint.fromEc2Instance(instance),
  // An Application Load Balancer endpoint
  endpoint: cloudfront.VpcOriginEndpoint.fromApplicationLoadBalancer(alb),
  // A Network Load Balancer endpoint
  endpoint: cloudfront.VpcOriginEndpoint.fromNetoworkLoadBalancer(nlb),
  // Endpoint from ARN, i.e. imported resource
  endpoint: new cloudfront.VpcOriginEndpoint({ endpointArn }),
  // Optional VPC origin resource configurations
  vpcOriginName: 'Name of the VPC origin',
  httpPort: 80,
  httpsPort: 443,
  protocolPolicy: cloudfront.OriginProtocolPolicy.MATCH_VIEWER,
  originSslProtocols: [cloudfront.OriginSslPolicy.TLSV1_2],
});

Added an origin class cloudfront_origins.VpcOrigin for distribution configuration.
It can be configured with an Application Load Balancer, a Network Load Balancer, an EC2 instance, or a cloudfront.VpcOrigin construct.

import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';

// An EC2 instance as a VPC origin
const ec2InstanceOrigin = origins.VpcOrigin.withEc2Instance(instance, {
  // Optional VPC origin configurations
  domainName: 'internal.example.com', // default: PrivateDnsName of the instance
  readTimeout: cdk.Duration.seconds(30),
  keepaliveTimeout: cdk.Duration.seconds(5),
  // Optional VPC origin resource configurations
  vpcOriginName: 'Name of the VPC origin',
  httpPort: 80,
  httpsPort: 443,
  protocolPolicy: cloudfront.OriginProtocolPolicy.MATCH_VIEWER,
  originSslProtocols: [cloudfront.OriginSslPolicy.TLSV1_2],
  // Optional origin common configurations
  connectionTimeout: Duration.seconds(10),
  connectionAttempts: 3,
  customHeaders: {},
  originShieldRegion: 'region-name',
  originShieldEnabled: true,
  originId: 'origin-id',
});

// An Application Load Balancer as a VPC origin
const albOrigin = origins.VpcOrigin.withApplicationLoadBalancer(alb, {
  // Optional VPC origin configurations
  domainName: 'internal.example.com', // default: DNSName of the ALB
  readTimeout: cdk.Duration.seconds(30),
  keepaliveTimeout: cdk.Duration.seconds(5),
  // Optional VPC origin resource configurations
  // Optional origin common configurations
});

// A Network Load Balancer as a VPC origin
const nlbOrigin = origins.VpcOrigin.withNetworkLoadBalancer(nlb, {
  // Optional VPC origin configurations
  domainName: 'internal.example.com', // default: DNSName  of the NLB
  readTimeout: cdk.Duration.seconds(30),
  keepaliveTimeout: cdk.Duration.seconds(5),
  // Optional VPC origin resource configurations
  // Optional origin common configurations
});

// Use an explicit VPC origin resource
const vpcOriginOrigin = origins.VpcOrigin.withVpcOrigin(vpcOrigin, {
  // Mandatory if the vpcOrigin is created without domainName
  domainName: 'internal.example.com',
  // Optional VPC origin configurations
  readTimeout: cdk.Duration.seconds(30),
  keepaliveTimeout: cdk.Duration.seconds(5),
  // Optional origin common configurations
});

Describe any new or updated permissions being added

No permissions are added automatically.
See README how to allow connections from VPC origins.

Description of how you validated changes

Unit tests and integ tests.

Checklist


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

@aws-cdk-automation aws-cdk-automation requested a review from a team February 6, 2025 13:55
@github-actions github-actions bot added effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p2 admired-contributor [Pilot] contributed between 13-24 PRs to the CDK labels Feb 6, 2025
httpsPort: props.httpsPort,
name: props.vpcOriginName ?? Names.uniqueResourceName(this, {}),
originProtocolPolicy: props.protocolPolicy,
originSslProtocols: props.originSslProtocols ?? [OriginSslPolicy.TLS_V1_2],
Copy link
Contributor Author

@Tietew Tietew Feb 6, 2025

Choose a reason for hiding this comment

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

The default value of originSslProtocols is ['SSLv3', 'TLSv1'].
This explicit default ['TLSv1.2'] is same as the AWS management console.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For details, see #33318 (comment)

Copy link

codecov bot commented Feb 6, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.16%. Comparing base (a2cd7ae) to head (a92b453).
Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #33318   +/-   ##
=======================================
  Coverage   82.16%   82.16%           
=======================================
  Files         119      119           
  Lines        6857     6857           
  Branches     1157     1157           
=======================================
  Hits         5634     5634           
  Misses       1120     1120           
  Partials      103      103           
Flag Coverage Δ
suite.unit 82.16% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
packages/aws-cdk ∅ <ø> (∅)
packages/aws-cdk-lib/core 82.16% <ø> (ø)

@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 Feb 7, 2025
Copy link
Collaborator

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

Choose a reason for hiding this comment

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

(This review is outdated)

Copy link
Collaborator

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

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 dismissed their stale review February 7, 2025 16:08

Dismissing outdated PRLinter review.

@aws-cdk-automation aws-cdk-automation removed the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Feb 7, 2025
@aws-cdk-automation aws-cdk-automation dismissed their stale review February 7, 2025 16:33

✅ 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 Feb 7, 2025
@SimonCMoore SimonCMoore added the pr/needs-maintainer-review This PR needs a review from a Core Team Member label Feb 14, 2025
@aws-cdk-automation aws-cdk-automation removed pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. pr/needs-maintainer-review This PR needs a review from a Core Team Member labels Feb 14, 2025
@GavinZZ GavinZZ self-assigned this Feb 14, 2025
Copy link
Contributor

@GavinZZ GavinZZ left a comment

Choose a reason for hiding this comment

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

I apologize in advance for not being too familiar with CloudFront and CloudFront-Origin modules.

Can you explain to me the difference between the two VpcOrigins (one of cloudfront module and one in cloudfront-origin module). It's very confusing for me and I see that in the added unit test of cloudfront-origins,

const instance = new ec2.Instance(stack, 'Instance', {
    vpc,
    instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE4_GRAVITON, ec2.InstanceSize.MICRO),
    machineImage: ec2.MachineImage.latestAmazonLinux2023(),
  });

  // WHEN
  new cloudfront.Distribution(stack, 'Distribution', {
    defaultBehavior: {
      origin: VpcOrigin.withEc2Instance(instance),
    },
  });

This will create a AWS resource called AWS::CloudFront::VpcOrigin but i don't see how it's being created as VpcOrigin from cloudfront-origin module doesn't create the L1 resource.

httpsPort: props.httpsPort,
name: props.vpcOriginName ?? Names.uniqueResourceName(this, { maxLength: 64 }),
originProtocolPolicy: props.protocolPolicy,
originSslProtocols: props.originSslProtocols ?? [OriginSslPolicy.TLS_V1_2],
Copy link
Contributor

Choose a reason for hiding this comment

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

Which source did you see the default value? I don't see it in the CloudFormation documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created a simple CfnVpcOrigin resource:

export class VpcOrigin extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });
    const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', { vpc });

    new cloudfront.CfnVpcOrigin(this, 'VpcOrigin', {
      vpcOriginEndpointConfig: { name: 'ALBVpcOrigin', arn: alb.loadBalancerArn },
    });
  }
}

Created VPC origin in AWS management console shows the default values:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I create a VPC origin in AWS management console, the default of SSL protocol is TLSv1.2.
image


this.vpcOriginArn = this.getResourceArnAttribute(resource.attrArn, {
service: 'cloudfront',
region: '',
Copy link
Contributor

Choose a reason for hiding this comment

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

is vpc origin arn region agnostic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.
See https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazoncloudfront.html#amazoncloudfront-resources-for-iam-policies

Resource types defined by Amazon CloudFront

Resource types ARN
vpcorigin arn:${Partition}:cloudfront::${Account}:vpcorigin/${Id}

}
}

class VpcOriginWithVpcOrigin extends VpcOrigin {
Copy link
Contributor

Choose a reason for hiding this comment

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

This name is very confusing

Copy link
Contributor Author

@Tietew Tietew Feb 17, 2025

Choose a reason for hiding this comment

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

Please see my comment at #33318 (comment)

machineImage: ec2.MachineImage.latestAmazonLinux2023(),
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});
new cloudfront.VpcOrigin(stack, 'VpcOriginFromInstance', {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of this call?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This integ test verifies AWS::CloudFront::VpcOrigin resources are deployed with no errors.
The VpcOrigin resources are not attached to any CloudFront distributions in this test.

}
}

class VpcOriginWithEndpoint extends VpcOrigin {
Copy link
Contributor

Choose a reason for hiding this comment

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

what's the use of this class if it's not exported?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similar to S3BucketOriginWithOAC and S3BucketOriginWithOAI.
VpcOriginWith**** is used internally by VpcOrigin.with*** static methods.

S3BucketOrigin.withOriginAccessControl static method creates S3BucketOriginWithOAC.

public static withOriginAccessControl(bucket: IBucket, props?: S3BucketOriginWithOACProps): cloudfront.IOrigin {
return new S3BucketOriginWithOAC(bucket, props);
}

S3BucketOriginWithOAC is not exported.
class S3BucketOriginWithOAC extends S3BucketOrigin {

@aws-cdk-automation
Copy link
Collaborator

This PR cannot be merged because it has conflicts. Please resolve them. The PR will be considered stale and closed if it remains in an unmergeable state.

@mergify mergify bot dismissed GavinZZ’s stale review February 17, 2025 02:23

Pull request has been modified.

@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 Feb 17, 2025
@Tietew
Copy link
Contributor Author

Tietew commented Feb 17, 2025

@GavinZZ Thank you for your review.

Can you explain to me the difference between the two VpcOrigins

cloudfront.VpcOrigin

This is the L2 construct represents AWS::CloudFront::VpcOrigin resource (CfnVpcOrigin).
The class name VpcOrigin is derived from the resource type.

This resource is very simliar to OriginAccessControl and OriginAccessIdentity.

  • S3BucketOrigin will create OriginAccessControl internally by default.
  • Users can create a OriginAccessControl resource explicitly and pass it to S3BucketOrigin.

cloudfront_origins.VpcOrigin

This is the helper class represents VPC origin similar to S3BucketOrigin class.
I agree the names of classes are very confusing...

  • The Origin connects to S3 Bucket - S3BucketOrigin
  • The Origin connects to VpcOrigin - VpcOrigin or VpcOriginOrigin ? (also confusing)

Instead, we cloud create separated origin classes:

  • The Origin connects to ALB as VPC origin - VpcApplicationLoadBalancerOrigin
  • The Origin connects to NLB as VPC origin - VpcNetworkLoadBalancerOrigin
  • The Origin connects to EC2 as VPC origin - VpcEc2InstanceOrigin
  • The Origin connects to any resource represented by VpcOrigin resource - ???

Example1 - simple usage:

declare const alb: elbv2.ApplicationLoadBalancer;

new cloudfront.Distribution(this, 'Distribution', {
  defaultBehavior: {
    origin: new origins.VpcApplicationLoadBalancerOrigin(alb),
  }
});

Example2 - create explicit VpcOrigin resource:

declare const alb: elbv2.ApplicationLoadBalancer;

const vpcOrigin = new cloudfront.VpcOrigin(this, 'VpcOrigin', {
  endpoint: cloudfront.VpcOrigin.applicationLoadBalancer(alb),
});
new cloudfront.Distribution(this, 'Distribution', {
  defaultBehavior: {
    origin: new origins.XxxxxxxxxOrigin(vpcOrigin), // How to name the class?
  }
});

@Tietew
Copy link
Contributor Author

Tietew commented Feb 17, 2025

This will create a AWS resource called AWS::CloudFront::VpcOrigin but i don't see how it's being created as VpcOrigin from cloudfront-origin module doesn't create the L1 resource.

L1 resource is created here:

class VpcOriginWithEndpoint extends VpcOrigin {
  // ...
  public bind(_scope: Construct, options: cloudfront.OriginBindOptions): cloudfront.OriginBindConfig {
    this.vpcOrigin ??= new cloudfront.VpcOrigin(_scope, 'VpcOrigin', {
      endpoint: this.vpcOriginEndpoint,
      vpcOriginName: this.props.vpcOriginName,
      httpPort: this.props.httpPort,
      httpsPort: this.props.httpsPort,
      protocolPolicy: this.props.protocolPolicy,
      originSslProtocols: this.props.originSslProtocols,
    });
    return super.bind(_scope, options);
  }
}

Similar to how S3BucketOrigin creates OriginAccessControl resource.

public bind(scope: Construct, options: cloudfront.OriginBindOptions): cloudfront.OriginBindConfig {
if (!this.originAccessControl) {
this.originAccessControl = new cloudfront.S3OriginAccessControl(scope, 'S3OriginAccessControl');
}


// Call ec2:DescribeSecurityGroups API to retrieve the VPC origins security group.
const getSg = new cr.AwsCustomResource(this, 'GetSecurityGroup', {
onCreate: {

Choose a reason for hiding this comment

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

Should the docs also provide an onUpdate and onDelete?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

onUpdate is needed when the vpc id is changed but unusual.
onDelete is not needed because this custom resource creates no resources.
The security group will be deleted by CloudFront after all VPC origins (in the VPC) are deleted.

Choose a reason for hiding this comment

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

Ah okay, my bad. I thought the onDelete would be triggered when the origin would be deleted, therefore it would have to detach itself from the security group and therefore would need the security group ids and invoke this custom resource again.

Tietew and others added 2 commits February 17, 2025 18:41
Co-authored-by: Gijs Martens <gijs.martens@coolblue.nl>
Co-authored-by: Gijs Martens <gijs.martens@coolblue.nl>
Copy link
Contributor

@GavinZZ GavinZZ left a comment

Choose a reason for hiding this comment

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

@Tietew thanks for the detailed answer to my questions. That's really helpful to me to understand the changes. LGTM.

Copy link
Contributor

mergify bot commented Feb 18, 2025

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot merged commit 8f3e793 into aws:main Feb 18, 2025
20 checks passed
Copy link
Contributor

mergify bot commented Feb 18, 2025

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: a92b453
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

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

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 18, 2025
@Tietew Tietew deleted the cf-vpc-origin branch February 18, 2025 22:29
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
admired-contributor [Pilot] contributed between 13-24 PRs to the CDK effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p2 pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(CloudFront): Add coverage/support for VPC origins construct
5 participants