Skip to content

Commit

Permalink
feat(EventBridge): EventBusOpenAccess rule (#636)
Browse files Browse the repository at this point in the history
* feat(EventBridge): EventBusOpenAccess rule

* chore: cdk v2 specific changes
  • Loading branch information
dontirun authored Feb 16, 2022
1 parent f161510 commit 59c27b5
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 6 deletions.
1 change: 1 addition & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The [AWS Solutions Library](https://aws.amazon.com/solutions/) offers a collecti
| AwsSolutions-EMR4 | The EMR cluster does not use a security configuration with local disk encryption enabled. | Local disk encryption uses a combination of open-source HDFS encryption and LUKS encryption to secure data at rest. |
| AwsSolutions-EMR5 | The EMR cluster does not use a security configuration with encryption in transit enabled and configured. | EMR Clusters should have a method for encrypting data in transit using Transport Layer Security (TLS). |
| AwsSolutions-EMR6 | The EMR cluster does not implement authentication via an EC2 Key Pair or Kerberos. | SSH clients can use an EC2 key pair to authenticate to cluster instances. Alternatively, with EMR release version 5.10.0 or later, solutions can configure Kerberos to authenticate users and SSH connections to the master node. |
| AwsSolutions-EVB1 | The event bus policy allows for open access. | An open policy ("\*" principal without a condition) grants anonymous access to an event bus. Use a condition to limit the permission to accounts that fulfill a certain requirement, such as being a member of a certain AWS organization. |
| AwsSolutions-IAM4 | The IAM user, role, or group uses AWS managed policies. | An AWS managed policy is a standalone policy that is created and administered by AWS. Currently, many AWS managed policies do not restrict resource scope. Replace AWS managed policies with system specific (customer) managed policies. |
| AwsSolutions-IAM5 | The IAM entity contains wildcard permissions and does not have a cdk_nag rule suppression with evidence for those permission. | Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. |
| AwsSolutions-KDF1 | The Kinesis Data Firehose delivery stream does have server-side encryption enabled. | This allows the system to meet strict regulatory requirements and enhance the security of system data. |
Expand Down
10 changes: 10 additions & 0 deletions src/packs/aws-solutions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import {
EMRLocalDiskEncryption,
EMRS3AccessLogging,
} from '../rules/emr';
import { EventBusOpenAccess } from '../rules/eventbridge';
import {
GlueEncryptedCloudWatchLogs,
GlueJobBookmarkEncrypted,
Expand Down Expand Up @@ -1318,6 +1319,15 @@ export class AwsSolutionsChecks extends NagPack {
* @param ignores list of ignores for the resource
*/
private checkApplicationIntegration(node: CfnResource): void {
this.applyRule({
ruleSuffixOverride: 'EVB1',
info: 'The event bus policy allows for open access.',
explanation:
'An open policy ("*" principal without a condition) grants anonymous access to an event bus. Use a condition to limit the permission to accounts that fulfill a certain requirement, such as being a member of a certain AWS organization.',
level: NagMessageLevel.ERROR,
rule: EventBusOpenAccess,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'SNS2',
info: 'The SNS Topic does not have server-side encryption enabled.',
Expand Down
69 changes: 69 additions & 0 deletions src/rules/eventbridge/EventBusOpenAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { parse } from 'path';
import { CfnResource, Stack } from 'aws-cdk-lib';
import { CfnEventBusPolicy } from 'aws-cdk-lib/aws-events';
import { NagRuleCompliance } from '../../nag-rules';

/**
* EventBridge event bus policies do not allow for open access
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnEventBusPolicy) {
if (Stack.of(node).resolve(node.principal) === '*') {
const condition = Stack.of(node).resolve(node.condition);
if (
condition === undefined ||
condition.key === undefined ||
condition.type === undefined ||
condition.value === undefined
) {
return NagRuleCompliance.NON_COMPLIANT;
}
}
const resolvedStatement = Stack.of(node).resolve(node.statement);
if (resolvedStatement !== undefined) {
const condition = Stack.of(node).resolve(resolvedStatement?.Condition);
if (
resolvedStatement?.Effect === 'Allow' &&
checkMatchingPrincipal(resolvedStatement?.Principal) === true &&
(condition === undefined || JSON.stringify(condition) === '{}')
) {
return NagRuleCompliance.NON_COMPLIANT;
}
}
return NagRuleCompliance.COMPLIANT;
} else {
return NagRuleCompliance.NOT_APPLICABLE;
}
},
'name',
{ value: parse(__filename).name }
);

/**
* Helper function to check whether the event bus policy applies to all principals
* @param node The CfnEventBusPolicy to check
* @param principal The principals in the event bus policy
* @returns Whether the CfnEventBusPolicy applies to all principals
*/
function checkMatchingPrincipal(principals: any): boolean {
if (principals === '*') {
return true;
}
const awsPrincipal = principals.AWS;
if (Array.isArray(awsPrincipal)) {
for (const account of awsPrincipal) {
if (account === '*') {
return true;
}
}
} else if (awsPrincipal === '*') {
return true;
}
return false;
}
5 changes: 5 additions & 0 deletions src/rules/eventbridge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
export { default as EventBusOpenAccess } from './EventBusOpenAccess';
1 change: 1 addition & 0 deletions src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * as elasticache from './elasticache';
export * as elasticbeanstalk from './elasticbeanstalk';
export * as elb from './elb';
export * as emr from './emr';
export * as eventbridge from './eventbridge';
export * as glue from './glue';
export * as iam from './iam';
export * as kinesis from './kinesis';
Expand Down
1 change: 1 addition & 0 deletions test/Packs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ describe('Check NagPack Details', () => {
'AwsSolutions-EMR4',
'AwsSolutions-EMR5',
'AwsSolutions-EMR6',
'AwsSolutions-EVB1',
'AwsSolutions-IAM4',
'AwsSolutions-IAM5',
'AwsSolutions-KDF1',
Expand Down
138 changes: 138 additions & 0 deletions test/rules/EventBridge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { Aspects, Stack } from 'aws-cdk-lib';
import { CfnEventBusPolicy } from 'aws-cdk-lib/aws-events';
import { EventBusOpenAccess } from '../../src/rules/eventbridge';
import { validateStack, TestType, TestPack } from './utils';

const testPack = new TestPack([EventBusOpenAccess]);
let stack: Stack;

beforeEach(() => {
stack = new Stack();
Aspects.of(stack).add(testPack);
});

describe('Amazon EventBridge', () => {
describe('EventBusOpenAccess: DMS replication instances are not public', () => {
const ruleId = 'EventBusOpenAccess';
test('Noncompliance 1', () => {
new CfnEventBusPolicy(stack, 'rPolicy', {
statementId: 'foo',
action: 'events:*',
principal: '*',
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Noncompliance 2', () => {
new CfnEventBusPolicy(stack, 'rPolicy', {
statementId: 'foo',
statement: {
Effect: 'Allow',
Principal: '*',
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Noncompliance 3', () => {
new CfnEventBusPolicy(stack, 'rPolicy', {
statementId: 'foo',
statement: {
Effect: 'Allow',
Principal: {
AWS: ['arn:aws:iam::111122223333:user/alice', '*'],
},
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Noncompliance 4', () => {
new CfnEventBusPolicy(stack, 'rPolicy', {
statementId: 'foo',
statement: {
Effect: 'Allow',
Principal: { AWS: '*' },
Action: 'events:*',
Condition: {},
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Noncompliance 5', () => {
new CfnEventBusPolicy(stack, 'rPolicy', {
statementId: 'foo',
action: 'events:*',
principal: '*',
condition: {
key: 'foo',
type: 'bar',
value: 'baz',
},
statement: {
Effect: 'Allow',
Principal: '*',
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Compliance', () => {
new CfnEventBusPolicy(stack, 'rPolicy1', {
statementId: 'foo',
action: 'events:*',
principal: '*',
condition: {
key: 'foo',
type: 'bar',
value: 'baz',
},
});
new CfnEventBusPolicy(stack, 'rPolicy2', {
statementId: 'foo',
statement: {
Effect: 'Allow',
Principal: '*',
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
Condition: {
StringEquals: { 'aws:PrincipalOrgID': 'o-1234567890' },
},
},
});
new CfnEventBusPolicy(stack, 'rPolicy3', {
statementId: 'foo',
statement: {
Effect: 'Allow',
Principal: {
AWS: [
'arn:aws:iam::111122223333:user/alice',
'arn:aws:iam::111122223333:user/bob',
],
},
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
new CfnEventBusPolicy(stack, 'rPolicy4', {
statementId: 'foo',
statement: {
Effect: 'Deny',
Principal: {
AWS: '*',
},
Action: 'events:*',
Resource: 'arn:aws:events:us-east-1:111122223333:event-bus/default',
},
});
validateStack(stack, ruleId, TestType.COMPLIANCE);
});
});
});
12 changes: 6 additions & 6 deletions yarn.lock

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

0 comments on commit 59c27b5

Please sign in to comment.