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: Terraform Best Practices kyverno-json-policies for AWS ECS #109

Merged
merged 2 commits into from
Jan 31, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Check `awsvpc` network mode

When you're running containers on AWS, managing the networking becomes crucial, especially when leveraging the benefits of hosting multiple containers on a single host. There are various network modes to choose from:

1. **Host mode:** This is the most basic network mode in Amazon ECS. It essentially shares the host's networking stack with the containers.

2. **Bridge mode:** This mode employs a virtual network bridge to establish a layer between the host and the container's networking.

3. **AWSVPC mode:** Here, Amazon ECS generates and oversees an Elastic Network Interface (ENI) for each task, providing each task with a distinct private IP address within the VPC.

By default, the network mode for ECS tasks is set to `bridge`. However, we will opt for the `awsvpc` network mode here. This choice is strategic, as it facilitates controlled traffic flow between tasks and enhances security. `awsvpc` offers task-level network isolation for tasks running on Amazon EC2. Importantly, it's the exclusive network mode allowing the assignment of security groups to tasks.

To formalize our approach, it's prudent to establish a policy that ensures the usage of 'awsvpc' network mode. This policy aligns with our security strategy by adding an extra layer of protection through task-level isolation and the ability to apply security groups at the task level. Therefore, adopting and enforcing this policy safeguards our containerized infrastructure while optimizing networking capabilities on AWS.

### Policy Validation Testing Instructions

To evaluate and test the policy ensuring the presence of the `awsvpc` network mode in the Terraform plan payload, follow the steps outlined below:

For testing this policy you will need to:
- Make sure you have `kyverno-json` installed on the machine
- Properly authenticate with AWS

1. **Initialize Terraform:**
```bash
terraform init
```

2. **Create Binary Terraform Plan:**
```bash
terraform plan -out tfplan.binary
```

3. **Convert Binary to JSON Payload:**
```bash
terraform show -json tfplan.binary | jq > payload.json
```

4. **Test the Policy with Kyverno:**
```
kyverno-json scan --payload payload.json --policy policy.yaml
```

a. **Test Policy Against Valid Payload:**
```
kyverno-json scan --policy check-aws-vpc-network-mode.yaml --payload policy-test/good-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- check-awsvpc-network-mode / check-awsvpc-network-mode / PASSED
Done
```

b. **Test Against Invalid Payload:**
```
kyverno-json scan --policy check-aws-vpc-network-mode.yaml --payload policy-test/bad-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- check-awsvpc-network-mode / check-awsvpc-network-mode / FAILED: ECS services and tasks are required to use awsvpc network mode.: all[0].check.values.(network_mode=='awsvpc'): Invalid value: false: Expected value: true
Done
```

---

