Skip to content

Commit

Permalink
Merge pull request #168 from salemdar/sqs-encryption
Browse files Browse the repository at this point in the history
Add Server-Side encryption option for SQS, fixes #138
  • Loading branch information
mnapoli committed Mar 18, 2022
2 parents 2b4b3cd + 574f12a commit e8c6808
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist
node_modules
.serverless/
package-lock.json
.idea
25 changes: 25 additions & 0 deletions docs/queue.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,31 @@ constructs:
delay: 60
```

### Encryption

Turn on server-side encryption for the queue.

You can set the `encryption` option to `kmsManaged` to use a SQS managed master key.

```yaml
constructs:
my-queue:
# ...
# Encryption will be enabled and managed by AWS
encryption: 'kmsManaged'
```

Or you can set it to `kms` and provide your own key via `encryptionKey` option.

```yaml
constructs:
my-queue:
# ...
# Encryption will be enabled and managed by AWS
encryption: 'kms'
encryptionKey: 'MySuperSecretKey'
```

### Batch size

```yaml
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@aws-cdk/aws-ec2": "^1.138",
"@aws-cdk/aws-events": "^1.138",
"@aws-cdk/aws-iam": "^1.138",
"@aws-cdk/aws-kms": "^1.138",
"@aws-cdk/aws-lambda": "^1.138",
"@aws-cdk/aws-s3": "^1.138",
"@aws-cdk/aws-sns": "^1.138",
Expand Down
31 changes: 30 additions & 1 deletion src/constructs/aws/Queue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Queue as CdkQueue } from "@aws-cdk/aws-sqs";
import { Key } from "@aws-cdk/aws-kms";
import { Queue as CdkQueue, QueueEncryption } from "@aws-cdk/aws-sqs";
import type { FromSchema } from "json-schema-to-ts";
import { Alarm, ComparisonOperator, Metric } from "@aws-cdk/aws-cloudwatch";
import { Subscription, SubscriptionProtocol, Topic } from "@aws-cdk/aws-sns";
Expand All @@ -7,6 +8,7 @@ import type { Construct as CdkConstruct } from "@aws-cdk/core";
import { CfnOutput, Duration } from "@aws-cdk/core";
import chalk from "chalk";
import type { PurgeQueueRequest, SendMessageRequest } from "aws-sdk/clients/sqs";
import { isNil } from "lodash";
import type { Ora } from "ora";
import ora from "ora";
import { spawnSync } from "child_process";
Expand Down Expand Up @@ -47,6 +49,8 @@ const QUEUE_DEFINITION = {
},
fifo: { type: "boolean" },
delay: { type: "number" },
encryption: { type: "string" },
encryptionKey: { type: "string" },
},
additionalProperties: false,
required: ["worker"],
Expand Down Expand Up @@ -153,13 +157,37 @@ export class Queue extends AwsConstruct {
delay = Duration.seconds(configuration.delay);
}

let encryption = undefined;
if (isNil(configuration.encryption) || configuration.encryption.length === 0) {
encryption = {};
} else if (configuration.encryption === "kmsManaged") {
encryption = { encryption: QueueEncryption.KMS_MANAGED };
} else if (configuration.encryption === "kms") {
if (isNil(configuration.encryptionKey) || configuration.encryptionKey.length === 0) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': 'encryptionKey' must be set if the 'encryption' is set to 'kms'`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
encryption = {
encryption: QueueEncryption.KMS,
encryptionMasterKey: new Key(this, configuration.encryptionKey),
};
} else {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': 'encryption' must be one of 'kms', 'kmsManaged', null, '${configuration.encryption}' given.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}

const baseName = `${this.provider.stackName}-${id}`;

const dlq = new CdkQueue(this, "Dlq", {
queueName: configuration.fifo === true ? `${baseName}-dlq.fifo` : `${baseName}-dlq`,
// 14 days is the maximum, we want to keep these messages for as long as possible
retentionPeriod: Duration.days(14),
fifo: configuration.fifo,
...encryption,
});

this.queue = new CdkQueue(this, "Queue", {
Expand All @@ -172,6 +200,7 @@ export class Queue extends AwsConstruct {
fifo: configuration.fifo,
deliveryDelay: delay,
contentBasedDeduplication: configuration.fifo,
...encryption,
});

const alarmEmail = configuration.alarm;
Expand Down
65 changes: 65 additions & 0 deletions test/unit/queues.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,71 @@ describe("queues", () => {
});
});

it("allows changing the encryption to kmsManaged", async () => {
const { cfTemplate, computeLogicalId } = await runServerless({
fixture: "queues",
configExt: merge({}, pluginConfigExt, {
constructs: {
emails: {
encryption: "kmsManaged",
},
},
}),
command: "package",
});
expect(cfTemplate.Resources[computeLogicalId("emails", "Queue")]).toMatchObject({
Properties: {
KmsMasterKeyId: "alias/aws/sqs",
},
});
});

it("allows changing the encryption to kms", async () => {
const { cfTemplate, computeLogicalId } = await runServerless({
fixture: "queues",
configExt: merge({}, pluginConfigExt, {
constructs: {
emails: {
encryption: "kms",
encryptionKey: "MyKey",
},
},
}),
command: "package",
});
expect(cfTemplate.Resources[computeLogicalId("emails", "Queue")]).toMatchObject({
Properties: {
KmsMasterKeyId: {
"Fn::GetAtt": [computeLogicalId("emails", "MyKey"), "Arn"],
},
},
});
});

it("should throw an error if encryption is 'kms' but encryptionKey is missing", async () => {
expect.assertions(2);

try {
await runServerless({
fixture: "queues",
configExt: merge({}, pluginConfigExt, {
constructs: {
emails: {
encryption: "kms",
},
},
}),
command: "package",
});
} catch (error) {
expect(error).toBeInstanceOf(ServerlessError);
expect(error).toHaveProperty(
"message",
"Invalid configuration in 'constructs.emails': 'encryptionKey' must be set if the 'encryption' is set to 'kms'"
);
}
});

it("should throw an error if the delay is invalid", async () => {
expect.assertions(2);

Expand Down

0 comments on commit e8c6808

Please sign in to comment.