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(route53): Vpc endpoint service private dns #10780

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
446a54c
Merge pull request #21 from aws/master
flemjame-at-amazon Aug 31, 2020
26f6998
Merge pull request #22 from aws/master
flemjame-at-amazon Sep 2, 2020
702316c
Merge pull request #23 from aws/master
flemjame-at-amazon Sep 9, 2020
af13454
Merge pull request #24 from aws/master
flemjame-at-amazon Sep 16, 2020
7e8d2f1
Merge pull request #26 from aws/master
flemjame-at-amazon Oct 1, 2020
4d7ec0f
Merge pull request #27 from aws/master
flemjame-at-amazon Oct 5, 2020
4c617d5
Merge pull request #28 from aws/master
flemjame-at-amazon Oct 6, 2020
8f40f10
Initial commit
flemjame-at-amazon Oct 7, 2020
1fc9648
Fix the tests, comment code, and add error checking for multiplmultip…
flemjame-at-amazon Oct 7, 2020
5d9472d
feat(route53-patterns): Vpc Endpoint Service private DNS
flemjame-at-amazon Oct 8, 2020
bb7e875
Also add a readme blurb in the EC2 section
flemjame-at-amazon Oct 9, 2020
3687738
Merge pull request #29 from aws/master
flemjame-at-amazon Oct 21, 2020
578c851
Update jest to resolve conflict
flemjame-at-amazon Oct 21, 2020
426f771
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Oct 21, 2020
46b4360
Remove elbv2 as a dependency by mocking out the lb in unit testing, a…
flemjame-at-amazon Oct 21, 2020
d6c31f1
review feedback
flemjame-at-amazon Oct 21, 2020
30a654a
Change property name to publicHostedZone
flemjame-at-amazon Oct 21, 2020
e260ffc
Change property name to publicHostedZone
flemjame-at-amazon Oct 21, 2020
e948aab
Review feedback
flemjame-at-amazon Oct 21, 2020
684db32
Move to route53
flemjame-at-amazon Oct 21, 2020
3b026e8
Don't need route53-patterns package.json changes
flemjame-at-amazon Oct 21, 2020
4c294ba
Add integration test
flemjame-at-amazon Oct 22, 2020
da1d66e
Add integration test
flemjame-at-amazon Oct 22, 2020
e83aaa1
Add integration test
flemjame-at-amazon Oct 22, 2020
94a61dd
Switch from random string to hash of parameters that would cause a ch…
flemjame-at-amazon Oct 22, 2020
06c141b
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Oct 22, 2020
b0b4e46
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Oct 27, 2020
4f95f91
Merge pull request #30 from aws/master
flemjame-at-amazon Oct 27, 2020
bf66346
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Oct 29, 2020
ff00622
Resolve conflicts
flemjame-at-amazon Oct 29, 2020
624a2d5
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Oct 29, 2020
2094a72
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 3, 2020
8dd5d53
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 4, 2020
fdf8436
Not sure why the hashes changed after a merge
flemjame-at-amazon Nov 4, 2020
bc913f9
Review feedback
flemjame-at-amazon Nov 4, 2020
46e3645
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 9, 2020
e8c91fd
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 16, 2020
f2f6fa4
Review feedback
flemjame-at-amazon Nov 16, 2020
dd506ff
Switch to user ARN
flemjame-at-amazon Nov 16, 2020
31d80c6
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 16, 2020
5a90d35
last element of an ARN needs to be joined by a / not a :
flemjame-at-amazon Nov 16, 2020
1e94c9b
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 17, 2020
31f5b10
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 19, 2020
c2a80f6
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 23, 2020
722a94e
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Nov 30, 2020
09354d2
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Dec 8, 2020
c99da4d
Update README.md
flemjame-at-amazon Dec 8, 2020
8d10ca2
Merge branch 'master' into vpc-endpoint-service-private-dns
flemjame-at-amazon Dec 16, 2020
b56d705
Merge branch 'master' into vpc-endpoint-service-private-dns
mergify[bot] Dec 17, 2020
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
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,20 @@ new VpcEndpointService(this, 'EndpointService', {
});
```

Endpoint services support private DNS, which makes it easier for clients to connect to your service by automatically setting up DNS in their VPC.
You can enable private DNS on an endpoint service like so:

```ts
new VpcEndpointServiceDomainName(stack, 'EndpointDomain', {
endpointService: vpces,
domainName: 'my-stuff.aws-cdk.dev',
publicZone: zone,
});
```

Note: You must have verifiable ownership of the domain before customers can use private DNS. The VpcEndpointServiceDomainName
will verify ownership for you, assuming you own the domain.

## Instances

You can use the `Instance` class to start up a single EC2 instance. For production setups, we recommend
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ export interface IVpcEndpointService extends IResource {
* @attribute
*/
readonly vpcEndpointServiceName: string;

/**
* The id of the VPC Endpoint Service that clients use to connect to,
* like vpce-svc-xxxxxxxxxxxxxxxx
*
* @attribute
*/
readonly vpcEndpointServiceId: string;
Comment on lines +31 to +37
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a test that uses this property?

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, in ec2:

new cdk.CfnOutput(this, 'MyVpcEndpointServiceWithPrincipalsEndpointServiceId', {
exportName: 'EndpointServiceId',
value: service2.vpcEndpointServiceId,
description: 'Reference this service from other stacks',

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 made it an attribute of IVpcEndpointService because I would rather have it take an IVpcEndpointService as an argument, vs a VpcEndpointService

}

/**
Expand Down
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-route53-patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,41 @@ As an existing certificate is not provided, one will be created in `us-east-1` b
})
});
```