These instructions are designed to systematically assess the compliance of Terraform plans with the defined policy. Following these steps ensures a thorough examination of the `awsvpc` network mode presence in the payload, allowing for effective validation and adherence to the specified policy.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: json.kyverno.io/v1alpha1
kind: ValidatingPolicy
metadata:
name: check-awsvpc-network-mode
labels:
ecs.aws.network.kyverno.io: awsvpc
annotations:
policies.kyverno.io/title: Check awsvpc network mode
policies.kyverno.io/category: ECS Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/description: >-
The awsvpc network mode restricts the flow of traffic between different
tasks or between your tasks and other services that run within your Amazon VPC.
The awsvpc network mode provides task-level network isolation for tasks
that run on Amazon EC2.
spec:
rules:
- name: check-awsvpc-network-mode
match:
any:
- (planned_values.root_module.resources[?type=='aws_ecs_task_definition'] | length(@) > `0`): true
assert:
all:
- message: ECS services and tasks are required to use awsvpc network mode.
check:
~.(planned_values.root_module.resources[?type=='aws_ecs_task_definition'].values):
network_mode: awsvpc
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
"format_version": "1.2",
"terraform_version": "1.6.6-dev",
"planned_values": {
"root_module": {
"resources": [
{
"address": "aws_ecs_task_definition.task",
"mode": "managed",
"type": "aws_ecs_task_definition",
"name": "task",
"provider_name": "registry.terraform.io/hashicorp/aws",
"schema_version": 1,
"values": {
"container_definitions": "[{\"cpu\":512,\"essential\":true,\"image\":\"nginx:1.23.1\",\"memory\":2048,\"name\":\"foo-task\",\"portMappings\":[{\"containerPort\":80,\"hostPort\":80}]}]",
"cpu": "512",
"ephemeral_storage": [],
"execution_role_arn": null,
"family": "service",
"inference_accelerator": [],
"ipc_mode": null,
"memory": "2048",
"network_mode": "bridge",
"pid_mode": null,
"placement_constraints": [],
"proxy_configuration": [],
"requires_compatibilities": [
"EC2"
],
"runtime_platform": [],
"skip_destroy": false,
"tags": null,
"task_role_arn": null,
"volume": []
},
"sensitive_values": {
"ephemeral_storage": [],
"inference_accelerator": [],
"placement_constraints": [],
"proxy_configuration": [],
"requires_compatibilities": [
false
],
"runtime_platform": [],
"tags_all": {},
"volume": []
}
}
]
}
},
"resource_changes": [
{
"address": "aws_ecs_task_definition.task",
"mode": "managed",
"type": "aws_ecs_task_definition",
"name": "task",
"provider_name": "registry.terraform.io/hashicorp/aws",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"container_definitions": "[{\"cpu\":512,\"essential\":true,\"image\":\"nginx:1.23.1\",\"memory\":2048,\"name\":\"foo-task\",\"portMappings\":[{\"containerPort\":80,\"hostPort\":80}]}]",
"cpu": "512",
"ephemeral_storage": [],
"execution_role_arn": null,
"family": "service",
"inference_accelerator": [],
"ipc_mode": null,
"memory": "2048",
"network_mode": "awsvpc",
"pid_mode": null,
"placement_constraints": [],
"proxy_configuration": [],
"requires_compatibilities": [
"EC2"
],
"runtime_platform": [],
"skip_destroy": false,
"tags": null,
"task_role_arn": null,
"volume": []
},
"after_unknown": {
"arn": true,
"arn_without_revision": true,
"ephemeral_storage": [],
"id": true,
"inference_accelerator": [],
"placement_constraints": [],
"proxy_configuration": [],
"requires_compatibilities": [
false
],
"revision": true,
"runtime_platform": [],
"tags_all": true,
"volume": []
},
"before_sensitive": false,
"after_sensitive": {
"ephemeral_storage": [],
"inference_accelerator": [],
"placement_constraints": [],
"proxy_configuration": [],
"requires_compatibilities": [
false
],
"runtime_platform": [],
"tags_all": {},
"volume": []
}
}
}
],
"configuration": {
"provider_config": {
"aws": {
"name": "aws",
"full_name": "registry.terraform.io/hashicorp/aws",
"version_constraint": "~> 4.0",
"expressions": {
"region": {
"constant_value": "us-west-1"
}
}
},
"docker": {
"name": "docker",
"full_name": "registry.terraform.io/kreuzwerker/docker",
"version_constraint": "~> 2.20.0"
}
},
"root_module": {
"resources": [
{
"address": "aws_ecs_task_definition.task",
"mode": "managed",
"type": "aws_ecs_task_definition",
"name": "task",
"provider_config_key": "aws",
"expressions": {
"container_definitions": {
"constant_value": " [\n {\n \"name\" : \"foo-task\",\n \"image\" : \"nginx:1.23.1\",\n \"cpu\" : 512,\n \"memory\" : 2048,\n \"essential\" : true,\n \"portMappings\" : [\n {\n \"containerPort\" : 80,\n \"hostPort\" : 80\n }\n ]\n }\n ]\n"
},
"cpu": {
"constant_value": 512
},
"family": {
"constant_value": "service"
},
"memory": {
"constant_value": 2048
},
"network_mode": {
"constant_value": "awsvpc"
},
"requires_compatibilities": {
"constant_value": [
"EC2"
]
}
},
"schema_version": 1
}
]
}
},
"timestamp": "2024-01-15T13:25:27Z",
"errored": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
resource "aws_ecs_task_definition" "task" {
family = "service"
network_mode = "bridge"
requires_compatibilities = ["EC2"]
cpu = 512
memory = 2048
container_definitions = <<DEFINITION
[
{
"name" : "foo-task",
"image" : "nginx:1.23.1",
"cpu" : 512,
"memory" : 2048,
"essential" : true,
"portMappings" : [
{
"containerPort" : 80,
"hostPort" : 80
}
]
}
]
DEFINITION
}

# Setting up the configuration for using Docker and AWS providers

terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~>2.20.0"
}
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}

# Configuring docker and AWS as providers
provider "docker" {}

provider "aws" {
region = "us-west-1"
}
Loading
Loading