diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts index b31c631d6938c..b26555fb260a7 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts @@ -1,7 +1,7 @@ import cdk = require('@aws-cdk/cdk'); import { BaseListener } from '../shared/base-listener'; import { HealthCheck } from '../shared/base-target-group'; -import { Protocol } from '../shared/enums'; +import { Protocol, SslPolicy } from '../shared/enums'; import { INetworkLoadBalancer } from './network-load-balancer'; import { INetworkLoadBalancerTarget, INetworkTargetGroup, NetworkTargetGroup } from './network-target-group'; @@ -20,6 +20,31 @@ export interface BaseNetworkListenerProps { * @default None */ readonly defaultTargetGroups?: INetworkTargetGroup[]; + + /** + * Protocol for listener, expects TCP or TLS + */ + readonly protocol?: Protocol; + + /** + * Certificate list of ACM cert ARNs + */ + readonly certificates?: INetworkListenerCertificateProps[]; + + /** + * SSL Policy + */ + readonly sslPolicy?: SslPolicy; +} + +/** + * Properties for adding a certificate to a listener + */ +export interface INetworkListenerCertificateProps { + /** + * Certificate ARN from ACM + */ + readonly certificateArn: string } /** @@ -49,10 +74,27 @@ export class NetworkListener extends BaseListener implements INetworkListener { private readonly loadBalancer: INetworkLoadBalancer; constructor(scope: cdk.Construct, id: string, props: NetworkListenerProps) { + const certs = props.certificates || []; + const proto = props.protocol || (certs.length > 0 ? Protocol.Tls : Protocol.Tcp); + + if ([Protocol.Tcp, Protocol.Tls].indexOf(proto) === -1) { + throw new Error(`The protocol must be either ${Protocol.Tcp} or ${Protocol.Tls}. Found ${props.protocol}`); + } + + if (proto === Protocol.Tls && certs.filter(v => v != null).length === 0) { + throw new Error(`When the protocol is set to TLS, you must specify certificates`); + } + + if (proto !== Protocol.Tls && certs.length > 0) { + throw new Error(`Protocol must be TLS when certificates have been specified`); + } + super(scope, id, { loadBalancerArn: props.loadBalancer.loadBalancerArn, - protocol: Protocol.Tcp, + protocol: proto, port: props.port, + sslPolicy: props.sslPolicy, + certificates: props.certificates }); this.loadBalancer = props.loadBalancer; @@ -108,7 +150,6 @@ export class NetworkListener extends BaseListener implements INetworkListener { listenerArn: new cdk.CfnOutput(this, 'ListenerArn', { value: this.listenerArn }).makeImportValue().toString() }; } - } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts index f043272ab139d..66851cd621750 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts @@ -30,7 +30,12 @@ export enum Protocol { /** * TCP */ - Tcp = 'TCP' + Tcp = 'TCP', + + /** + * TLS + */ + Tls = 'TLS' } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package-lock.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package-lock.json index b3668a6be5605..2f7088718bc9b 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package-lock.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package-lock.json @@ -1,5 +1,297 @@ { "name": "@aws-cdk/aws-elasticloadbalancingv2", - "version": "0.28.0", - "lockfileVersion": 1 + "version": "0.29.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@aws-cdk/assert": { + "version": "0.29.0", + "dev": true, + "requires": { + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/cloudformation-diff": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0", + "source-map-support": "^0.5.12" + } + }, + "@aws-cdk/assets": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/assets/-/assets-0.29.0.tgz", + "integrity": "sha512-OfAq/xxGd+Fh6nsXKpTrKzRfi9Q8lsTd5+PyLeuMyVAHE6mPGXi0qL93T4dtwErDEEXVlLWBiMFpvBztNKSgLw==", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-s3": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0", + "minimatch": "^3.0.4" + }, + "dependencies": { + "@aws-cdk/cx-api": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-0.29.0.tgz", + "integrity": "sha512-SnktMmv4flwsGSJmfDJzqD23K/U/UzCCrJkShZX7Etqdm0SiI1aS5PlkDq4iHEy42bIRlyIOhqavKCkTMWt9fg==" + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "@aws-cdk/aws-autoscaling-api": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling-api/-/aws-autoscaling-api-0.29.0.tgz", + "integrity": "sha512-9rwx90OJelKPPGuL9OY/fs8AijKfU+sC8hsaTZ9bc9AjiPeUCc8/d+jvaJbw7Gc0PV0l0HNC2zKo95pGt6Bqug==", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-certificatemanager": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-0.29.0.tgz", + "integrity": "sha512-KUsCXq4Tf/Dpf9jH6cLhYl5bzHpxdXrG6gdIEjmjbHt9lqcRBXWclvn+ItyveBiBq81CB9czs+ypsBX5gbmi9Q==", + "requires": { + "@aws-cdk/aws-cloudformation": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-lambda": "^0.29.0", + "@aws-cdk/aws-route53": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-cloudformation": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudformation/-/aws-cloudformation-0.29.0.tgz", + "integrity": "sha512-JwyqWMjmHstRZu1Zlih+Y+NvC5c8SVpybzcEmrAfwxa4e8fyzpCrWg+iJHhUZWXof/wQRD1ZsL/WUdVEwZfKJA==", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-lambda": "^0.29.0", + "@aws-cdk/aws-sns": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-cloudwatch": { + "version": "0.29.0", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-codedeploy-api": { + "version": "0.29.0", + "requires": { + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-ec2": { + "version": "0.29.0", + "requires": { + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0" + } + }, + "@aws-cdk/aws-events": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-events/-/aws-events-0.29.0.tgz", + "integrity": "sha512-97dzhbtA6MyL+wpnsCxZ7whhAqMtQYD5fx+bPZzE6NjRoZ/41F1Jk5Uz9maWiVhH+1pP1joY3svxy1ZpOxXOHQ==", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-iam": { + "version": "0.29.0", + "requires": { + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/region-info": "^0.29.0" + } + }, + "@aws-cdk/aws-kms": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kms/-/aws-kms-0.29.0.tgz", + "integrity": "sha512-3n711C5mVIm527gkZECiEFHDqJmd9vk/vJRFlGM/1tGA8esxsRATekJsLQOchDXrU22Hsr7MhSO23CHkxzbBPg==", + "requires": { + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-lambda": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda/-/aws-lambda-0.29.0.tgz", + "integrity": "sha512-7kYZ501H3JCpJ0fbrWED3z/kqyqK2zS8cy9GmbSolXZKtUmkppWArGMXpUiCJAkwVdAj7i+3dMznzLtWus/nZA==", + "requires": { + "@aws-cdk/assets": "^0.29.0", + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-ec2": "^0.29.0", + "@aws-cdk/aws-events": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-logs": "^0.29.0", + "@aws-cdk/aws-s3": "^0.29.0", + "@aws-cdk/aws-s3-notifications": "^0.29.0", + "@aws-cdk/aws-sqs": "^0.29.0", + "@aws-cdk/aws-stepfunctions": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0" + }, + "dependencies": { + "@aws-cdk/cx-api": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-0.29.0.tgz", + "integrity": "sha512-SnktMmv4flwsGSJmfDJzqD23K/U/UzCCrJkShZX7Etqdm0SiI1aS5PlkDq4iHEy42bIRlyIOhqavKCkTMWt9fg==" + } + } + }, + "@aws-cdk/aws-logs": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-logs/-/aws-logs-0.29.0.tgz", + "integrity": "sha512-q+UzWt9yZs3Nzcz2+YNpBEf3/cpjzMV7iBTer212RtTW3FGsKv0gX4uhAMl9qFZZFve0Zfrw4XI6RLeZW/Ujsg==", + "requires": { + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-route53": { + "version": "0.29.0", + "requires": { + "@aws-cdk/aws-ec2": "^0.29.0", + "@aws-cdk/aws-logs": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0" + } + }, + "@aws-cdk/aws-s3": { + "version": "0.29.0", + "requires": { + "@aws-cdk/aws-events": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-kms": "^0.29.0", + "@aws-cdk/aws-s3-notifications": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-s3-notifications": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-notifications/-/aws-s3-notifications-0.29.0.tgz", + "integrity": "sha512-Hdx+89vYcRxCVioarogYs2BIB9tvfsTC035gZwzMeUCj3evyRTfTs63LdM4edb/nkpQj609XKIKzkf3oWBHMIQ==", + "requires": { + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-sns": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns/-/aws-sns-0.29.0.tgz", + "integrity": "sha512-HyChJcZCxKxICdbqqUewJQVemH7lTZ+hY/x72bCIBA92aMOt8qzKEz3AmQC9k6sRCMPPUTsYiqty3R5dZ0+UAw==", + "requires": { + "@aws-cdk/aws-autoscaling-api": "^0.29.0", + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-events": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-lambda": "^0.29.0", + "@aws-cdk/aws-s3-notifications": "^0.29.0", + "@aws-cdk/aws-sqs": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-sqs": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sqs/-/aws-sqs-0.29.0.tgz", + "integrity": "sha512-LLtcD1N8nm+rQNPQ3T5vmUUI+AzRG/Dz8pxe6q1iUw7R66powQDybMV/xWS1vjdkeFkbamdrk3EH4fgEO0rauQ==", + "requires": { + "@aws-cdk/aws-autoscaling-api": "^0.29.0", + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/aws-kms": "^0.29.0", + "@aws-cdk/aws-s3-notifications": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/aws-stepfunctions": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-stepfunctions/-/aws-stepfunctions-0.29.0.tgz", + "integrity": "sha512-8SQkKn2aPTDHqZo4jyuKQVMMmkb+jut8XSFZje7hRa4FDGqVmbYQb9lMyTEqSwZFb+UTmPiCKn82mNUlKtcx7w==", + "requires": { + "@aws-cdk/aws-cloudwatch": "^0.29.0", + "@aws-cdk/aws-events": "^0.29.0", + "@aws-cdk/aws-iam": "^0.29.0", + "@aws-cdk/cdk": "^0.29.0" + } + }, + "@aws-cdk/cdk": { + "version": "0.29.0", + "requires": { + "@aws-cdk/cx-api": "^0.29.0" + } + }, + "cdk-build-tools": { + "version": "0.29.0", + "dev": true, + "requires": { + "awslint": "^0.29.0", + "fs-extra": "^7.0.1", + "jest": "^24.7.1", + "jsii": "^0.10.3", + "jsii-pacmak": "^0.10.3", + "nodeunit": "^0.11.3", + "nyc": "^14.0.0", + "pkglint": "^0.29.0", + "ts-jest": "^24.0.2", + "tslint": "^5.16.0", + "typescript": "^3.4.3", + "yargs": "^13.2.2" + } + }, + "cdk-integ-tools": { + "version": "0.29.0", + "dev": true, + "requires": { + "@aws-cdk/cloudformation-diff": "^0.29.0", + "@aws-cdk/cx-api": "^0.29.0", + "aws-cdk": "^0.29.0", + "yargs": "^13.2.2" + } + }, + "cfn2ts": { + "version": "0.29.0", + "dev": true, + "requires": { + "@aws-cdk/cfnspec": "^0.29.0", + "codemaker": "^0.10.0", + "fast-json-patch": "^2.1.0", + "fs-extra": "^7.0.1", + "yargs": "^13.2.2" + } + }, + "pkglint": { + "version": "0.29.0", + "dev": true, + "requires": { + "case": "^1.6.1", + "colors": "^1.3.3", + "fs-extra": "^7.0.1", + "semver": "^6.0.0", + "yargs": "^13.2.2" + } + } + } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 45ce49d0dfafc..e112a87fab820 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -66,6 +66,7 @@ "pkglint": "^0.29.0" }, "dependencies": { + "@aws-cdk/aws-certificatemanager": "^0.29.0", "@aws-cdk/aws-cloudwatch": "^0.29.0", "@aws-cdk/aws-codedeploy-api": "^0.29.0", "@aws-cdk/aws-ec2": "^0.29.0", @@ -82,7 +83,8 @@ "@aws-cdk/aws-iam": "^0.29.0", "@aws-cdk/aws-route53": "^0.29.0", "@aws-cdk/aws-s3": "^0.29.0", - "@aws-cdk/cdk": "^0.29.0" + "@aws-cdk/cdk": "^0.29.0", + "@aws-cdk/aws-certificatemanager": "^0.29.0" }, "engines": { "node": ">= 8.10.0" diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts index f5fd6f554c1da..40ee69ff2f1a8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts @@ -1,4 +1,5 @@ import { expect, haveResource, MatchStyle } from '@aws-cdk/assert'; +import acm = require('@aws-cdk/aws-certificatemanager'); import ec2 = require('@aws-cdk/aws-ec2'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; @@ -144,6 +145,83 @@ export = { test.done(); }, + + 'Trivial add TLS listener'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'Stack'); + const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc }); + const cert = new acm.Certificate(stack, 'Certificate', { + domainName: 'example.com' + }); + + // WHEN + lb.addListener('Listener', { + port: 443, + protocol: elbv2.Protocol.Tls, + certificates: [ { certificateArn: cert.certificateArn } ], + sslPolicy: elbv2.SslPolicy.TLS12, + defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })] + }); + + // THEN + expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::Listener', { + Protocol: 'TLS', + Port: 443, + Certificates: [ + { CertificateArn: { Ref: "Certificate4E7ABB08" } } + ], + SslPolicy: "ELBSecurityPolicy-TLS-1-2-2017-01" + })); + + test.done(); + }, + + 'Invalid Protocol listener'(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'Stack'); + const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc }); + + test.throws(() => lb.addListener('Listener', { + port: 443, + protocol: elbv2.Protocol.Http, + defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })] + }), Error, '/The protocol must be either TCP or TLS. Found HTTP/'); + + test.done(); + }, + + 'Protocol & certs TLS listener'(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'Stack'); + const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc }); + + test.throws(() => lb.addListener('Listener', { + port: 443, + protocol: elbv2.Protocol.Tls, + defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })] + }), Error, '/When the protocol is set to TLS, you must specify certificates/'); + + test.done(); + }, + + 'TLS and certs specified listener'(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.VpcNetwork(stack, 'Stack'); + const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc }); + const cert = new acm.Certificate(stack, 'Certificate', { + domainName: 'example.com' + }); + + test.throws(() => lb.addListener('Listener', { + port: 443, + protocol: elbv2.Protocol.Tcp, + certificates: [ { certificateArn: cert.certificateArn } ], + defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })] + }), Error, '/Protocol must be TLS when certificates have been specified/'); + + test.done(); + }, }; class ResourceWithLBDependency extends cdk.CfnResource {