Skip to content

Commit

Permalink
feat(ec2): support KMS keys for block device mappings for both instan…
Browse files Browse the repository at this point in the history
…ces and launch templates (#18326)

This pullrequest will add the optional ```kmsKeyId``` property to the ```EbsDeviceOptions``` Interface. Whit this, it will be possible to specify the kmsKeyId used for encrypting the ebs volumes when launching instances. At the moment I already use this via an escape hatch in my projects, but it's not that handy as the block device mapping is an array.

I don't like to only specify a kmsKeyId (= ARN) but accepting an ```kms.IKey``` in the properties would be a bigger change.

fixes: #18309
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
markussiebert authored Feb 1, 2022
1 parent 71601c1 commit 17dbe5f
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 3 deletions.
31 changes: 31 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,37 @@ new ec2.Instance(this, 'Instance', {

```

It is also possible to encrypt the block devices. In this example we will create an customer managed key encrypted EBS-backed root device:

```ts
import { Key } from '@aws-cdk/aws-kms';

declare const vpc: ec2.Vpc;
declare const instanceType: ec2.InstanceType;
declare const machineImage: ec2.IMachineImage;

const kmsKey = new Key(this, 'KmsKey')

new ec2.Instance(this, 'Instance', {
vpc,
instanceType,
machineImage,

// ...

blockDevices: [
{
deviceName: '/dev/sda1',
volume: ec2.BlockDeviceVolume.ebs(50, {
encrypted: true,
kmsKey: kmsKey,
}),
},
],
});

```

### Volumes

Whereas a `BlockDeviceVolume` is an EBS volume that is created and destroyed as part of the creation and destruction of a specific instance. A `Volume` is for when you want an EBS volume separate from any particular instance. A `Volume` is an EBS block device that can be attached to, or detached from, any instance at any time. Some types of `Volume`s can also be attached to multiple instances at the same time to allow you to have shared storage between those instances.
Expand Down
23 changes: 21 additions & 2 deletions packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
return blockDevices.map<RT>(({ deviceName, volume, mappingEnabled }): RT => {
const { virtualName, ebsDevice: ebs } = volume;

let finalEbs: CfnLaunchTemplate.EbsProperty | CfnInstance.EbsProperty | undefined;

if (ebs) {
const { iops, volumeType } = ebs;

const { iops, volumeType, kmsKey, ...rest } = ebs;

if (!iops) {
if (volumeType === EbsDeviceVolumeType.IO1) {
Expand All @@ -34,9 +37,25 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
} else if (volumeType !== EbsDeviceVolumeType.IO1) {
Annotations.of(construct).addWarning('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1');
}

/**
* Because the Ebs properties of the L2 Constructs do not match the Ebs properties of the Cfn Constructs,
* we have to do some transformation and handle all destructed properties
*/

finalEbs = {
...rest,
iops,
volumeType,
kmsKeyId: kmsKey?.keyArn,
};

} else {
finalEbs = undefined;
}


const noDevice = mappingEnabled === false ? noDeviceValue : undefined;
return { deviceName, ebs, virtualName, noDevice } as any;
return { deviceName, ebs: finalEbs, virtualName, noDevice } as any;
});
}
13 changes: 12 additions & 1 deletion packages/@aws-cdk/aws-ec2/lib/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ export interface EbsDeviceOptions extends EbsDeviceOptionsBase {
* @default false
*/
readonly encrypted?: boolean;

/**
* The ARN of the AWS Key Management Service (AWS KMS) CMK used for encryption.
*
* You have to ensure that the KMS CMK has the correct permissions to be used by the service launching the ec2 instances.
*
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html#ebs-encryption-requirements
*
* @default - If encrypted is true, the default aws/ebs KMS key will be used.
*/
readonly kmsKey?: IKey;
}

/**
Expand All @@ -108,7 +119,7 @@ export interface EbsDeviceSnapshotOptions extends EbsDeviceOptionsBase {
/**
* Properties of an EBS block device
*/
export interface EbsDeviceProps extends EbsDeviceSnapshotOptions {
export interface EbsDeviceProps extends EbsDeviceSnapshotOptions, EbsDeviceOptions {
/**
* The snapshot ID of the volume to use
*
Expand Down
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/instance.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import { Match, Template } from '@aws-cdk/assertions';
import { Key } from '@aws-cdk/aws-kms';
import { Asset } from '@aws-cdk/aws-s3-assets';
import { StringParameter } from '@aws-cdk/aws-ssm';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
Expand Down Expand Up @@ -184,6 +185,7 @@ describe('instance', () => {
describe('blockDeviceMappings', () => {
test('can set blockDeviceMappings', () => {
// WHEN
const kmsKey = new Key(stack, 'EbsKey');
new Instance(stack, 'Instance', {
vpc,
machineImage: new AmazonLinuxImage(),
Expand All @@ -197,6 +199,16 @@ describe('instance', () => {
volumeType: EbsDeviceVolumeType.IO1,
iops: 5000,
}),
}, {
deviceName: 'ebs-cmk',
mappingEnabled: true,
volume: BlockDeviceVolume.ebs(15, {
deleteOnTermination: true,
encrypted: true,
kmsKey: kmsKey,
volumeType: EbsDeviceVolumeType.IO1,
iops: 5000,
}),
}, {
deviceName: 'ebs-snapshot',
mappingEnabled: false,
Expand Down Expand Up @@ -224,6 +236,22 @@ describe('instance', () => {
VolumeType: 'io1',
},
},
{
DeviceName: 'ebs-cmk',
Ebs: {
DeleteOnTermination: true,
Encrypted: true,
KmsKeyId: {
'Fn::GetAtt': [
'EbsKeyD3FEE551',
'Arn',
],
},
Iops: 5000,
VolumeSize: 15,
VolumeType: 'io1',
},
},
{
DeviceName: 'ebs-snapshot',
Ebs: {
Expand Down
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/launch-template.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Role,
ServicePrincipal,
} from '@aws-cdk/aws-iam';
import { Key } from '@aws-cdk/aws-kms';
import {
App,
Duration,
Expand Down Expand Up @@ -254,6 +255,7 @@ describe('LaunchTemplate', () => {

test('Given blockDeviceMapping', () => {
// GIVEN
const kmsKey = new Key(stack, 'EbsKey');
const blockDevices: BlockDevice[] = [
{
deviceName: 'ebs',
Expand All @@ -264,6 +266,16 @@ describe('LaunchTemplate', () => {
volumeType: EbsDeviceVolumeType.IO1,
iops: 5000,
}),
}, {
deviceName: 'ebs-cmk',
mappingEnabled: true,
volume: BlockDeviceVolume.ebs(15, {
deleteOnTermination: true,
encrypted: true,
kmsKey: kmsKey,
volumeType: EbsDeviceVolumeType.IO1,
iops: 5000,
}),
}, {
deviceName: 'ebs-snapshot',
mappingEnabled: false,
Expand Down Expand Up @@ -297,6 +309,22 @@ describe('LaunchTemplate', () => {
VolumeType: 'io1',
},
},
{
DeviceName: 'ebs-cmk',
Ebs: {
DeleteOnTermination: true,
Encrypted: true,
KmsKeyId: {
'Fn::GetAtt': [
'EbsKeyD3FEE551',
'Arn',
],
},
Iops: 5000,
VolumeSize: 15,
VolumeType: 'io1',
},
},
{
DeviceName: 'ebs-snapshot',
Ebs: {
Expand Down

0 comments on commit 17dbe5f

Please sign in to comment.