Skip to content

Commit

Permalink
feat(AwsSolutions): new ECS, EKS, EMR, Glue, MediaStore, RDS, SNS, an…
Browse files Browse the repository at this point in the history
…d SQS rules (#621)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
dontirun authored Feb 11, 2022
1 parent 2ddb25a commit d6d8653
Show file tree
Hide file tree
Showing 34 changed files with 2,079 additions and 41 deletions.
6 changes: 3 additions & 3 deletions .projen/deps.json

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

4 changes: 2 additions & 2 deletions .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ const { awscdk } = require('projen');
const project = new awscdk.AwsCdkConstructLibrary({
author: 'Arun Donti',
authorAddress: 'donti@amazon.com',
cdkVersion: '2.1.0',
cdkVersion: '2.11.0',
defaultReleaseBranch: 'v2-main',
majorVersion: 2,
npmDistTag: 'latest',
name: 'cdk-nag',
description:
'Check CDK v2 applications for best practices using a combination on available rule packs.',
repositoryUrl: 'https://github.com/cdklabs/cdk-nag.git',
devDeps: ['@aws-cdk/assert@^2.1'],
devDeps: ['@aws-cdk/assert@^2.11'],
publishToPypi: {
distName: 'cdk-nag',
module: 'cdk_nag',
Expand Down
11 changes: 11 additions & 0 deletions RULES.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json

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

125 changes: 122 additions & 3 deletions src/packs/aws-solutions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ import {
ECSTaskDefinitionContainerLogging,
} from '../rules/ecs';
import { EFSEncrypted } from '../rules/efs';
import {
EKSClusterControlPlaneLogs,
EKSClusterNoEndpointPublicAccess,
} from '../rules/eks';
import {
ElastiCacheClusterInVPC,
ElastiCacheClusterNonDefaultPort,
Expand All @@ -79,7 +83,16 @@ import {
ELBLoggingEnabled,
ELBTlsHttpsListenersOnly,
} from '../rules/elb';
import { EMRAuthEC2KeyPairOrKerberos, EMRS3AccessLogging } from '../rules/emr';
import {
EMRAuthEC2KeyPairOrKerberos,
EMREncryptionInTransit,
EMRLocalDiskEncryption,
EMRS3AccessLogging,
} from '../rules/emr';
import {
GlueEncryptedCloudWatchLogs,
GlueJobBookmarkEncrypted,
} from '../rules/glue';
import { IAMNoManagedPolicies, IAMNoWildcardPermissions } from '../rules/iam';
import {
KinesisDataAnalyticsFlinkCheckpointing,
Expand All @@ -94,6 +107,7 @@ import {
MediaStoreContainerCORSPolicy,
MediaStoreContainerHasContainerPolicy,
MediaStoreContainerLifecyclePolicy,
MediaStoreContainerSSLRequestsOnly,
} from '../rules/mediastore';
import {
MSKBrokerLogging,
Expand Down Expand Up @@ -124,7 +138,9 @@ import {
AuroraMySQLPostgresIAMAuth,
RDSInstanceBackupEnabled,
RDSInstanceDeletionProtectionEnabled,
RDSMultiAZSupport,
RDSNonDefaultPort,
RDSRestrictedInbound,
RDSStorageEncrypted,
} from '../rules/rds';
import {
Expand All @@ -151,8 +167,12 @@ import {
SageMakerNotebookNoDirectInternetAccess,
} from '../rules/sagemaker';
import { SecretsManagerRotationEnabled } from '../rules/secretsmanager';
import { SNSEncryptedKMS } from '../rules/sns';
import { SQSQueueDLQ, SQSQueueSSE } from '../rules/sqs';
import { SNSEncryptedKMS, SNSTopicSSLRequestsOnly } from '../rules/sns';
import {
SQSQueueDLQ,
SQSQueueSSE,
SQSQueueSSLRequestsOnly,
} from '../rules/sqs';
import {
StepFunctionStateMachineAllLogsToCloudWatch,
StepFunctionStateMachineXray,
Expand Down Expand Up @@ -291,6 +311,24 @@ export class AwsSolutionsChecks extends NagPack {
rule: ECSTaskDefinitionContainerLogging,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EKS1',
info: "The EKS cluster's Kubernetes API server endpoint has public access enabled.",
explanation:
"A cluster's Kubernetes API server endpoint should not be publicly accessible from the Internet in order to avoid exposing private data and minimizing security risks. The API server endpoints should only be accessible from within a AWS Virtual Private Cloud (VPC).",
level: NagMessageLevel.ERROR,
rule: EKSClusterNoEndpointPublicAccess,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EKS2',
info: "The EKS Cluster does not publish 'api', 'audit', 'authenticator, 'controllerManager', and 'scheduler' control plane logs.",
explanation:
'EKS control plane logging provides audit and diagnostic logs directly from the Amazon EKS control plane to CloudWatch Logs in your account. These logs make it easy for you to secure and run your clusters.',
level: NagMessageLevel.ERROR,
rule: EKSClusterControlPlaneLogs,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'ELB1',
info: 'The CLB is used for incoming HTTP/HTTPS traffic. Use ALBs instead.',
Expand Down Expand Up @@ -406,6 +444,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: RDSStorageEncrypted,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'RDS3',
info: 'The non-Aurora RDS DB instance does not have multi-AZ support enabled.',
explanation:
'Use multi-AZ deployment configurations for high availability and automatic failover support fully managed by AWS.',
level: NagMessageLevel.ERROR,
rule: RDSMultiAZSupport,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'RDS6',
info: 'The RDS Aurora MySQL/PostgresSQL cluster does not have IAM Database Authentication enabled.',
Expand All @@ -415,6 +462,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: AuroraMySQLPostgresIAMAuth,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'RDS8',
info: 'The RDS DB Security Group allows for 0.0.0.0/0 inbound access.',
explanation:
'RDS DB security groups should not allow access from 0.0.0.0/0 (i.e. anywhere, every machine that has the ability to establish a connection) in order to reduce the risk of unauthorized access.',
level: NagMessageLevel.ERROR,
rule: RDSRestrictedInbound,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'RDS10',
info: 'The RDS instance or Aurora DB cluster does not have deletion protection enabled.',
Expand Down Expand Up @@ -931,6 +987,24 @@ export class AwsSolutionsChecks extends NagPack {
rule: EMRS3AccessLogging,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EMR4',
info: 'The EMR cluster does not use a security configuration with local disk encryption enabled.',
explanation:
'Local disk encryption uses a combination of open-source HDFS encryption and LUKS encryption to secure data at rest.',
level: NagMessageLevel.ERROR,
rule: EMRLocalDiskEncryption,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EMR5',
info: 'The EMR cluster does not use a security configuration with encryption in transit enabled and configured.',
explanation:
'EMR Clusters should have a method for encrypting data in transit using Transport Layer Security (TLS).',
level: NagMessageLevel.ERROR,
rule: EMREncryptionInTransit,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EMR6',
info: 'The EMR cluster does not implement authentication via an EC2 Key Pair or Kerberos.',
Expand All @@ -940,6 +1014,24 @@ export class AwsSolutionsChecks extends NagPack {
rule: EMRAuthEC2KeyPairOrKerberos,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'GL1',
info: 'The Glue crawler or job does not use a security configuration with CloudWatch Log encryption enabled.',
explanation:
'Enabling encryption at rest helps prevent unauthorized users from getting access to the logging data published to CloudWatch Logs.',
level: NagMessageLevel.WARN,
rule: GlueEncryptedCloudWatchLogs,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'GL3',
info: 'The Glue job does not have use a security configuration with job bookmark encryption enabled.',
explanation:
'Job bookmark encryption encrypts bookmark data before it is sent to Amazon S3 for storage.',
level: NagMessageLevel.WARN,
rule: GlueJobBookmarkEncrypted,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'KDA3',
info: 'The Kinesis Data Analytics Flink Application does not have checkpointing enabled.',
Expand Down Expand Up @@ -1225,6 +1317,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: SNSEncryptedKMS,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'SNS3',
info: 'The SNS Topic does not require requests to use SSL.',
explanation:
'Without HTTPS (TLS), a network-based attacker can eavesdrop on network traffic or manipulate it, using an attack such as man-in-the-middle. Allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition in the topic policy to force requests to use SSL. If SSE is already enabled then this control is auto enforced.',
level: NagMessageLevel.ERROR,
rule: SNSTopicSSLRequestsOnly,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'SQS2',
info: 'The SQS Queue does not have server-side encryption enabled.',
Expand All @@ -1243,6 +1344,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: SQSQueueDLQ,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'SQS4',
info: 'The SQS queue does not require requests to use SSL.',
explanation:
'Without HTTPS (TLS), a network-based attacker can eavesdrop on network traffic or manipulate it, using an attack such as man-in-the-middle. Allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition in the queue policy to force requests to use SSL.',
level: NagMessageLevel.ERROR,
rule: SQSQueueSSLRequestsOnly,
node: node,
});
}

/**
Expand All @@ -1260,6 +1370,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: MediaStoreContainerAccessLogging,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'MS3',
info: 'The MediaStore container does not require requests to use SSL.',
explanation:
'You can use HTTPS (TLS) to help prevent potential attackers from eavesdropping on or manipulating network traffic using person-in-the-middle or similar attacks. You should allow only encrypted connections over HTTPS (TLS) using the aws:SecureTransport condition on MediaStore container policies.',
level: NagMessageLevel.ERROR,
rule: MediaStoreContainerSSLRequestsOnly,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'MS4',
info: 'The MediaStore container does not define a metric policy to send metrics to CloudWatch.',
Expand Down
82 changes: 82 additions & 0 deletions src/rules/eks/EKSClusterControlPlaneLogs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
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 { CfnCluster } from 'aws-cdk-lib/aws-eks';
import { NagRuleCompliance, NagRules } from '../../nag-rules';

/**
* EKS Clusters publish 'api', 'audit', 'authenticator, 'controllerManager', and 'scheduler' control plane logs
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnCluster) {
const logging = Stack.of(node).resolve(node.logging);
if (logging === undefined) {
return NagRuleCompliance.NON_COMPLIANT;
}
const clusterLogging = Stack.of(node).resolve(logging.clusterLogging);
if (clusterLogging === undefined) {
return NagRuleCompliance.NON_COMPLIANT;
}
const enabledTypes = Stack.of(node).resolve(clusterLogging.enabledTypes);
if (!Array.isArray(enabledTypes)) {
return NagRuleCompliance.NON_COMPLIANT;
}
const requiredTypes = new Set([
'api',
'audit',
'authenticator',
'controllerManager',
'scheduler',
]);
for (const enabled of enabledTypes) {
requiredTypes.delete(NagRules.resolveIfPrimitive(node, enabled.type));
if (requiredTypes.size === 0) {
break;
}
}
if (requiredTypes.size !== 0) {
return NagRuleCompliance.NON_COMPLIANT;
}
return NagRuleCompliance.COMPLIANT;
} else if (node.cfnResourceType === 'Custom::AWSCDK-EKS-Cluster') {
// The CDK uses a Custom Resource with AWS SDK calls to create EKS Clusters
const props = Stack.of(node).resolve((<any>node)._cfnProperties);
const clusterLogging = Stack.of(node).resolve(
props?.Config?.logging?.clusterLogging
);
if (!Array.isArray(clusterLogging)) {
return NagRuleCompliance.NON_COMPLIANT;
}
const requiredTypes = new Set([
'api',
'audit',
'authenticator',
'controllerManager',
'scheduler',
]);
for (const config of clusterLogging) {
if (config?.enabled === true) {
for (const type of config?.types) {
requiredTypes.delete(type);
if (requiredTypes.size === 0) {
break;
}
}
}
}
if (requiredTypes.size !== 0) {
return NagRuleCompliance.NON_COMPLIANT;
}
return NagRuleCompliance.COMPLIANT;
} else {
return NagRuleCompliance.NOT_APPLICABLE;
}
},
'name',
{ value: parse(__filename).name }
);
Loading

0 comments on commit d6d8653

Please sign in to comment.