## VPC Endpoint Service Private DNS

When you create a VPC endpoint service, AWS generates endpoint-specific DNS hostnames that consumers use to communicate with the service.
For example, vpce-1234-abcdev-us-east-1.vpce-svc-123345.us-east-1.vpce.amazonaws.com.
By default, your consumers access the service with that DNS name.
This can cause problems with HTTPS traffic because the DNS will not match the backend certificate.
To mitigate this, clients have to create an alias in Route53 which requires changes to their application.

Private DNS for an endpoint service lets you configure a private DNS name so consumers can
access the service using an existing DNS name without making changes to their applications.
This DNS name can also be guaranteed to match up with the backend certificate.

Before consumers can use the private DNS name, you must verify that you have control of the domain/subdomain.

Assuming your account has verifiable ownership of the particlar domain/subdomain,
this construct sets up the private DNS configuration on the endpoint service,
creates all the necessary Route53 entries, and verifies domain ownership.

```
stack = new Stack();
vpc = new Vpc(stack, 'VPC');
nlb = new NetworkLoadBalancer(stack, 'NLB', {
vpc,
});
vpces = new VpcEndpointService(stack, 'VPCES', {
vpcEndpointServiceLoadBalancers: [nlb],
});
// You must use a public hosted zone so domain ownership can be verified
zone = new PublicHostedZone(stack, 'PHZ', {
zoneName: 'aws-cdk.dev',
});
new VpcEndpointServiceDomainName(stack, 'EndpointDomain', {
endpointService: vpces,
domainName: 'my-stuff.aws-cdk.dev',
publicZone: zone,
});
```
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-route53-patterns/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './vpc-endpoint-service-domain-name';
export * from './website-redirect';
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { IVpcEndpointService } from '@aws-cdk/aws-ec2';
import { IPublicHostedZone, TxtRecord } from '@aws-cdk/aws-route53';
import { Fn } from '@aws-cdk/core';
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources';
import { Construct } from 'constructs';

// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
import { Construct as CoreConstruct } from '@aws-cdk/core';

/**
* Properties to configure a VPC Endpoint Service domain name
*/
export interface VpcEndpointServiceDomainNameProps {

/**
* The VPC Endpoint Service to configure Private DNS for
*/
readonly endpointService: IVpcEndpointService;

/**
* The domain name to use.
*
* This domain name must be verifiably owned by this account, or otherwise
* delegated to this account.
* https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-dns-validation.html
*/
readonly domainName: string;

/**
* The public hosted zone to use for the domain.
*/
readonly publicZone: IPublicHostedZone;
}

