Skip to content

Commit

Permalink
feat(apigateway): mTLS support (#10521)
Browse files Browse the repository at this point in the history
Resolves #10487 

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
doug-ferris-mondo authored Oct 15, 2020
1 parent b2f16b3 commit eb2c568
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 1 deletion.
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ running on AWS Lambda, or any web application.
- [IAM-based authorizer](#iam-based-authorizer)
- [Lambda-based token authorizer](#lambda-based-token-authorizer)
- [Lambda-based request authorizer](#lambda-based-request-authorizer)
- [Mutual TLS](#mutal-tls-mtls)
- [Deployments](#deployments)
- [Deep dive: Invalidation of deployments](#deep-dive-invalidation-of-deployments)
- [Custom Domains](#custom-domains)
Expand Down Expand Up @@ -573,6 +574,24 @@ Authorizers can also be passed via the `defaultMethodOptions` property within th
explicitly overridden, the specified defaults will be applied across all `Method`s across the `RestApi` or across all `Resource`s,
depending on where the defaults were specified.

## Mutual TLS (mTLS)

Mutual TLS can be configured to limit access to your API based by using client certificates instead of (or as an extension of) using authorization headers.

```ts
new apigw.DomainName(this, 'domain-name', {
domainName: 'example.com',
certificate: acm.Certificate.fromCertificateArn(this, 'cert' 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'),
mtls: {
bucket: new Bucket(this, 'bucket')),
key: 'truststore.pem',
version: 'version',
},
});
```

Instructions for configuring your trust store can be found [here](https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/).

## Deployments

By default, the `RestApi` construct will automatically create an API Gateway
Expand Down
41 changes: 40 additions & 1 deletion packages/@aws-cdk/aws-apigateway/lib/domain-name.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as acm from '@aws-cdk/aws-certificatemanager';
import { IBucket } from '@aws-cdk/aws-s3';
import { IResource, Resource, Token } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDomainName } from './apigateway.generated';
Expand Down Expand Up @@ -40,6 +41,12 @@ export interface DomainNameOptions {
* @default SecurityPolicy.TLS_1_0
*/
readonly securityPolicy?: SecurityPolicy

/**
* The mutual TLS authentication configuration for a custom domain name.
* @default - mTLS is not configured.
*/
readonly mtls?: MTLSConfig
}

export interface DomainNameProps extends DomainNameOptions {
Expand Down Expand Up @@ -76,6 +83,7 @@ export interface IDomainName extends IResource {
* @attribute DistributionHostedZoneId,RegionalHostedZoneId
*/
readonly domainNameAliasHostedZoneId: string;

}

export class DomainName extends Resource implements IDomainName {
Expand Down Expand Up @@ -107,12 +115,13 @@ export class DomainName extends Resource implements IDomainName {
throw new Error('domainName does not support uppercase letters. ' +
`got: '${props.domainName}'`);
}

const mtlsConfig = this.configureMTLS(props.mtls);
const resource = new CfnDomainName(this, 'Resource', {
domainName: props.domainName,
certificateArn: edge ? props.certificate.certificateArn : undefined,
regionalCertificateArn: edge ? undefined : props.certificate.certificateArn,
endpointConfiguration: { types: [endpointType] },
mutualTlsAuthentication: mtlsConfig,
securityPolicy: props.securityPolicy,
});

Expand Down Expand Up @@ -145,6 +154,14 @@ export class DomainName extends Resource implements IDomainName {
...options,
});
}

private configureMTLS(mtlsConfig?: MTLSConfig): CfnDomainName.MutualTlsAuthenticationProperty | undefined {
if (!mtlsConfig) return undefined;
return {
truststoreUri: mtlsConfig.bucket.s3UrlForObject(mtlsConfig.key),
truststoreVersion: mtlsConfig.version,
};
}
}

export interface DomainNameAttributes {
Expand All @@ -162,4 +179,26 @@ export interface DomainNameAttributes {
* Thje Route53 hosted zone ID to use in order to connect a record set to this domain through an alias.
*/
readonly domainNameAliasHostedZoneId: string;

}

/**
* The mTLS authentication configuration for a custom domain name.
*/
export interface MTLSConfig {
/**
* The bucket that the trust store is hosted in.
*/
readonly bucket: IBucket;
/**
* The key in S3 to look at for the trust store
*/
readonly key: string;

/**
* The version of the S3 object that contains your truststore.
* To specify a version, you must have versioning enabled for the S3 bucket.
* @default - latest version
*/
readonly version?: string;
}
44 changes: 44 additions & 0 deletions packages/@aws-cdk/aws-apigateway/test/test.domains.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ABSENT, expect, haveResource } from '@aws-cdk/assert';
import * as acm from '@aws-cdk/aws-certificatemanager';
import { Bucket } from '@aws-cdk/aws-s3';
import { Stack } from '@aws-cdk/core';
import { Test } from 'nodeunit';
import * as apigw from '../lib';
Expand Down Expand Up @@ -399,4 +400,47 @@ export = {
}));
test.done();
},

'accepts a mutual TLS configuration'(test: Test) {
const stack = new Stack();
const bucket = Bucket.fromBucketName(stack, 'testBucket', 'exampleBucket');
new apigw.DomainName(stack, 'another-domain', {
domainName: 'example.com',
mtls: {
bucket,
key: 'someca.pem',
},
certificate: acm.Certificate.fromCertificateArn(stack, 'cert', 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'),
});

expect(stack).to(haveResource('AWS::ApiGateway::DomainName', {
'DomainName': 'example.com',
'EndpointConfiguration': { 'Types': ['REGIONAL'] },
'RegionalCertificateArn': 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d',
'MutualTlsAuthentication': { 'TruststoreUri': 's3://exampleBucket/someca.pem' },
}));
test.done();
},

'mTLS should allow versions to be set on the s3 bucket'(test: Test) {
const stack = new Stack();
const bucket = Bucket.fromBucketName(stack, 'testBucket', 'exampleBucket');
new apigw.DomainName(stack, 'another-domain', {
domainName: 'example.com',
certificate: acm.Certificate.fromCertificateArn(stack, 'cert2', 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'),
mtls: {
bucket,
key: 'someca.pem',
version: 'version',
},
});
expect(stack).to(haveResource('AWS::ApiGateway::DomainName', {
'DomainName': 'example.com',
'EndpointConfiguration': { 'Types': ['REGIONAL'] },
'RegionalCertificateArn': 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d',
'MutualTlsAuthentication': { 'TruststoreUri': 's3://exampleBucket/someca.pem', 'TruststoreVersion': 'version' },
}));
test.done();
},

};

0 comments on commit eb2c568

Please sign in to comment.