Skip to content

Commit

Permalink
upgrade cdk and default encryption for compliance (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goodwin authored Oct 23, 2023
1 parent 12adb34 commit 38eb3b0
Show file tree
Hide file tree
Showing 36 changed files with 662 additions and 230 deletions.
2 changes: 1 addition & 1 deletion apps/test-app-sst/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@serverless-stack/core": "^1.18.4",
"@serverless-stack/resources": "^1.18.4",
"@tsconfig/node18": "^1.0.1",
"aws-cdk-lib": "2.80.0",
"aws-cdk-lib": "2.102.0",
"chalk": "^5.2.0",
"fs-extra": "^11.1.0",
"typescript": "^5",
Expand Down
4 changes: 2 additions & 2 deletions apps/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
},
"dependencies": {
"@eventual/aws-cdk": "workspace:^",
"aws-cdk-lib": "2.80.0",
"aws-cdk-lib": "2.102.0",
"constructs": "10.1.154"
},
"devDependencies": {
"@eventual/cli": "workspace:^",
"@types/jest": "^29.5.1",
"@types/node": "^18",
"aws-cdk": "2.80.0",
"aws-cdk": "2.102.0",
"esbuild": "^0.17.4",
"jest": "^29",
"test-app-runtime": "workspace:^",
Expand Down
2 changes: 1 addition & 1 deletion apps/test-app/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ new ServiceDashboard(stack, "BenchmarkDashboard", {
const bench = new NodejsFunction(stack, "BenchmarkFunc", {
entry: require.resolve("test-app-runtime/lib/bench.js"),
handler: "handle",
runtime: Runtime.NODEJS_16_X,
runtime: Runtime.NODEJS_LATEST,
architecture: Architecture.ARM_64,
bundling: {
// https://github.com/aws/aws-cdk/issues/21329#issuecomment-1212336356
Expand Down
5 changes: 5 additions & 0 deletions apps/tests/aws-runtime-cdk/eventual.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectType": "aws-cdk",
"synth": "npx cdk synth --app \"ts-node --esm ./src/app.mts\"",
"deploy": "../aws-runtime/scripts/deploy"
}
10 changes: 6 additions & 4 deletions apps/tests/aws-runtime-cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"version": "0.0.0",
"main": "lib/index.js",
"scripts": {
"cdk": "cdk"
"cdk": "cdk",
"nag": "ts-node-esm ./scripts/report-violations.ts"
},
"dependencies": {
"@aws-cdk/aws-apigatewayv2-alpha": "^2.80.0-alpha.0",
"aws-cdk-lib": "2.80.0",
"@aws-cdk/aws-apigatewayv2-alpha": "^2.102.0-alpha.0",
"aws-cdk-lib": "2.102.0",
"cdk-nag": "^2.27.164",
"constructs": "10.1.154"
},
"devDependencies": {
Expand All @@ -19,7 +21,7 @@
"@eventual/core": "workspace:^",
"@types/jest": "^29.5.1",
"@types/node": "^18",
"aws-cdk": "^2.80.0",
"aws-cdk": "^2.102.0",
"esbuild": "^0.17.4",
"jest": "^29",
"tests-runtime": "workspace:^",
Expand Down
32 changes: 32 additions & 0 deletions apps/tests/aws-runtime-cdk/scripts/report-violations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import hipaa from "../cdk.out/HIPAA.Security-eventual-tests-NagReport.json" assert { type: "json" };
import awsSolutions from "../cdk.out/HIPAA.Security-eventual-tests-NagReport.json" assert { type: "json" };

type Report = typeof hipaa | typeof awsSolutions;

report([hipaa]);

function report(report: Report[]) {
const errors = report.flatMap((report) => report.lines);

const nonCompliant = errors.filter(
(line) => line.compliance === "Non-Compliant"
);
const compliant = errors.filter((line) => line.compliance === "Compliant");
type Violation = (typeof nonCompliant)[number];

console.log("# Non-compliant");
printErrors(nonCompliant);
console.log("# Compliant");
printErrors(compliant);

function printErrors(error: Violation[]) {
const uniqueErrors = Array.from(
new Set(error.map((line) => format(line.ruleInfo)))
);
console.log(uniqueErrors.sort().join("\n"));
}

function format(line: string) {
return `- [ ] ${line.replace(/- \(Control.*/g, "")}`;
}
}
56 changes: 43 additions & 13 deletions apps/tests/aws-runtime-cdk/src/app.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
import * as eventual from "@eventual/aws-cdk";
import { DebugDashboard, ServiceDashboard } from "@eventual/aws-cdk";
import { LogLevel } from "@eventual/core";
import { App, CfnOutput, CfnResource, Stack } from "aws-cdk-lib";
import { App, CfnOutput, Stack } from "aws-cdk-lib";
import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";
import {
ArnPrincipal,
Expand All @@ -13,11 +13,20 @@ import {
} from "aws-cdk-lib/aws-iam";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import { Queue } from "aws-cdk-lib/aws-sqs";
import { Duration } from "aws-cdk-lib/core";
import { Aspects, Duration } from "aws-cdk-lib/core";
import {
AwsSolutionsChecks,
HIPAASecurityChecks,
NagPack,
NagPackProps,
NagReportFormat,
} from "cdk-nag";
import { createRequire as topLevelCreateRequire } from "module";
import path from "path";
import { ChaosExtension } from "./chaos-extension.js";

import { ComplianceStandard } from "@eventual/aws-cdk";
import { CfnPipe } from "aws-cdk-lib/aws-pipes";
import type * as testServiceRuntime from "tests-runtime";

const require = topLevelCreateRequire(import.meta.url);
Expand Down Expand Up @@ -56,6 +65,9 @@ const testService = new eventual.Service<typeof testServiceRuntime>(
TEST_QUEUE_URL: testQueue.queueUrl,
TEST_TABLE_NAME: testTable.tableName,
},
compliance: {
standards: [ComplianceStandard.HIPAA],
},
system: {
workflowService: {
logLevel: LogLevel.DEBUG,
Expand All @@ -79,6 +91,27 @@ const testService = new eventual.Service<typeof testServiceRuntime>(
}
);

// these run linting rules on the CDK code and should all pass to enforce compliance
enableNagPack(AwsSolutionsChecks);
enableNagPack(HIPAASecurityChecks);
function enableNagPack<P extends NagPackProps>(
Pack: new (props: P) => NagPack,
props?: P
) {
// TODO: enable once we comply with all policies and tests pass in deployment
const nag = false;
if (nag) {
Aspects.of(testService).add(
new Pack({
reports: true,
reportFormats: [NagReportFormat.CSV, NagReportFormat.JSON],
verbose: true,
...props,
} as P)
);
}
}

testService.grantInvokeHttpServiceApi(role);
testService.system.accessRole.grantAssumeRole(role);
eventual.Service.grantDescribeParameters(stack, role);
Expand Down Expand Up @@ -128,18 +161,15 @@ asyncWriterFunction.grantInvoke(pipeRole);
testService.grantInvokeHttpServiceApi(asyncWriterFunction);

// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-pipes-pipe.html
new CfnResource(stack, "pipe", {
type: "AWS::Pipes::Pipe",
properties: {
TargetParameters: {
InputTemplate:
'{"token": "<$.body.token>","type":"<$.body.type>","ingestionTime":"<aws.pipes.event.ingestion-time>"}',
},
Name: stack.stackName + "_pipe",
RoleArn: pipeRole.roleArn,
Source: testQueue.queueArn,
Target: asyncWriterFunction.functionArn,
new CfnPipe(stack, "pipe", {
targetParameters: {
inputTemplate:
'{"token": "<$.body.token>","type":"<$.body.type>","ingestionTime":"<aws.pipes.event.ingestion-time>"}',
},
name: stack.stackName + "_pipe",
roleArn: pipeRole.roleArn,
source: testQueue.queueArn,
target: asyncWriterFunction.functionArn,
});

new ServiceDashboard(stack, "dashboard", {
Expand Down
2 changes: 1 addition & 1 deletion apps/tests/aws-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@types/jest": "^29.5.1",
"@types/node": "^18",
"@types/ws": "^8.5.5",
"aws-cdk": "^2.80.0",
"aws-cdk": "^2.102.0",
"esbuild": "^0.17.4",
"jest": "^29",
"node-fetch": "^3.3.0",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"test:local": "pnpm -r --filter tests-runtime run test:local-start",
"typecheck": "tsc -b",
"watch": "tsc -b -w",
"export": "turbo run export"
"export": "turbo run export",
"nag": "pnpm --filter tests-cdk run nag"
},
"devDependencies": {
"@types/jest": "^29.5.1",
Expand Down
18 changes: 9 additions & 9 deletions packages/@eventual/aws-cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@
"aws4": "^1.12.0"
},
"peerDependencies": {
"@aws-cdk/aws-apigatewayv2-alpha": "^2.80.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.80.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.80.0-alpha.0",
"aws-cdk-lib": "^2.80.0",
"@aws-cdk/aws-apigatewayv2-alpha": "^2.102.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.102.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.102.0-alpha.0",
"aws-cdk-lib": "^2.102.0",
"constructs": "^10.0.0",
"esbuild": ">=0.16.x <1.0.0"
},
"devDependencies": {
"@aws-cdk/aws-apigatewayv2-alpha": "2.80.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "2.80.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "2.80.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-alpha": "2.102.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-authorizers-alpha": "2.102.0-alpha.0",
"@aws-cdk/aws-apigatewayv2-integrations-alpha": "2.102.0-alpha.0",
"@types/aws-lambda": "8.10.115",
"@types/aws4": "^1.11.2",
"@types/jest": "^29.5.1",
"@types/node": "^18",
"aws-cdk": "2.80.0",
"aws-cdk-lib": "2.80.0",
"aws-cdk": "2.102.0",
"aws-cdk-lib": "2.102.0",
"constructs": "10.1.154",
"esbuild": "^0.17.4",
"jest": "^29",
Expand Down
11 changes: 10 additions & 1 deletion packages/@eventual/aws-cdk/src/bucket-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { ServiceFunction } from "./service-function";
import { formatBucketArn, serviceBucketArn, ServiceEntityProps } from "./utils";
import { EventualResource } from "./resource";
import { SecureBucket } from "./secure/bucket";

export type BucketOverrides<Service> = Partial<
ServiceEntityProps<
Expand Down Expand Up @@ -141,6 +142,12 @@ export class BucketService<Service> {
],
})
);
if (this.props.compliancePolicy.isCustomerManagedKeys()) {
// data in the buckets are encrypted with a key that the customer owns
this.props.compliancePolicy.dataEncryptionKey.grantEncryptDecrypt(
grantee
);
}
}

private readonly ENV_MAPPINGS = {
Expand Down Expand Up @@ -179,7 +186,8 @@ class Bucket extends Construct implements IBucket {
const bucketOverrides =
props.serviceProps.bucketOverrides?.[props.bucket.name];

this.bucket = new s3.Bucket(this, "Bucket", {
this.bucket = new SecureBucket(this, "Bucket", {
compliancePolicy: props.serviceProps.compliancePolicy,
...bucketOverrides,
cors:
props.serviceProps.cors &&
Expand Down Expand Up @@ -264,6 +272,7 @@ export class BucketNotificationHandler
const bucketName = props.handler.spec.bucketName;

this.handler = new ServiceFunction(this, "Handler", {
compliancePolicy: props.serviceProps.compliancePolicy,
build: props.serviceProps.build,
bundledFunction: props.handler,
functionNameSuffix: `bucket-handler-${bucketName}-${handlerName}`,
Expand Down
8 changes: 6 additions & 2 deletions packages/@eventual/aws-cdk/src/command-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { ServiceFunction } from "./service-function.js";
import type { TaskService } from "./task-service";
import { ServiceEntityProps, serviceFunctionArn } from "./utils";
import type { WorkflowService } from "./workflow-service";
import { SecureFunction } from "./secure/function.js";

export type ApiOverrides = Omit<SpecHttpApiProps, "apiDefinition">;

Expand Down Expand Up @@ -180,6 +181,7 @@ export class CommandService<Service = any> {
commandsSystemScope,
"SystemCommandHandler",
{
compliancePolicy: props.compliancePolicy,
build: this.props.build,
bundledFunction:
this.props.build.system.eventualService.systemCommandHandler,
Expand Down Expand Up @@ -242,6 +244,7 @@ export class CommandService<Service = any> {
scope,
commandNamespaceName(command),
{
compliancePolicy: props.compliancePolicy,
build: self.props.build,
bundledFunction: manifest,
functionNameSuffix: commandFunctionNameSuffix(command),
Expand Down Expand Up @@ -330,13 +333,14 @@ export class CommandService<Service = any> {

let optionsFunction: Function | undefined;
if (props.cors && !props.cors.disableOptionsEndpoint) {
optionsFunction = new Function(commandsSystemScope, "Options", {
optionsFunction = new SecureFunction(commandsSystemScope, "Options", {
compliancePolicy: props.compliancePolicy,
functionName: serviceFunctionName(
props.serviceName,
"options-command"
),
handler: "index.handler",
runtime: Runtime.NODEJS_18_X,
runtime: Runtime.NODEJS_LATEST,
architecture: Architecture.ARM_64,
memorySize: 512,
// the headers will be replaced with the correct headers based on the cors configuration
Expand Down
Loading

0 comments on commit 38eb3b0

Please sign in to comment.