Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-cloudfront/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,41 @@ new CloudFrontWebDistribution(stack, 'ADistribution', {
],
});
```

## KeyGroup & PublicKey API

Now you can create a key group to use with CloudFront signed URLs and signed cookies. You can add public keys to use with CloudFront features such as signed URLs, signed cookies, and field-level encryption.

The following example command uses OpenSSL to generate an RSA key pair with a length of 2048 bits and save to the file named `private_key.pem`.

```bash
openssl genrsa -out private_key.pem 2048
```

The resulting file contains both the public and the private key. The following example command extracts the public key from the file named `private_key.pem` and stores it in `public_key.pem`.

```bash
openssl rsa -pubout -in private_key.pem -out public_key.pem
```

Note: Don't forget to copy/paste the contents of `public_key.pem` file including `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----` lines into `encodedKey` parameter when creating a `PublicKey`.

Example:

```ts
new cloudfront.KeyGroup(stack, 'MyKeyGroup', {
items: [
new cloudfront.PublicKey(stack, 'MyPublicKey', {
encodedKey: '...', // contents of public_key.pem file
// comment: 'Key is expiring on ...',
}),
],
// comment: 'Key group containing public keys ...',
});
```

See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html

See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs

See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-cloudfront/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export * from './cache-policy';
export * from './distribution';
export * from './geo-restriction';
export * from './key-group';
export * from './origin';
export * from './origin-access-identity';
export * from './origin-request-policy';
export * from './public-key';
export * from './web-distribution';

export * as experimental from './experimental';
Expand Down
82 changes: 82 additions & 0 deletions packages/@aws-cdk/aws-cloudfront/lib/key-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { IResource, Lazy, Names, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnKeyGroup } from './cloudfront.generated';
import { IPublicKey } from './public-key';

/**
* Represents a Key Group
*/
export interface IKeyGroup extends IResource {
/**
* The ID of the key group.
* @attribute
*/
readonly keyGroupId: string;
}

/**
* Properties for creating a Public Key
*/
export interface KeyGroupProps {
/**
* A name to identify the key group.
* @default - generated from the `id`
*/
readonly keyGroupName?: string;

/**
* A comment to describe the key group.
* @default - no comment
*/
readonly comment?: string;

/**
* A list of the identifiers of the public keys in the key group.
*/
readonly items: IPublicKey[];
}

/**
* A Key Group configuration
*
* @resource AWS::CloudFront::KeyGroup
*/
export class KeyGroup extends Resource implements IKeyGroup {

/** Imports a Key Group from its id. */
public static fromKeyGroupId(scope: Construct, id: string, keyGroupId: string): IKeyGroup {
return new class extends Resource implements IKeyGroup {
public readonly keyGroupId = keyGroupId;
}(scope, id);
}

public readonly keyGroupId: string;

constructor(scope: Construct, id: string, props: KeyGroupProps) {
super(scope, id, {
physicalName: props.keyGroupName ??
Lazy.string({ produce: () => this.generateName() }),
});

const resource = new CfnKeyGroup(this, 'Resource', {
keyGroupConfig: {
name: this.physicalName,
comment: props.comment,
items: this.getKeyIdentifiers(props.items),
},
});
this.keyGroupId = resource.ref;
}

private getKeyIdentifiers(items: IPublicKey[]): string[] {
return items.map(key => key.publicKeyId);
}

private generateName(): string {
const name = Names.uniqueId(this);
if (name.length > 80) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you find this documented somewhere, or find it via trial & error, or is this just a conservative guess?

Copy link
Contributor Author

@robertd robertd Feb 1, 2021

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

My main point was more around the length. If the key group can have a 255-character (or 1024-character) name, should we artificially restrict to 80? I suppose this is fine for a first take; Names.uniqueId will rarely return a name this long anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should these helper functions (shorteners) be part of Names perhaps?

return name.substring(0, 40) + name.substring(name.length - 40);
}
return name;
}
}
81 changes: 81 additions & 0 deletions packages/@aws-cdk/aws-cloudfront/lib/public-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { IResource, Lazy, Names, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnPublicKey } from './cloudfront.generated';

/**
* Represents a Public Key
*/
export interface IPublicKey extends IResource {
/**
* The ID of the key group.
* @attribute
*/
readonly publicKeyId: string;
}

/**
* Properties for creating a Public Key
*/
export interface PublicKeyProps {
/**
* A name to identify the public key.
* @default - generated from the `id`
*/
readonly publicKeyName?: string;

/**
* A comment to describe the public key.
* @default - no comment
*/
readonly comment?: string;

/**
* The public key that you can use with signed URLs and signed cookies, or with field-level encryption.
* @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
* @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/field-level-encryption.html
*/
readonly encodedKey: string;
}

/**
* A Public Key Configuration
*
* @resource AWS::CloudFront::PublicKey
*/
export class PublicKey extends Resource implements IPublicKey {

/** Imports a Public Key from its id. */
public static fromPublicKeyId(scope: Construct, id: string, publicKeyId: string): IPublicKey {
return new class extends Resource implements IPublicKey {
public readonly publicKeyId = publicKeyId;
}(scope, id);
}

public readonly publicKeyId: string;

constructor(scope: Construct, id: string, props: PublicKeyProps) {
super(scope, id, {
physicalName: props.publicKeyName ??
Lazy.string({ produce: () => this.generateName() }),
});

const resource = new CfnPublicKey(this, 'Resource', {
publicKeyConfig: {
name: this.physicalName,
callerReference: this.node.addr,
encodedKey: props.encodedKey,
comment: props.comment,
},
});

this.publicKeyId = resource.ref;
}

private generateName(): string {
const name = Names.uniqueId(this);
if (name.length > 80) {
return name.substring(0, 40) + name.substring(name.length - 40);
}
return name;
}
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-cloudfront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@
"resource-attribute:@aws-cdk/aws-cloudfront.CachePolicy.cachePolicyLastModifiedTime",
"construct-interface-extends-iconstruct:@aws-cdk/aws-cloudfront.IOriginRequestPolicy",
"resource-interface-extends-resource:@aws-cdk/aws-cloudfront.IOriginRequestPolicy",
"resource-attribute:@aws-cdk/aws-cloudfront.OriginRequestPolicy.originRequestPolicyLastModifiedTime"
"resource-attribute:@aws-cdk/aws-cloudfront.OriginRequestPolicy.originRequestPolicyLastModifiedTime",
"resource-attribute:@aws-cdk/aws-cloudfront.KeyGroup.keyGroupLastModifiedTime",
"resource-attribute:@aws-cdk/aws-cloudfront.PublicKey.publicKeyCreatedTime"
]
},
"awscdkio": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"Resources": {
"AwesomePublicKeyED3E7F55": {
"Type": "AWS::CloudFront::PublicKey",
"Properties": {
"PublicKeyConfig": {
"CallerReference": "c88e460888c5762c9c47ac0cdc669370d787fb2d9f",
"EncodedKey": "-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS\n JAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa\n dlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj\n 6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e\n 0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD\n /3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx\n NQIDAQAB\n -----END PUBLIC KEY-----\n ",
"Name": "awscdkcloudfrontcustomAwesomePublicKey0E83393B"
}
}
},
"AwesomeKeyGroup3EF8348B": {
"Type": "AWS::CloudFront::KeyGroup",
"Properties": {
"KeyGroupConfig": {
"Items": [
{
"Ref": "AwesomePublicKeyED3E7F55"
}
],
"Name": "awscdkcloudfrontcustomAwesomeKeyGroup73FD4DCA"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as cdk from '@aws-cdk/core';
import * as cloudfront from '../lib';

const app = new cdk.App();

const stack = new cdk.Stack(app, 'aws-cdk-cloudfront-custom');

new cloudfront.KeyGroup(stack, 'AwesomeKeyGroup', {
items: [
new cloudfront.PublicKey(stack, 'AwesomePublicKey', {
encodedKey: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS
JAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa
dlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj
6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e
0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD
/3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx
NQIDAQAB
-----END PUBLIC KEY-----
`,
}),
],
});

app.synth();
Loading