Skip to content

Commit

Permalink
feat(HIPAA Security): add Cloudtrail checks (#313)
Browse files Browse the repository at this point in the history
* feat(HIPAAA-Security): Add Cloudtrail checks. closes #166, closes #168, closes #169

* fix syntax error in 'Commit and push changes (if changed) stage

* ci: fix syntax error in 'Commit and push changes (if changed) stage'

* ci: fix syntax error in 'Commit and push changes (if changed) stage'

* Update rule IDs and alphabetize in RULES.md

Co-authored-by: Taylor Ondrey <ondreyt@amazon.com>
Co-authored-by: Arun Donti <dontirun@gmail.com>
  • Loading branch information
3 people authored Aug 20, 2021
1 parent db08dda commit 97883ae
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 8 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/build.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ project.buildWorkflow.file.addOverride('jobs.build.steps', [
{
if: 'steps.git_diff.outputs.has_changes',
name: 'Commit and push changes (if changed)',
run: 'git add . && git commit -m "chore: self mutation" \n&& git push origin HEAD:${{ github.event.pull_request.head.ref }}',
run: 'git add .\ngit commit -m "chore: self mutation"\ngit push origin HEAD:${{ github.event.pull_request.head.ref }}',
},
{
if: 'steps.git_diff.outputs.has_changes',
Expand Down
6 changes: 5 additions & 1 deletion RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ There are currently no warnings for this rule pack.

### Errors

There are currently no errors for this rule pack.
| AWS Config Rule Name | Summary | Relevant Control IDs |
| --- | --- | --- |
| [HIPAA.Security-CloudTrailCloudWatchLogsEnabled](https://docs.aws.amazon.com/config/latest/developerguide/cloud-trail-cloud-watch-logs-enabled.html) | Use Amazon CloudWatch to centrally collect and manage log event activity. Inclusion of AWS CloudTrail data provides details of API call activity within your AWS account. | 164.308(a)(3)(ii)(A), 164.312(b) |
| [HIPAA.Security-CloudTrailEncryptionEnabled](https://docs.aws.amazon.com/config/latest/developerguide/cloud-trail-encryption-enabled.html) | Because sensitive data may exist and to help protect data at rest, ensure encryption is enabled for your AWS CloudTrail trails. | 164.312(a)(2)(iv), 164.312(e)(2)(ii) |
| [HIPAA.Security-CloudTrailLogFileValidationEnabled](https://docs.aws.amazon.com/config/latest/developerguide/cloud-trail-log-file-validation-enabled.html) | Utilize AWS CloudTrail log file validation to check the integrity of CloudTrail logs. Log file validation helps determine if a log file was modified or deleted or unchanged after CloudTrail delivered it. This feature is built using industry standard algorithms: SHA-256 for hashing and SHA-256 with RSA for digital signing. This makes it computationally infeasible to modify, delete or forge CloudTrail log files without detection.| 164.312(c)(1), 164.312(c)(2) |

### Excluded Rules

Expand Down
59 changes: 55 additions & 4 deletions src/HIPAA-Security/hipaa-security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

import { CfnResource, IConstruct } from '@aws-cdk/core';
import { Annotations, CfnResource, IConstruct } from '@aws-cdk/core';
import { NagPack } from '../common';
import {
hipaaSecurityCloudTrailCloudWatchLogsEnabled,
hipaaSecurityCloudTrailEncryptionEnabled,
hipaaSecurityCloudTrailLogFileValidationEnabled,
} from './rules/cloudtrail/index';

/**
* Check for HIPAA Security compliance.
Expand All @@ -14,10 +19,10 @@ export class HIPAASecurityChecks extends NagPack {
public visit(node: IConstruct): void {
if (node instanceof CfnResource) {
// Get ignores metadata if it exists
// const ignores = node.getMetadata('cdk_nag')?.rules_to_suppress;
const ignores = node.getMetadata('cdk_nag')?.rules_to_suppress;
// this.checkAPIGW(node, ignores);
// this.checkAutoScaling(node, ignores);
// this.checkCloudTrail(node, ignores);
this.checkCloudTrail(node, ignores);
// this.checkCloudWatch(node, ignores);
// this.checkCodeBuild(node, ignores);
// this.checkDMS(node, ignores);
Expand Down Expand Up @@ -61,7 +66,53 @@ export class HIPAASecurityChecks extends NagPack {
// * @param node the IConstruct to evaluate
// * @param ignores list of ignores for the resource
// */
// private checkCloudTrail(node: CfnResource, ignores: any): void {}
private checkCloudTrail(node: CfnResource, ignores: any): void {
if (
!this.ignoreRule(
ignores,
'HIPAA.Security-CloudTrailCloudWatchLogsEnabled'
) &&
!hipaaSecurityCloudTrailCloudWatchLogsEnabled(node)
) {
const ruleId = 'HIPAA.Security-CloudTrailCloudWatchLogsEnabled';
const info =
'The trail does not have CloudWatch logs enabled - (Control IDs: 164.308(a)(3)(ii)(A), 164.312(b)).';
const explanation =
'Use Amazon CloudWatch to centrally collect and manage log event activity. Inclusion of AWS CloudTrail data provides details of API call activity within your AWS account.';
Annotations.of(node).addError(
this.createMessage(ruleId, info, explanation)
);
}
if (
!this.ignoreRule(ignores, 'HIPAA.Security-CloudTrailEncryptionEnabled') &&
!hipaaSecurityCloudTrailEncryptionEnabled(node)
) {
const ruleId = 'HIPAA.Security-CloudTrailEncryptionEnabled';
const info =
'The trail does not have a KMS key ID or have encryption enabled - (Control ID: 164.312(a)(2)(iv), 164.312(e)(2)(ii)).';
const explanation =
'Because sensitive data may exist and to help protect data at rest, ensure encryption is enabled for your AWS CloudTrail trails.';
Annotations.of(node).addError(
this.createMessage(ruleId, info, explanation)
);
}
if (
!this.ignoreRule(
ignores,
'HIPAA.Security-CloudTrailLogFileValidationEnabled'
) &&
!hipaaSecurityCloudTrailLogFileValidationEnabled(node)
) {
const ruleId = 'HIPAA.Security-CloudTrailLogFileValidationEnabled';
const info =
'The trail does not have log file validation enabled - (Control ID: 164.312(c)(1), 164.312(c)(2)).';
const explanation =
'Utilize AWS CloudTrail log file validation to check the integrity of CloudTrail logs. Log file validation helps determine if a log file was modified or deleted or unchanged after CloudTrail delivered it. This feature is built using industry standard algorithms: SHA-256 for hashing and SHA-256 with RSA for digital signing. This makes it computationally infeasible to modify, delete or forge CloudTrail log files without detection.';
Annotations.of(node).addError(
this.createMessage(ruleId, info, explanation)
);
}
}

// /**
// * Check CloudWatch Resources
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { CfnTrail } from '@aws-cdk/aws-cloudtrail';
import { IConstruct, Stack } from '@aws-cdk/core';

/**
* CloudTrail trails have CloudWatch logs enabled - (Control IDs: 164.308(a)(3)(ii)(A), 164.312(b))
* @param node the CfnResource to check
*/
export default function (node: IConstruct): boolean {
if (node instanceof CfnTrail) {
const cloudWatch = Stack.of(node).resolve(node.cloudWatchLogsLogGroupArn);

if (cloudWatch == undefined) {
return false;
}
}
return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { CfnTrail } from '@aws-cdk/aws-cloudtrail';
import { IConstruct, Stack } from '@aws-cdk/core';

/**
* CloudTrail trails have encryption enabled - (Control ID: 164.312(a)(2)(iv), 164.312(e)(2)(ii))
* @param node the CfnResource to check
*/
export default function (node: IConstruct): boolean {
if (node instanceof CfnTrail) {
const keyID = Stack.of(node).resolve(node.kmsKeyId);

if (keyID == undefined) {
return false;
}
}
return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { CfnTrail } from '@aws-cdk/aws-cloudtrail';
import { IConstruct, Stack } from '@aws-cdk/core';

/**
* CloudTrail trails have log file validation enabled - (Control ID: 164.312(c)(1), 164.312(c)(2))
* @param node the CfnResource to check
*/
export default function (node: IConstruct): boolean {
if (node instanceof CfnTrail) {
const enabled = Stack.of(node).resolve(node.enableLogFileValidation);

if (enabled != true) {
return false;
}
}
return true;
}
3 changes: 3 additions & 0 deletions src/HIPAA-Security/rules/cloudtrail/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
export { default as hipaaSecurityCloudTrailCloudWatchLogsEnabled } from './hipaaSecurityCloudTrailCloudWatchLogsEnabled';
export { default as hipaaSecurityCloudTrailEncryptionEnabled } from './hipaaSecurityCloudTrailEncryptionEnabled';
export { default as hipaaSecurityCloudTrailLogFileValidationEnabled } from './hipaaSecurityCloudTrailLogFileValidationEnabled';
160 changes: 160 additions & 0 deletions test/HIPAA-Security/HIPAA-Security-CloudTrail.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SynthUtils } from '@aws-cdk/assert';
import { Trail } from '@aws-cdk/aws-cloudtrail';
import { Key } from '@aws-cdk/aws-kms';
import { LogGroup } from '@aws-cdk/aws-logs';

import { Aspects, Stack } from '@aws-cdk/core';
import { HIPAASecurityChecks } from '../../src';

describe('HIPAA Security CloudTrail Compliance Checks', () => {
describe('AWS CloudTrail', () => {
test('HIPAA.Security-CloudTrailCloudWatchLogsEnabled: CloudTrail trails have CloudWatch logs enabled', () => {
const nonCompliant = new Stack();
Aspects.of(nonCompliant).add(new HIPAASecurityChecks());

const trail = new Trail(nonCompliant, 'rTrail');
trail.logAllLambdaDataEvents();

const messages1 = SynthUtils.synthesize(nonCompliant).messages;
expect(messages1).toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailCloudWatchLogsEnabled:'
),
}),
})
);

const activeCompliant = new Stack();
Aspects.of(activeCompliant).add(new HIPAASecurityChecks());

const myLogs = new LogGroup(activeCompliant, 'rLogGroup');

const trail2 = new Trail(activeCompliant, 'rTrail', {
cloudWatchLogGroup: myLogs,
sendToCloudWatchLogs: true,
});

trail2.logAllLambdaDataEvents();

const messages2 = SynthUtils.synthesize(activeCompliant).messages;
expect(messages2).not.toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailCloudWatchLogsEnabled:'
),
}),
})
);
});