/**
* A Private DNS configuration for a VPC endpoint service.
*/
export class VpcEndpointServiceDomainName extends CoreConstruct {

// Track all domain names created, so someone doesn't accidentally associate two domains with a single service
private static readonly endpointServices: IVpcEndpointService[] = [];

// The way this class works is by using three custom resources and a TxtRecord in conjunction
// The first custom resource tells the VPC endpoint service to use the given DNS name
// The VPC endpoint service will then say:
// "ok, create a TXT record using these two values to prove you own the domain"
// The second custom resource retrieves these two values from the service
// The TxtRecord is created from these two values
// The third custom resource tells the VPC Endpoint Service to verify the domain ownership
constructor(scope: Construct, id: string, props: VpcEndpointServiceDomainNameProps) {
super(scope, id);

// Make sure a user doesn't accidentally add multiple domains
this.validateParams(props);
VpcEndpointServiceDomainName.endpointServices.push(props.endpointService);

const serviceId = props.endpointService.vpcEndpointServiceId;
const privateDnsName = props.domainName;

// Turns creates the Private DNS configuration on the endpoint service
const enable = new AwsCustomResource(this, 'EnableDns', {
onCreate: {
service: 'EC2',
action: 'modifyVpcEndpointServiceConfiguration',
parameters: {
ServiceId: serviceId,
PrivateDnsName: privateDnsName,
},
physicalResourceId: PhysicalResourceId.of(id),
},
onUpdate: {
service: 'EC2',
action: 'modifyVpcEndpointServiceConfiguration',
parameters: {
ServiceId: serviceId,
PrivateDnsName: privateDnsName,
},
physicalResourceId: PhysicalResourceId.of(id),
},
onDelete: {
service: 'EC2',
action: 'modifyVpcEndpointServiceConfiguration',
parameters: {
ServiceId: serviceId,
RemovePrivateDnsName: true,
},
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});

// Get the name/value pair for the TxtRecord
// We're always going to check the name/value because it's harmless
const guaranteeAlwaysLookup = randomString();
const getNames = new AwsCustomResource(this, 'GetNames', {
onCreate: {
service: 'EC2',
action: 'describeVpcEndpointServiceConfigurations',
parameters: {
ServiceIds: [serviceId],
},
physicalResourceId: PhysicalResourceId.of(guaranteeAlwaysLookup),
},
onUpdate: {
service: 'EC2',
action: 'describeVpcEndpointServiceConfigurations',
parameters: {
ServiceIds: [serviceId],
},
physicalResourceId: PhysicalResourceId.of(guaranteeAlwaysLookup),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
// We only want to call and get the TXT DNS details after we've enabled private DNS
getNames.node.addDependency(enable);

// Here are the name/value we get from the endpoint service
const name = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Name');
const value = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Value');

// Create the TXT record in the provided hosted zone
const verificationRecord = new TxtRecord(this, 'DnsVerificationRecord', {
recordName: name,
values: [value],
zone: props.publicZone,
});
// Only try making it once we have the values
verificationRecord.node.addDependency(getNames);

// Tell the endpoint service to verify the domain ownership
const startVerification = new AwsCustomResource(this, 'StartVerification', {
onCreate: {
service: 'EC2',
action: 'startVpcEndpointServicePrivateDnsVerification',
parameters: {
ServiceId: serviceId,
},
physicalResourceId: PhysicalResourceId.of(Fn.join(':', [name, value])),
},
onUpdate: {
service: 'EC2',
action: 'startVpcEndpointServicePrivateDnsVerification',
parameters: {
ServiceId: serviceId,
},
physicalResourceId: PhysicalResourceId.of(Fn.join(':', [name, value])),
},
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
// Only verify after the record has been created
startVerification.node.addDependency(verificationRecord);

// Finally, don't do any of the above before the endpoint service is created
this.node.addDependency(props.endpointService);
}

private validateParams(props: VpcEndpointServiceDomainNameProps): void {
if (VpcEndpointServiceDomainName.endpointServices.includes(props.endpointService)) {
throw new Error(
'Cannot create a VpcEndpointServiceDomainName for service ' +
props.endpointService.node.uniqueId +
', another VpcEndpointServiceDomainName is already associated with it');
}
}
}

/**
* Generate a random hex string
*/
function randomString(): string {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 8);
}
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-route53-patterns/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,32 +65,40 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assert": "0.0.0",
"@types/nodeunit": "^0.0.31",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"cfn2ts": "0.0.0",
"jest": "^26.4.2",
"nodeunit": "^0.11.3",
"pkglint": "0.0.0"
},
"dependencies": {
"@aws-cdk/aws-certificatemanager": "0.0.0",
"@aws-cdk/aws-cloudfront": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-route53": "0.0.0",
"@aws-cdk/aws-route53-targets": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/region-info": "0.0.0",
"constructs": "^3.0.4"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-certificatemanager": "0.0.0",
"@aws-cdk/aws-cloudfront": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-route53": "0.0.0",
"@aws-cdk/aws-route53-targets": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/region-info": "0.0.0",
"constructs": "^3.0.4"
},
Expand Down
Empty file.
Loading