Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: error if ECS task definition includes awslogs driver running in blocking mode #1812

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ The [AWS Solutions Library](https://aws.amazon.com/solutions/) offers a collecti
| AwsSolutions-ECS2 | The ECS Task Definition includes a container definition that directly specifies environment variables. | Use secrets to inject environment variables during container startup from AWS Systems Manager Parameter Store or Secrets Manager instead of directly specifying plaintext environment variables. Updates to direct environment variables require operators to change task definitions and perform new deployments. |
| AwsSolutions-ECS4 | The ECS Cluster has CloudWatch Container Insights disabled. | CloudWatch Container Insights allow operators to gain a better perspective on how the cluster’s applications and microservices are performing. |
| AwsSolutions-ECS7 | One or more containers in the ECS Task Definition do not have container logging enabled. | Container logging allows operators to view and aggregate the logs from the container. Containers should use the 'awslogs' driver at a minimum. |
| AwsSolutions-ECS8 | One or more containers in the ECS Task Definition are using the 'awslogs' driver in blocking mode. | Containers using 'awslogs' in blocking mode will be interupted if logs can't be immediately sent to Amazon CloudWatch Logs. |
| AwsSolutions-EFS1 | The EFS is not configured for encryption at rest. | By using an encrypted file system, data and metadata are automatically encrypted before being written to the file system. Similarly, as data and metadata are read, they are automatically decrypted before being presented to the application. These processes are handled transparently by EFS without requiring modification of applications. |
| AwsSolutions-EKS1 | The EKS cluster's Kubernetes API server endpoint has public access enabled. | 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). |
| AwsSolutions-EKS2 | The EKS Cluster does not publish 'api', 'audit', 'authenticator, 'controllerManager', and 'scheduler' control plane logs. | 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. This is a granular rule that returns individual findings that can be suppressed with `appliesTo`. The findings are in the format `LogExport::<log>` for exported logs. Example: `appliesTo: ['LogExport::authenticate']`. |
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 @@ -62,6 +62,7 @@ import {
ECSClusterCloudWatchContainerInsights,
ECSTaskDefinitionContainerLogging,
ECSTaskDefinitionNoEnvironmentVariables,
ECSTaskDefinitionAwslogsDriverNotBlocking,
} from '../rules/ecs';
import { EFSEncrypted } from '../rules/efs';
import {
Expand Down Expand Up @@ -328,6 +329,15 @@ export class AwsSolutionsChecks extends NagPack {
rule: ECSTaskDefinitionContainerLogging,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'ECS8',
info: "One or more containers in the ECS Task Definition are using the 'awslogs' driver in blocking mode.",
explanation:
"Containers using 'awslogs' in blocking mode will be interupted if logs can't be immediately sent to Amazon CloudWatch Logs.",
level: NagMessageLevel.ERROR,
rule: ECSTaskDefinitionAwslogsDriverNotBlocking,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'EKS1',
info: "The EKS cluster's Kubernetes API server endpoint has public access enabled.",
Expand Down
50 changes: 50 additions & 0 deletions src/rules/ecs/ECSTaskDefinitionAwslogsDriverNotBlocking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
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 { CfnTaskDefinition } from 'aws-cdk-lib/aws-ecs';
import { NagRuleCompliance } from '../../nag-rules';

/**
* Containers in ECS Task Definitions do not use the awslogs in blocking mode
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnTaskDefinition) {
const containerDefinitions = Stack.of(node).resolve(
node.containerDefinitions
);
if (containerDefinitions === undefined) {
return NagRuleCompliance.NOT_APPLICABLE;
}

let isUsingAwslogsDriver = false;
for (const containerDefinition of containerDefinitions) {
const resolvedDefinition = Stack.of(node).resolve(containerDefinition);
const logConfiguration = Stack.of(node).resolve(
resolvedDefinition.logConfiguration
);
if (logConfiguration?.logDriver === 'awslogs') {
isUsingAwslogsDriver = true;
if (logConfiguration?.options?.mode === undefined) {
return NagRuleCompliance.NON_COMPLIANT;
} else if (logConfiguration?.options?.mode === 'blocking') {
return NagRuleCompliance.NON_COMPLIANT;
}
}
}
if (isUsingAwslogsDriver) {
return NagRuleCompliance.COMPLIANT;
} else {
return NagRuleCompliance.NOT_APPLICABLE;
}
} else {
return NagRuleCompliance.NOT_APPLICABLE;
}
},
'name',
{ value: parse(__filename).name }
);
1 change: 1 addition & 0 deletions src/rules/ecs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as ECSClusterCloudWatchContainerInsights } from './ECSClusterCl
export { default as ECSTaskDefinitionContainerLogging } from './ECSTaskDefinitionContainerLogging';
export { default as ECSTaskDefinitionNoEnvironmentVariables } from './ECSTaskDefinitionNoEnvironmentVariables';
export { default as ECSTaskDefinitionUserForHostMode } from './ECSTaskDefinitionUserForHostMode';
export { default as ECSTaskDefinitionAwslogsDriverNotBlocking } from './ECSTaskDefinitionAwslogsDriverNotBlocking';
1 change: 1 addition & 0 deletions test/Packs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ describe('Check NagPack Details', () => {
'AwsSolutions-ECS2',
'AwsSolutions-ECS4',
'AwsSolutions-ECS7',
'AwsSolutions-ECS8',
'AwsSolutions-EFS1',
'AwsSolutions-EKS1',
'AwsSolutions-EKS2',
Expand Down
64 changes: 64 additions & 0 deletions test/rules/ECS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ContainerImage,
Cluster,
LogDriver,
AwsLogDriverMode,
} from 'aws-cdk-lib/aws-ecs';
import { Aspects, Stack } from 'aws-cdk-lib/core';
import { validateStack, TestType, TestPack } from './utils';
Expand All @@ -17,13 +18,15 @@ import {
ECSTaskDefinitionContainerLogging,
ECSTaskDefinitionNoEnvironmentVariables,
ECSTaskDefinitionUserForHostMode,
ECSTaskDefinitionAwslogsDriverNotBlocking,
} from '../../src/rules/ecs';

const testPack = new TestPack([
ECSClusterCloudWatchContainerInsights,
ECSTaskDefinitionContainerLogging,
ECSTaskDefinitionNoEnvironmentVariables,
ECSTaskDefinitionUserForHostMode,
ECSTaskDefinitionAwslogsDriverNotBlocking,
]);
let stack: Stack;

Expand Down Expand Up @@ -202,3 +205,64 @@ describe('Amazon Elastic Container Service (Amazon ECS)', () => {
});
});
});

describe('ECSTaskDefinitionAwslogsDriverNotBlocking: Containers in ECS Task Definitions have logging enabled', () => {
const ruleId = 'ECSTaskDefinitionAwslogsDriverNotBlocking';
test('Noncompliance 1', () => {
new TaskDefinition(stack, 'rTaskDef', {
compatibility: Compatibility.EC2,
}).addContainer('rContainer', {
image: ContainerImage.fromRegistry('imageName'),
memoryReservationMiB: 42,
logging: LogDriver.awsLogs({
streamPrefix: 'foo',
mode: AwsLogDriverMode.BLOCKING,
}),
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Noncompliance 2', () => {
const taskDef = new TaskDefinition(stack, 'rTaskDef', {
compatibility: Compatibility.EC2,
});
taskDef.addContainer('rContainer', {
image: ContainerImage.fromRegistry('imageName'),
memoryReservationMiB: 42,
logging: LogDriver.awsLogs({
streamPrefix: 'foo',
mode: AwsLogDriverMode.NON_BLOCKING,
}),
});
taskDef.addContainer('rContainer2', {
image: ContainerImage.fromRegistry('imageName'),
memoryReservationMiB: 42,
logging: LogDriver.awsLogs({
streamPrefix: 'bar',
// mode is implicitly AwsLogDriverMode.BLOCKING
}),
});
validateStack(stack, ruleId, TestType.NON_COMPLIANCE);
});
test('Compliance', () => {
const taskDef = new TaskDefinition(stack, 'rTaskDef', {
compatibility: Compatibility.EC2,
});
taskDef.addContainer('rContainer', {
image: ContainerImage.fromRegistry('imageName'),
memoryReservationMiB: 42,
logging: LogDriver.awsLogs({
streamPrefix: 'foo',
mode: AwsLogDriverMode.NON_BLOCKING,
}),
});
taskDef.addContainer('rContainer2', {
image: ContainerImage.fromRegistry('imageName'),
memoryReservationMiB: 42,
logging: LogDriver.awsLogs({
streamPrefix: 'bar',
mode: AwsLogDriverMode.NON_BLOCKING,
}),
});
validateStack(stack, ruleId, TestType.COMPLIANCE);
});
});