Skip to content

Commit

Permalink
feat(apigatewayv2): http api - mTLS support (aws#17284)
Browse files Browse the repository at this point in the history
Resolves aws#12559 

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
serahisaactho authored and TikiTDO committed Feb 21, 2022
1 parent fe98237 commit fb63c70
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 0 deletions.
24 changes: 24 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Higher level constructs for Websocket APIs | ![Experimental](https://img.shields
- [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors)
- [Publishing HTTP APIs](#publishing-http-apis)
- [Custom Domain](#custom-domain)
- [Mutual TLS](#mutual-tls-mtls)
- [Managing access](#managing-access)
- [Metrics](#metrics)
- [VPC Link](#vpc-link)
Expand Down Expand Up @@ -254,6 +255,29 @@ declare const apiDemo: apigwv2.HttpApi;
const demoDomainUrl = apiDemo.defaultStage?.domainUrl; // returns "https://example.com/demo"
```

## 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
import * as s3 from '@aws-cdk/aws-s3';
const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
const domainName = 'example.com';
const bucket = new s3.Bucket.fromBucketName(stack, 'TrustStoreBucket', ...);

new DomainName(stack, 'DomainName', {
domainName,
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
mtls: {
bucket,
key: 'someca.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/)

### Managing access

API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP
Expand Down
37 changes: 37 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ICertificate } 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, CfnDomainNameProps } from '../apigatewayv2.generated';
Expand Down Expand Up @@ -59,6 +60,32 @@ export interface DomainNameProps {
* The ACM certificate for this domain name
*/
readonly certificate: ICertificate;
/**
* The mutual TLS authentication configuration for a custom domain name.
* @default - mTLS is not configured.
*/
readonly mtls?: MTLSConfig
}

/**
* 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;
}

/**
Expand Down Expand Up @@ -88,6 +115,7 @@ export class DomainName extends Resource implements IDomainName {
throw new Error('empty string for domainName not allowed');
}

const mtlsConfig = this.configureMTLS(props.mtls);
const domainNameProps: CfnDomainNameProps = {
domainName: props.domainName,
domainNameConfigurations: [
Expand All @@ -96,10 +124,19 @@ export class DomainName extends Resource implements IDomainName {
endpointType: 'REGIONAL',
},
],
mutualTlsAuthentication: mtlsConfig,
};
const resource = new CfnDomainName(this, 'Resource', domainNameProps);
this.name = resource.ref;
this.regionalDomainName = Token.asString(resource.getAtt('RegionalDomainName'));
this.regionalHostedZoneId = Token.asString(resource.getAtt('RegionalHostedZoneId'));
}

private configureMTLS(mtlsConfig?: MTLSConfig): CfnDomainName.MutualTlsAuthenticationProperty | undefined {
if (!mtlsConfig) return undefined;
return {
truststoreUri: mtlsConfig.bucket.s3UrlForObject(mtlsConfig.key),
truststoreVersion: mtlsConfig.version,
};
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
Expand All @@ -97,6 +98,7 @@
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
Expand Down
63 changes: 63 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Template } from '@aws-cdk/assertions';
import { Certificate } from '@aws-cdk/aws-certificatemanager';
import { Bucket } from '@aws-cdk/aws-s3';
import { Stack } from '@aws-cdk/core';
import { DomainName, HttpApi } from '../../lib';

Expand Down Expand Up @@ -168,4 +169,66 @@ describe('DomainName', () => {
expect(t).toThrow('defaultDomainMapping not supported with createDefaultStage disabled');

});

test('accepts a mutual TLS configuration', () => {
// GIVEN
const stack = new Stack();
const bucket = Bucket.fromBucketName(stack, 'testBucket', 'example-bucket');

// WHEN
new DomainName(stack, 'DomainName', {
domainName,
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
mtls: {
bucket,
key: 'someca.pem',
},
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::DomainName', {
DomainName: 'example.com',
DomainNameConfigurations: [
{
CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate',
EndpointType: 'REGIONAL',
},
],
MutualTlsAuthentication: {
TruststoreUri: 's3://example-bucket/someca.pem',
},
});
});

test('mTLS should allow versions to be set on the s3 bucket', () => {
// GIVEN
const stack = new Stack();
const bucket = Bucket.fromBucketName(stack, 'testBucket', 'example-bucket');

// WHEN
new DomainName(stack, 'DomainName', {
domainName,
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
mtls: {
bucket,
key: 'someca.pem',
version: 'version',
},
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::DomainName', {
DomainName: 'example.com',
DomainNameConfigurations: [
{
CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate',
EndpointType: 'REGIONAL',
},
],
MutualTlsAuthentication: {
TruststoreUri: 's3://example-bucket/someca.pem',
TruststoreVersion: 'version',
},
});
});
});

0 comments on commit fb63c70

Please sign in to comment.