Note: the following is purely for illustrative purposes
This repository manages the inspection vpc and rulesets within the AWS estate. The inspection VPC is seated at the heart of the estate, and leverages AWS Network Firewall as a managed solution. It's remit is
- To filter all traffic between networks and enviroments (i.e. production, development, ci).
- To filter all egress traffic from the spokes to the outbound internet.
- To provide the spokes with a central place to egress all traffic to the internet i.e. sharing NAT gateways.
A firewall policy in AWS Network firewall comprises of one of more references stateful / stateless rule groups. The difference between these two groups is similar to NACL vs security groups in AWS; where SG's have knowlegde of direction and permit established connections to return traffic without the need of an additional rule. Note this module follows AWS recommendations, and has opted to ignore stateless rules completely, deferring purely to stateful Suricata rules.
Rule groups also have the ability to source in variables containing ipsets (a collection of CIDR block) or portsets (a collection of ports). These be referenced within the Suricata rules themselves, providing a reusable snippet i.e.
(in the tfvars)
policy_variables = {
devops_net = ["10.128.0.0/24"]
remote_net = ["10.230.0.0/24"]
}
# This will produce variables DEVOPS_NET and REMOTE_NET, and make
# them available in the ruleset
pass tcp $REMOTE_NET any -> $HOME_NET
or
pass tcp [!$REMOTE_NET, $DEVOPS_NET] any -> $HOME_NET
The module use the contents of the var.firewall_rules
to source in the files and merge them together to produce the final ruleset.
The inspection VPC can be configured to support egress traffic. This is useful when the inspection VPC is used as a central point for all egress traffic from the spokes. The egress support is enabled by setting the var.enable_egress
to true. When enabled, the inspection VPC will have a route table that routes all traffic to the internet gateway. The route table is associated with the private subnets, and the internet gateway is attached to the VPC.
To enable egress support,
var.enable_egress
must be set to true.var.publie_subnet_netmask
must be set to a non-zero value.
## Provision a inspection firewall, but with an existing vpc
module "inspection" {
source = "../.."
availability_zones = var.availability_zones
firewall_rules = local.firewall_rules
name = var.name
private_subnet_netmask = var.private_subnet_netmask
public_subnet_netmask = var.public_subnet_netmask
ram_principals = var.ram_principals
tags = var.tags
transit_gateway_id = var.transit_gateway_id
}
Currently the inspection VPC is setup to segregate the flow and alert logs into two CloudWatch log groups:
- Alerts: are directed to
${var.name}-alert-log
. - Flows: are directed to
${var.name}-flow-log
.
This module also supports the ability to encrypt the logs using a KMS key. If the var.create_kms_key
is set to true, a KMS key will be created and used to encrypt the logs. The key will be created in the same region as the logs.
The module also supports the ability to deploy a CloudWatch dashboard to visualise the logs. The dashboard is created using a CloudFormation template, and is deployed into the same region as the logs. The dashboard is created using the aws_cloudformation_stack
resource, and is created using the assets/cloudfomation/nfw-cloudwatch-dashboard template.
The module supports the ability to reuse an existing VPC. This is useful when the inspection VPC is being deployed into an existing environment. The options defined depend on whether egress is enabled or not.
To reuse an existing VPC, with egress support
var.vpc_id
must be set to the ID of the VPC.var.private_subnet_id_by_az
must be set to a map of availability zone to subnet id i.e{ "eu-west-1a" = "subnet-12345678" }
.var.public_route_table_ids
must be set to a list of public route table ids associated with the public subnets.var.transit_route_table_by_az
must be set to a map of availability zone to transit route table id i.e{ "eu-west-1a" = "rtb-12345678" }
.var.transit_route_table_ids
must be set to a list of transit route table ids associated with the transit subnets.
## Provision a inspection firewall, but with an existing vpc
module "inspection" {
source = "../.."
availability_zones = var.availability_zones
firewall_rules = local.firewall_rules
name = var.name
private_subnet_id_by_az = var.private_subnet_id_by_az
public_route_table_ids = var.public_route_table_ids
ram_principals = var.ram_principals
tags = var.tags
transit_gateway_id = var.transit_gateway_id
transit_route_table_by_az = var.transit_route_table_by_az
transit_route_table_ids = var.transit_route_table_ids
vpc_id = var.vpc_id
}
To reuse an existing VPC, without egress support
var.vpc_id
must be set to the ID of the VPC.var.private_subnet_id_by_az
must be set to a map of availability zone to subnet id i.e{ "eu-west-1a" = "subnet-12345678" }
.var.transit_route_table_by_az
must be set to a map of availability zone to transit route table id i.e{ "eu-west-1a" = "rtb-12345678" }
.
module "inspection" {
source = "../.."
availability_zones = var.availability_zones
create_kms_key = false
enable_dashboard = var.enable_dashboard
firewall_rules = local.firewall_rules
name = var.name
private_subnet_id_by_az = var.vpc.private_subnet_id_by_az
ram_principals = var.ram_principals
tags = var.tags
transit_gateway_id = var.transit_gateway_id
transit_route_table_by_az = var.vpc.transit_route_table_by_az
vpc_id = var.vpc.vpc_id
}
The following pipeline permissions are required to deploy the inspection VPC
# tfsec:ignore:aws-iam-no-policy-wildcards
module "network_inspection_vpc_admin" {
count = var.repositories.firewall != null ? 1 : 0
source = "appvia/oidc/aws//modules/role"
version = "1.2.0"
name = var.repositories.firewall.role_name
common_provider = var.scm_name
description = "Deployment role used to deploy the inspection vpc"
permission_boundary = var.default_permissions_boundary_name
repository = var.repositories.firewall.url
tags = var.tags
read_only_policy_arns = [
"arn:aws:iam::aws:policy/AWSResourceAccessManagerReadOnlyAccess",
"arn:aws:iam::aws:policy/ReadOnlyAccess",
]
read_write_policy_arns = [
"arn:aws:iam::aws:policy/AWSResourceAccessManagerFullAccess",
"arn:aws:iam::aws:policy/AmazonEC2FullAccess",
"arn:aws:iam::aws:policy/CloudFormationFullAccess", # Assuming you are deploying the dashboard
"arn:aws:iam::aws:policy/LambdaFullAccess",
"arn:aws:iam::aws:policy/ReadOnlyAccess",
"arn:aws:iam::aws:policy/job-function/NetworkAdministrator",
]
read_write_inline_policies = {
"additional" = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"network-firewall:Associate*",
"network-firewall:Create*",
"network-firewall:Delete*",
"network-firewall:Describe*",
"network-firewall:Disassociate*",
"network-firewall:List*",
"network-firewall:Put*",
"network-firewall:Tag*",
"network-firewall:Untag*",
"network-firewall:Update*",
]
Effect = "Allow"
Resource = "*"
},
{
Action = ["iam:CreateServiceLinkedRole"],
Effect = "Allow",
Resource = ["arn:aws:iam::*:role/aws-service-role/network-firewall.amazonaws.com/AWSServiceRoleForNetworkFirewall"]
},
{
Action = ["logs:*"],
Effect = "Allow",
Resource = ["*"]
}
]
Version = "2012-10-17"
Statement = [
{
Action = [
"network-firewall:Describe*",
"network-firewall:List*"
]
Effect = "Allow"
Resource = "*"
},
{
Action = [
"logs:Get*",
"logs:List*",
"logs:Describe*",
],
Effect = "Allow",
Resource = ["*"]
}
]
})
}
read_only_inline_policies = {
"additional" = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"network-firewall:Describe*",
"network-firewall:List*"
]
Effect = "Allow"
Resource = "*"
},
{
Action = [
"logs:Describe*",
"logs:Get*",
"logs:List*",
],
Effect = "Allow",
Resource = ["*"]
}
]
})
}
Name | Version |
---|---|
terraform | >= 1.0 |
aws | ~> 5.0 |
Name | Version |
---|---|
aws | ~> 5.0 |
Name | Source | Version |
---|---|---|
network_firewall | aws-ia/networkfirewall/aws | 1.0.1 |
vpc | appvia/network/aws | 0.3.1 |
Name | Type |
---|---|
aws_cloudformation_stack.dashboard | resource |
aws_cloudwatch_log_group.alert_log | resource |
aws_cloudwatch_log_group.flow_log | resource |
aws_ec2_managed_prefix_list.this | resource |
aws_kms_key.current | resource |
aws_networkfirewall_firewall_policy.this | resource |
aws_networkfirewall_rule_group.stateful | resource |
aws_ram_principal_association.this | resource |
aws_ram_resource_association.this | resource |
aws_ram_resource_share.this | resource |
aws_s3_bucket.dashboard | resource |
aws_s3_bucket_acl.dashboard | resource |
aws_s3_bucket_ownership_controls.dashboard | resource |
aws_s3_bucket_policy.dashboard | resource |
aws_s3_bucket_public_access_block.dashboard | resource |
aws_s3_bucket_server_side_encryption_configuration.dashboard | resource |
aws_s3_bucket_versioning.dashboard | resource |
aws_s3_object.dashboard | resource |
aws_caller_identity.current | data source |
aws_iam_policy_document.dashboard | data source |
aws_iam_policy_document.logging | data source |
aws_kms_key.current | data source |
aws_region.current | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
availability_zones | Number of availability zones to deploy into | number |
n/a | yes |
name | Name of the environment to deploy into | string |
n/a | yes |
tags | Tags to apply to all resources | map(string) |
n/a | yes |
transit_gateway_id | The ID of the Transit Gateway | string |
n/a | yes |
cloudwatch_kms | Name of the KMS key to use for CloudWatch logs | string |
"" |
no |
cloudwatch_retention_in_days | Number of days to retain CloudWatch logs | number |
30 |
no |
create_kms_key | Create a KMS key for CloudWatch logs | bool |
false |
no |
dashboard_bucket | The name of the S3 bucket to store the CloudWatch Insights dashboard | string |
"lza-inspection-cw-dashboard" |
no |
dashboard_key | The name of the S3 bucket key to store the CloudWatch Insights dashboard | string |
"nfw-cloudwatch-dashboard.yml" |
no |
enable_dashboard | Indicates we should deploy the CloudWatch Insights dashboard | bool |
false |
no |
enable_egress | Indicates the inspectio vpc should have egress enabled | bool |
false |
no |
enable_policy_change_protection | Indicates the firewall policy should be protected from changes | bool |
false |
no |
enable_subnet_change_protection | Indicates the firewall subnets should be protected from changes | bool |
false |
no |
external_rule_groups | A collection of additional rule groups to add to the policy | list(object({ |
null |
no |
firewall_rules | A collection of firewall rules to add to the policy | list(object({ |
null |
no |
ip_prefixes | A collection of ip sets which can be referenced by the rules | map(object({ |
{} |
no |
network_cidr_blocks | List of CIDR blocks defining the aws environment | list(string) |
[ |
no |
policy_variables | A map of policy variables made available to the suricata rules | map(list(string)) |
{} |
no |
private_subnet_id_by_az | If reusing an existing VPC, provider a map of az to subnet id | map(string) |
{} |
no |
private_subnet_netmask | Netmask for the private subnets | number |
24 |
no |
public_route_table_ids | If reusing an existing VPC, provide the public route table ids | list(string) |
[] |
no |
public_subnet_netmask | Netmask for the public subnets | number |
0 |
no |
ram_principals | A list of principals to share the firewall policy with | map(string) |
{} |
no |
stateful_capacity | The number of stateful rule groups to create | number |
5000 |
no |
transit_route_table_by_az | If reusing an existing VPC, provider a map of az to subnet id | map(string) |
{} |
no |
transit_route_table_ids | If reusing an existing VPC, provide the transit route table ids | list(string) |
[] |
no |
vpc_cidr | CIDR block for the VPC | string |
"100.64.0.0/21" |
no |
vpc_id | If reusing an existing VPC, provide the VPC ID and private subnets ids | string |
"" |
no |
Name | Description |
---|---|
firewall_id | The ARN of the firewall. |
firewall_rule_groups | The rule groups to associate with the firewall. |
policy_variables | The policy variables to associate with the firewall. |
private_subnet_id_by_az | The private subnet IDs by availability zone. |
private_subnet_ids | The IDs of the private subnets. |
public_subnet_ids | The IDs of the public subnets. |
ram_principals | The principals to share the firewall with. |
routing_configuration | The routing configuration for the firewall. |
transit_attachment_id | The ID of the transit gateway attachment. |
transit_route_table_by_az | The transit route table by availability zone. |
transit_subnet_ids | The IDs of the transit subnets. |
vpc_id | The ID of the VPC. |