From 90e288dd00aba2ce451059bd5d7d0461d76ab64a Mon Sep 17 00:00:00 2001 From: Shiv Lakshminarayan Date: Sat, 28 Mar 2020 23:29:40 -0700 Subject: [PATCH 1/4] feat(kinesis): stream encryption with the Kinesis master key Adds a `StreamEncryption` option to specify that encryption should be enabled and managed by Kinesis. Closes #751 --- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 17 ++++++++++-- .../@aws-cdk/aws-kinesis/test/test.stream.ts | 27 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 09cc92e05e722..0b0d9323ff75a 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -201,7 +201,7 @@ export interface StreamProps { * If you choose KMS, you can specify a KMS key via `encryptionKey`. If * encryption key is not specified, a key will automatically be created. * - * @default Unencrypted + * @default StreamEncryption.UNENCRYPTED */ readonly encryption?: StreamEncryption; @@ -210,7 +210,7 @@ export interface StreamProps { * * The 'encryption' property must be set to "Kms". * - * @default If encryption is set to "Kms" and this property is undefined, a + * @default - If encryption is set to "KMS" and this property is undefined, a * new KMS key will be created and associated with this stream. */ readonly encryptionKey?: kms.IKey; @@ -306,6 +306,14 @@ export class Stream extends StreamBase { return { streamEncryption: undefined, encryptionKey: undefined }; } + if (encryptionType === StreamEncryption.KINESIS_MANAGED) { + const encryption = { encryptionType: 'KMS', keyId: 'alias/aws/kinesis'}; + return { + streamEncryption: encryption, + encryptionKey: undefined + }; + } + if (encryptionType === StreamEncryption.KMS) { const encryptionKey = props.encryptionKey || new kms.Key(this, 'Key', { description: `Created by ${this.node.path}` @@ -336,4 +344,9 @@ export enum StreamEncryption { * If `encryptionKey` is specified, this key will be used, otherwise, one will be defined. */ KMS = 'KMS', + + /** + * Server-side encryption with a master key managed by Amazon Kinesis + */ + KINESIS_MANAGED = 'MANAGED' } diff --git a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts index 40c37a49296ea..bfe8327e1ff3d 100644 --- a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts +++ b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts @@ -97,6 +97,33 @@ export = { test.done(); }, + + 'uses Kinesis master key if KINESIS_MANAGED encryption type is provided'(test: Test) { + const stack = new Stack(); + + new Stream(stack, 'MyStream', { + encryption: StreamEncryption.KINESIS_MANAGED + }); + + expect(stack).toMatch({ + "Resources": { + "MyStream5C050E93": { + "Type": "AWS::Kinesis::Stream", + "Properties": { + "ShardCount": 1, + "RetentionPeriodHours": 24, + "StreamEncryption": { + "EncryptionType": "KMS", + "KeyId": "alias/aws/kinesis" + } + } + } + } + }); + + test.done(); + }, + "auto-creates KMS key if encryption type is KMS but no key is provided"(test: Test) { const stack = new Stack(); From 38c88e4ec56bec16b25061b3e58731e4ba929f6b Mon Sep 17 00:00:00 2001 From: Shiv Lakshminarayan Date: Mon, 30 Mar 2020 09:23:25 -0700 Subject: [PATCH 2/4] rename KINESIS_MANAGED to MANAGED and update README --- packages/@aws-cdk/aws-kinesis/README.md | 11 ++++++++++- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 2 +- packages/@aws-cdk/aws-kinesis/test/test.stream.ts | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-kinesis/README.md b/packages/@aws-cdk/aws-kinesis/README.md index 2e7e0e6a26176..28efaf7d9167a 100644 --- a/packages/@aws-cdk/aws-kinesis/README.md +++ b/packages/@aws-cdk/aws-kinesis/README.md @@ -59,7 +59,16 @@ Streams are not encrypted by default. [Stream encryption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesis-stream-streamencryption.html) enables server-side encryption using an AWS KMS key for a specified stream. -You can enable encryption on your streams by specifying the `encryption` property. A KMS key will be created for you and associated with the stream. +You can enable encryption on your stream with the master key owned by Kinesis Data Streams by specifying the `encryption` property. + +```ts +const stream = new Stream(this, 'MyEncryptedStream', { + encryption: StreamEncryption.MANAGED +}); +``` + +You can enable encryption on your stream with a user-managed key by specifying the `encryption` property. +A KMS key will be created for you and associated with the stream. ```ts const stream = new Stream(this, "MyEncryptedStream", { diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 0b0d9323ff75a..7f5341550c414 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -348,5 +348,5 @@ export enum StreamEncryption { /** * Server-side encryption with a master key managed by Amazon Kinesis */ - KINESIS_MANAGED = 'MANAGED' + MANAGED = 'MANAGED' } diff --git a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts index 70f52c57733b0..f0049553dd35b 100644 --- a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts +++ b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts @@ -98,11 +98,11 @@ export = { test.done(); }, - 'uses Kinesis master key if KINESIS_MANAGED encryption type is provided'(test: Test) { + 'uses Kinesis master key if MANAGED encryption type is provided'(test: Test) { const stack = new Stack(); new Stream(stack, 'MyStream', { - encryption: StreamEncryption.KINESIS_MANAGED + encryption: StreamEncryption.MANAGED }); expect(stack).toMatch({ From 93f3a3af8ce46c50d4115cab2dd66a4544b1b98f Mon Sep 17 00:00:00 2001 From: Shiv Lakshminarayan Date: Mon, 30 Mar 2020 09:35:32 -0700 Subject: [PATCH 3/4] KINESIS_MANAGED -> MANAGED --- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 7f5341550c414..91a3b6d173995 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -306,7 +306,7 @@ export class Stream extends StreamBase { return { streamEncryption: undefined, encryptionKey: undefined }; } - if (encryptionType === StreamEncryption.KINESIS_MANAGED) { + if (encryptionType === StreamEncryption.MANAGED) { const encryption = { encryptionType: 'KMS', keyId: 'alias/aws/kinesis'}; return { streamEncryption: encryption, From ac1b7307d388976ef4495485a954f85b031f407f Mon Sep 17 00:00:00 2001 From: Shiv Lakshminarayan Date: Mon, 30 Mar 2020 14:37:01 -0700 Subject: [PATCH 4/4] default to KMS if encryptionKey is specified --- packages/@aws-cdk/aws-kinesis/README.md | 6 ++-- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 7 +++-- .../@aws-cdk/aws-kinesis/test/test.stream.ts | 31 ++++++++++++++++++- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-kinesis/README.md b/packages/@aws-cdk/aws-kinesis/README.md index 28efaf7d9167a..d805856111752 100644 --- a/packages/@aws-cdk/aws-kinesis/README.md +++ b/packages/@aws-cdk/aws-kinesis/README.md @@ -62,7 +62,7 @@ server-side encryption using an AWS KMS key for a specified stream. You can enable encryption on your stream with the master key owned by Kinesis Data Streams by specifying the `encryption` property. ```ts -const stream = new Stream(this, 'MyEncryptedStream', { +new Stream(this, 'MyEncryptedStream', { encryption: StreamEncryption.MANAGED }); ``` @@ -71,7 +71,7 @@ You can enable encryption on your stream with a user-managed key by specifying t A KMS key will be created for you and associated with the stream. ```ts -const stream = new Stream(this, "MyEncryptedStream", { +new Stream(this, "MyEncryptedStream", { encryption: StreamEncryption.KMS }); ``` @@ -83,7 +83,7 @@ import * as kms from "@aws-cdk/aws-kms"; const key = new kms.Key(this, "MyKey"); -const stream = new Stream(this, "MyEncryptedStream", { +new Stream(this, "MyEncryptedStream", { encryption: StreamEncryption.KMS, encryptionKey: key }); diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 91a3b6d173995..f6a683449c09d 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -201,7 +201,7 @@ export interface StreamProps { * If you choose KMS, you can specify a KMS key via `encryptionKey`. If * encryption key is not specified, a key will automatically be created. * - * @default StreamEncryption.UNENCRYPTED + * @default - StreamEncryption.KMS if encryptionKey is specified, or StreamEncryption.UNENCRYPTED otherwise */ readonly encryption?: StreamEncryption; @@ -294,8 +294,9 @@ export class Stream extends StreamBase { encryptionKey?: kms.IKey } { - // default to unencrypted. - const encryptionType = props.encryption || StreamEncryption.UNENCRYPTED; + // default based on whether encryption key is specified + const encryptionType = props.encryption ?? + (props.encryptionKey ? StreamEncryption.KMS : StreamEncryption.UNENCRYPTED); // if encryption key is set, encryption must be set to KMS. if (encryptionType !== StreamEncryption.KMS && props.encryptionKey) { diff --git a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts index f0049553dd35b..b229bc669808e 100644 --- a/packages/@aws-cdk/aws-kinesis/test/test.stream.ts +++ b/packages/@aws-cdk/aws-kinesis/test/test.stream.ts @@ -1,4 +1,4 @@ -import { expect } from '@aws-cdk/assert'; +import { expect, haveResource } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { App, Duration, Stack } from '@aws-cdk/core'; @@ -99,12 +99,15 @@ export = { }, 'uses Kinesis master key if MANAGED encryption type is provided'(test: Test) { + // GIVEN const stack = new Stack(); + // WHEN new Stream(stack, 'MyStream', { encryption: StreamEncryption.MANAGED }); + // THEN expect(stack).toMatch({ "Resources": { "MyStream5C050E93": { @@ -124,6 +127,32 @@ export = { test.done(); }, + 'if a KMS key is supplied, use KMS as the encryption type'(test: Test) { + // GIVEN + const stack = new Stack(); + const key = new kms.Key(stack, 'myKey'); + + // WHEN + new Stream(stack, 'myStream', { + encryptionKey: key + }); + + // THEN + expect(stack).to(haveResource('AWS::Kinesis::Stream', { + ShardCount: 1, + RetentionPeriodHours: 24, + StreamEncryption: { + EncryptionType: 'KMS', + KeyId: { + 'Fn::GetAtt': ['myKey441A1E73', 'Arn'] + } + } + }) + ); + + test.done(); + }, + "auto-creates KMS key if encryption type is KMS but no key is provided"(test: Test) { const stack = new Stack();