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(aws-cloudfront): Support Security Policy #804

Merged
merged 4 commits into from
Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 73 additions & 12 deletions packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,45 @@ export enum ViewerProtocolPolicy {
}

/**
* CloudFront can use a custom domain that you provide instead of a "cloudfront.net" domain.
* To use this feature - you must provide the list of additional domains,
* and the ACM Certificate that CloudFront should use for these additional domains.
* Configuration for custom domain names
*
* Note - CloudFront only accepts one additional certificate - therefore the certificate *must*
* use have SANs (Subject Alternative Names) for all domains listed.
*
* sslMethod is optional - we default to SNI if not specified. See the notes on SSLMethod if you wish to use other SSL termination types.
*
* @default sslMethod: SNI by default
* CloudFront can use a custom domain that you provide instead of a
* "cloudfront.net" domain. To use this feature you must provide the list of
* additional domains, and the ACM Certificate that CloudFront should use for
* these additional domains.
*/
export interface AliasConfiguration {
readonly names: string[],
readonly acmCertRef: string,
readonly sslMethod?: SSLMethod,
/**
* ARN of an AWS Certificate Manager (ACM) certificate.
*/
readonly acmCertRef: string;

/**
* Domain names on the certificate
*
* Both main domain name and Subject Alternative Names.
*/
readonly names: string[];

/**
* How CloudFront should serve HTTPS requests.
*
* See the notes on SSLMethod if you wish to use other SSL termination types.
*
* @default SNI
* @see https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ViewerCertificate.html
*/
readonly sslMethod?: SSLMethod;

/**
* The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections.
*
* CloudFront serves your objects only to browsers or devices that support at
* least the SSL version that you specify.
*
* @default securityPolicy: SSLv3 if sslMethod VIP, TLSv1 if sslMethod SNI
*/
readonly securityPolicy?: SecurityPolicyProtocol;
}

/**
Expand All @@ -64,6 +88,18 @@ export enum SSLMethod {
VIP = "vip"
}

/**
* The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections.
* CloudFront serves your objects only to browsers or devices that support at least the SSL version that you specify.
*/
export enum SecurityPolicyProtocol {
SSLv3 = "SSLv3",
TLSv1 = "TLSv1",
TLSv1_2016 = "TLSv1_2016",
TLSv1_1_2016 = "TLSv1.1_2016",
TLSv1_2_2018 = "TLSv1.2_2018"
}

/**
* CloudFront supports logging of incoming requests and can log details to a given S3 Bucket.
*
Expand Down Expand Up @@ -453,6 +489,17 @@ export class CloudFrontWebDistribution extends cdk.Construct {
ALL: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"],
};

/**
* Maps for which SecurityPolicyProtocol are available to which SSLMethods
*/
private readonly VALID_SSL_PROTOCOLS: { [key: string]: string[] } = {
"sni-only": [
SecurityPolicyProtocol.TLSv1, SecurityPolicyProtocol.TLSv1_1_2016,
SecurityPolicyProtocol.TLSv1_2016, SecurityPolicyProtocol.TLSv1_2_2018
],
"vip": [SecurityPolicyProtocol.SSLv3, SecurityPolicyProtocol.TLSv1],
};

constructor(parent: cdk.Construct, name: string, props: CloudFrontWebDistributionProps) {
super(parent, name);

Expand Down Expand Up @@ -554,7 +601,21 @@ export class CloudFrontWebDistribution extends cdk.Construct {
distributionConfig.viewerCertificate = {
acmCertificateArn: props.aliasConfiguration.acmCertRef,
sslSupportMethod: props.aliasConfiguration.sslMethod || SSLMethod.SNI,
minimumProtocolVersion: props.aliasConfiguration.securityPolicy
};

if (distributionConfig.viewerCertificate.minimumProtocolVersion !== undefined) {
const validProtocols = this.VALID_SSL_PROTOCOLS[distributionConfig.viewerCertificate.sslSupportMethod!.toString()];

if (validProtocols === undefined) {
throw new Error(`Invalid sslMethod. ${distributionConfig.viewerCertificate.sslSupportMethod!.toString()} is not fully implemented yet.`);
}

if (validProtocols.indexOf(distributionConfig.viewerCertificate.minimumProtocolVersion.toString()) === -1) {
// tslint:disable-next-line:max-line-length
throw new Error(`${distributionConfig.viewerCertificate.minimumProtocolVersion} is not compabtible with sslMethod ${distributionConfig.viewerCertificate.sslSupportMethod}.\n\tValid Protocols are: ${validProtocols.join(", ")}`);
}
}
} else {
distributionConfig.viewerCertificate = {
cloudFrontDefaultCertificate: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"Resources": {
"AnAmazingWebsiteProbablyCFDistribution47E3983B": {
"Type": "AWS::CloudFront::Distribution",
"Properties": {
"DistributionConfig": {
"CacheBehaviors": [],
"DefaultCacheBehavior": {
"AllowedMethods": [
"GET",
"HEAD"
],
"CachedMethods": [
"GET",
"HEAD"
],
"ForwardedValues": {
"Cookies": {
"Forward": "none"
},
"QueryString": false
},
"TargetOriginId": "origin1",
"ViewerProtocolPolicy": "redirect-to-https"
},
"DefaultRootObject": "index.html",
"Enabled": true,
"HttpVersion": "http2",
"IPV6Enabled": true,
"Origins": [
{
"CustomOriginConfig": {
"HTTPPort": 80,
"HTTPSPort": 443,
"OriginKeepaliveTimeout": 5,
"OriginProtocolPolicy": "https-only",
"OriginReadTimeout": 30,
"OriginSSLProtocols": [
"TLSv1.2"
]
},
"DomainName": "brelandm.a2z.com",
"Id": "origin1",
"OriginCustomHeaders": [
{
"HeaderName": "X-Custom-Header",
"HeaderValue": "somevalue"
}
]
}
],
"PriceClass": "PriceClass_100",
"ViewerCertificate": {
"AcmCertificateArn": "testACM",
"MinimumProtocolVersion": "TLSv1",
"SslSupportMethod": "sni-only"
},
"Aliases": [
"test.test.com"
]
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

import cdk = require('@aws-cdk/cdk');
import cloudfront = require('../lib');

const app = new cdk.App(process.argv);

const stack = new cdk.Stack(app, 'aws-cdk-cloudfront-custom');

new cloudfront.CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', {
originConfigs: [
{
originHeaders: {
"X-Custom-Header": "somevalue",
},
customOriginSource: {
domainName: "brelandm.a2z.com",
},
behaviors: [
{
isDefaultBehavior: true,
}
]
}
],
aliasConfiguration: {
acmCertRef: 'testACM',
names: ['test.test.com'],
sslMethod: cloudfront.SSLMethod.SNI,
securityPolicy: cloudfront.SecurityPolicyProtocol.TLSv1
}
});

process.stdout.write(app.run());