test('HIPAA.Security-CloudTrailEncryptionEnabled: CloudTrail trails have encryption enabled', () => {
const nonCompliant = new Stack();
Aspects.of(nonCompliant).add(new HIPAASecurityChecks());

const myLogs = new LogGroup(nonCompliant, 'rLogGroup');

const trail = new Trail(nonCompliant, 'rTrail', {
cloudWatchLogGroup: myLogs,
sendToCloudWatchLogs: true,
});

trail.stack;

const messages1 = SynthUtils.synthesize(nonCompliant).messages;
expect(messages1).toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailEncryptionEnabled:'
),
}),
})
);

const activeCompliant = new Stack();
Aspects.of(activeCompliant).add(new HIPAASecurityChecks());

const myLogs2 = new LogGroup(activeCompliant, 'rLogGroup');

const myKey = new Key(activeCompliant, 'rKey');

const trail2 = new Trail(activeCompliant, 'rTrail', {
cloudWatchLogGroup: myLogs2,
sendToCloudWatchLogs: true,
kmsKey: myKey,
});

trail2.logAllLambdaDataEvents();

const messages2 = SynthUtils.synthesize(activeCompliant).messages;
expect(messages2).not.toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailEncryptionEnabled:'
),
}),
})
);
});

test('HIPAA.Security-CloudTrailLogFileValidationEnabled: Cloud Trails have log file validation enabled', () => {
const nonCompliant = new Stack();
Aspects.of(nonCompliant).add(new HIPAASecurityChecks());

const myLogs = new LogGroup(nonCompliant, 'rLogGroup');

const trail = new Trail(nonCompliant, 'rTrail', {
cloudWatchLogGroup: myLogs,
sendToCloudWatchLogs: true,
enableFileValidation: false,
});

trail.stack;

const messages1 = SynthUtils.synthesize(nonCompliant).messages;
expect(messages1).toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailLogFileValidationEnabled:'
),
}),
})
);

const activeCompliant = new Stack();
Aspects.of(activeCompliant).add(new HIPAASecurityChecks());

const myLogs2 = new LogGroup(activeCompliant, 'rLogGroup');

const myKey = new Key(activeCompliant, 'rKey');

const trail2 = new Trail(activeCompliant, 'rTrail', {
cloudWatchLogGroup: myLogs2,
sendToCloudWatchLogs: true,
kmsKey: myKey,
});

trail2.logAllLambdaDataEvents();

const messages2 = SynthUtils.synthesize(activeCompliant).messages;
expect(messages2).not.toContainEqual(
expect.objectContaining({
entry: expect.objectContaining({
data: expect.stringContaining(
'HIPAA.Security-CloudTrailLogFileValidationEnabled:'
),
}),
})
);
});
});
});

0 comments on commit 97883ae

Please sign in to comment.