Skip to content

Commit 2a36a94

Browse files
authored
Merge branch 'main' into TheRealAmazonKendra/lambda-layer-awscli-upgrade
2 parents 7b80144 + 17dd132 commit 2a36a94

9 files changed

Lines changed: 196 additions & 3 deletions

File tree

packages/@aws-cdk/aws-certificatemanager/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ new acm.PrivateCertificate(this, 'PrivateCertificate', {
126126
});
127127
```
128128

129+
## Requesting certificates without transparency logging
130+
131+
Transparency logging can be opted out of for AWS Certificate Manager certificates. See [opting out of certifiacte transparency logging](https://docs.aws.amazon.com/acm/latest/userguide/acm-bestpractices.html#best-practices-transparency) for limits.
132+
133+
```ts
134+
new acm.Certificate(this, 'Certificate', {
135+
domainName: 'test.example.com',
136+
transparencyLoggingEnabled: false,
137+
});
138+
```
139+
129140
## Importing
130141

131142
If you want to import an existing certificate, you can do so from its ARN:

packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ let report = function (event, context, responseStatus, physicalResourceId, respo
7878
* @param {map} tags Tags to add to the requested certificate
7979
* @returns {string} Validated certificate ARN
8080
*/
81-
const requestCertificate = async function (requestId, domainName, subjectAlternativeNames, hostedZoneId, region, route53Endpoint, tags) {
81+
const requestCertificate = async function (requestId, domainName, subjectAlternativeNames, certificateTransparencyLoggingPreference, hostedZoneId, region, route53Endpoint, tags) {
8282
const crypto = require('crypto');
8383
const acm = new aws.ACM({ region });
8484
const route53 = route53Endpoint ? new aws.Route53({ endpoint: route53Endpoint }) : new aws.Route53();
@@ -92,6 +92,9 @@ const requestCertificate = async function (requestId, domainName, subjectAlterna
9292
const reqCertResponse = await acm.requestCertificate({
9393
DomainName: domainName,
9494
SubjectAlternativeNames: subjectAlternativeNames,
95+
Options: {
96+
CertificateTransparencyLoggingPreference: certificateTransparencyLoggingPreference
97+
},
9598
IdempotencyToken: crypto.createHash('sha256').update(requestId).digest('hex').slice(0, 32),
9699
ValidationMethod: 'DNS'
97100
}).promise();
@@ -288,6 +291,7 @@ exports.certificateRequestHandler = async function (event, context) {
288291
event.RequestId,
289292
event.ResourceProperties.DomainName,
290293
event.ResourceProperties.SubjectAlternativeNames,
294+
event.ResourceProperties.CertificateTransparencyLoggingPreference,
291295
event.ResourceProperties.HostedZoneId,
292296
event.ResourceProperties.Region,
293297
event.ResourceProperties.Route53Endpoint,

packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/test/handler.test.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ describe('DNS Validated Certificate Handler', () => {
133133
.expectResolve(() => {
134134
sinon.assert.calledWith(requestCertificateFake, sinon.match({
135135
DomainName: testDomainName,
136-
ValidationMethod: 'DNS'
136+
ValidationMethod: 'DNS',
137+
Options: {
138+
CertificateTransparencyLoggingPreference: undefined
139+
}
137140
}));
138141
sinon.assert.calledWith(changeResourceRecordSetsFake, sinon.match({
139142
ChangeBatch: {
@@ -731,6 +734,72 @@ describe('DNS Validated Certificate Handler', () => {
731734
});
732735
});
733736

737+
test('Create operation with `CertificateTransparencyLoggingPreference` requests a certificate with that preference set', () => {
738+
const requestCertificateFake = sinon.fake.resolves({
739+
CertificateArn: testCertificateArn,
740+
});
741+
742+
const describeCertificateFake = sinon.stub();
743+
describeCertificateFake.onFirstCall().resolves({
744+
Certificate: {
745+
CertificateArn: testCertificateArn
746+
}
747+
});
748+
describeCertificateFake.resolves({
749+
Certificate: {
750+
CertificateArn: testCertificateArn,
751+
DomainValidationOptions: [{
752+
ValidationStatus: 'SUCCESS',
753+
ResourceRecord: {
754+
Name: testRRName,
755+
Type: 'CNAME',
756+
Value: testRRValue
757+
}
758+
}]
759+
}
760+
});
761+
762+
const addTagsToCertificateFake = sinon.fake.resolves({});
763+
764+
const changeResourceRecordSetsFake = sinon.fake.resolves({
765+
ChangeInfo: {
766+
Id: 'bogus'
767+
}
768+
});
769+
770+
AWS.mock('ACM', 'requestCertificate', requestCertificateFake);
771+
AWS.mock('ACM', 'describeCertificate', describeCertificateFake);
772+
AWS.mock('Route53', 'changeResourceRecordSets', changeResourceRecordSetsFake);
773+
AWS.mock('ACM', 'addTagsToCertificate', addTagsToCertificateFake);
774+
775+
const request = nock(ResponseURL).put('/', body => {
776+
return body.Status === 'SUCCESS';
777+
}).reply(200);
778+
779+
return LambdaTester(handler.certificateRequestHandler)
780+
.event({
781+
RequestType: 'Create',
782+
RequestId: testRequestId,
783+
ResourceProperties: {
784+
DomainName: testDomainName,
785+
HostedZoneId: testHostedZoneId,
786+
Region: 'us-east-1',
787+
CertificateTransparencyLoggingPreference: 'DISABLED',
788+
Tags: testTags
789+
}
790+
})
791+
.expectResolve(() => {
792+
sinon.assert.calledWith(requestCertificateFake, sinon.match({
793+
DomainName: testDomainName,
794+
ValidationMethod: 'DNS',
795+
Options: {
796+
CertificateTransparencyLoggingPreference: 'DISABLED'
797+
}
798+
}));
799+
expect(request.isDone()).toBe(true);
800+
});
801+
});
802+
734803
test('Delete operation deletes the certificate', () => {
735804
const describeCertificateFake = sinon.fake.resolves({
736805
Certificate: {

packages/@aws-cdk/aws-certificatemanager/lib/certificate.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ export interface CertificateProps {
7272
* @default CertificateValidation.fromEmail()
7373
*/
7474
readonly validation?: CertificateValidation;
75+
76+
/**
77+
* Enable or disable transparency logging for this certificate
78+
*
79+
* Once a certificate has been logged, it cannot be removed from the log.
80+
* Opting out at that point will have no effect. If you opt out of logging
81+
* when you request a certificate and then choose later to opt back in,
82+
* your certificate will not be logged until it is renewed.
83+
* If you want the certificate to be logged immediately, we recommend that you issue a new one.
84+
*
85+
* @see https://docs.aws.amazon.com/acm/latest/userguide/acm-bestpractices.html#best-practices-transparency
86+
*
87+
* @default true
88+
*/
89+
readonly transparencyLoggingEnabled?: boolean;
7590
}
7691

7792
/**
@@ -214,11 +229,17 @@ export class Certificate extends CertificateBase implements ICertificate {
214229

215230
const allDomainNames = [props.domainName].concat(props.subjectAlternativeNames || []);
216231

232+
let certificateTransparencyLoggingPreference: string | undefined;
233+
if (props.transparencyLoggingEnabled !== undefined) {
234+
certificateTransparencyLoggingPreference = props.transparencyLoggingEnabled ? 'ENABLED' : 'DISABLED';
235+
}
236+
217237
const cert = new CfnCertificate(this, 'Resource', {
218238
domainName: props.domainName,
219239
subjectAlternativeNames: props.subjectAlternativeNames,
220240
domainValidationOptions: renderDomainValidation(validation, allDomainNames),
221241
validationMethod: validation.method,
242+
certificateTransparencyLoggingPreference,
222243
});
223244

224245
this.certificateArn = cert.ref;

packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi
9696
this.hostedZoneId = props.hostedZone.hostedZoneId.replace(/^\/hostedzone\//, '');
9797
this.tags = new cdk.TagManager(cdk.TagType.MAP, 'AWS::CertificateManager::Certificate');
9898

99+
let certificateTransparencyLoggingPreference: string | undefined;
100+
if (props.transparencyLoggingEnabled !== undefined) {
101+
certificateTransparencyLoggingPreference = props.transparencyLoggingEnabled ? 'ENABLED' : 'DISABLED';
102+
}
103+
99104
const requestorFunction = new lambda.Function(this, 'CertificateRequestorFunction', {
100105
code: lambda.Code.fromAsset(path.resolve(__dirname, '..', 'lambda-packages', 'dns_validated_certificate_handler', 'lib')),
101106
handler: 'index.certificateRequestHandler',
@@ -121,6 +126,7 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi
121126
properties: {
122127
DomainName: props.domainName,
123128
SubjectAlternativeNames: cdk.Lazy.list({ produce: () => props.subjectAlternativeNames }, { omitEmpty: true }),
129+
CertificateTransparencyLoggingPreference: certificateTransparencyLoggingPreference,
124130
HostedZoneId: this.hostedZoneId,
125131
Region: props.region,
126132
Route53Endpoint: props.route53Endpoint,

packages/@aws-cdk/aws-certificatemanager/test/certificate.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,45 @@ test('CertificateValidation.fromDnsMultiZone', () => {
322322
ValidationMethod: 'DNS',
323323
});
324324
});
325+
326+
describe('Transparency logging settings', () => {
327+
test('leaves transparency logging untouched by default', () => {
328+
const stack = new Stack();
329+
330+
new Certificate(stack, 'Certificate', {
331+
domainName: 'test.example.com',
332+
});
333+
334+
const certificateNodes = Template.fromStack(stack).findResources('AWS::CertificateManager::Certificate');
335+
expect(certificateNodes.Certificate4E7ABB08).toBeDefined();
336+
expect(certificateNodes.Certificate4E7ABB08.CertificateTransparencyLoggingPreference).toBeUndefined();
337+
});
338+
339+
test('can enable transparency logging', () => {
340+
const stack = new Stack();
341+
342+
new Certificate(stack, 'Certificate', {
343+
domainName: 'test.example.com',
344+
transparencyLoggingEnabled: true,
345+
});
346+
347+
Template.fromStack(stack).hasResourceProperties('AWS::CertificateManager::Certificate', {
348+
DomainName: 'test.example.com',
349+
CertificateTransparencyLoggingPreference: 'ENABLED',
350+
});
351+
});
352+
353+
test('can disable transparency logging', () => {
354+
const stack = new Stack();
355+
356+
new Certificate(stack, 'Certificate', {
357+
domainName: 'test.example.com',
358+
transparencyLoggingEnabled: false,
359+
});
360+
361+
Template.fromStack(stack).hasResourceProperties('AWS::CertificateManager::Certificate', {
362+
DomainName: 'test.example.com',
363+
CertificateTransparencyLoggingPreference: 'DISABLED',
364+
});
365+
});
366+
});

packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,31 @@ test('works with imported role', () => {
224224
Role: 'arn:aws:iam::account-id:role/role-name',
225225
});
226226
});
227+
228+
test('test transparency logging settings is passed to the custom resource', () => {
229+
const stack = new Stack();
230+
231+
const exampleDotComZone = new PublicHostedZone(stack, 'ExampleDotCom', {
232+
zoneName: 'example.com',
233+
});
234+
235+
new DnsValidatedCertificate(stack, 'Cert', {
236+
domainName: 'example.com',
237+
hostedZone: exampleDotComZone,
238+
transparencyLoggingEnabled: false,
239+
});
240+
241+
Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::CustomResource', {
242+
ServiceToken: {
243+
'Fn::GetAtt': [
244+
'CertCertificateRequestorFunction98FDF273',
245+
'Arn',
246+
],
247+
},
248+
DomainName: 'example.com',
249+
HostedZoneId: {
250+
Ref: 'ExampleDotCom4D1B83AA',
251+
},
252+
CertificateTransparencyLoggingPreference: 'DISABLED',
253+
});
254+
});

packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,18 @@ export class InterfaceVpcEndpoint extends VpcEndpoint implements IInterfaceVpcEn
520520

521521
/**
522522
* The DNS entries for the interface VPC endpoint.
523+
* Each entry is a combination of the hosted zone ID and the DNS name.
524+
* The entries are ordered as follows: regional public DNS, zonal public DNS, private DNS, and wildcard DNS.
525+
* This order is not enforced for AWS Marketplace services.
526+
*
527+
* The following is an example. In the first entry, the hosted zone ID is Z1HUB23UULQXV
528+
* and the DNS name is vpce-01abc23456de78f9g-12abccd3.ec2.us-east-1.vpce.amazonaws.com.
529+
*
530+
* ["Z1HUB23UULQXV:vpce-01abc23456de78f9g-12abccd3.ec2.us-east-1.vpce.amazonaws.com",
531+
* "Z1HUB23UULQXV:vpce-01abc23456de78f9g-12abccd3-us-east-1a.ec2.us-east-1.vpce.amazonaws.com",
532+
* "Z1C12344VYDITB0:ec2.us-east-1.amazonaws.com"]
533+
*
534+
* If you update the PrivateDnsEnabled or SubnetIds properties, the DNS entries in the list will change.
523535
* @attribute
524536
*/
525537
public readonly vpcEndpointDnsEntries: string[];

packages/aws-cdk/lib/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async function parseCommandLineArguments() {
5555
return yargs
5656
.env('CDK')
5757
.usage('Usage: cdk -a <cdk-app> COMMAND')
58-
.option('app', { type: 'string', alias: 'a', desc: 'REQUIRED: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js")', requiresArg: true })
58+
.option('app', { type: 'string', alias: 'a', desc: 'REQUIRED WHEN RUNNING APP: command-line for executing your app or a cloud assembly directory (e.g. "node bin/my-app.js"). Can also be specified in cdk.json or ~/.cdk.json', requiresArg: true })
5959
.option('build', { type: 'string', desc: 'Command-line for a pre-synth build' })
6060
.option('context', { type: 'array', alias: 'c', desc: 'Add contextual string parameter (KEY=VALUE)', nargs: 1, requiresArg: true })
6161
.option('plugin', { type: 'array', alias: 'p', desc: 'Name or path of a node package that extend the CDK features. Can be specified multiple times', nargs: 1 })

0 commit comments

Comments
 (0)