Skip to content

appvia/terraform-aws-firewall

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

85 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Appvia Banner

Terraform Registry Latest Release Slack Community Contributors

Github Actions

Terraform AWS Inspection VPC

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. Its remit is:

  • To filter all traffic between networks and environments (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.

Key Features

  • AWS Network Firewall Integration: Leverages AWS managed Network Firewall for stateful packet inspection
  • Suricata Rule Support: Supports custom Suricata rules for advanced threat detection
  • Policy Variables: Dynamic rule configuration using policy variables and IP sets
  • IP Prefix Management: Managed prefix lists for efficient IP address management
  • External Rule Groups: Support for AWS managed and custom external rule groups
  • Egress Support: Optional egress traffic routing through the inspection VPC
  • CloudWatch Integration: Comprehensive logging and monitoring with optional dashboard
  • KMS Encryption: Optional encryption for CloudWatch logs using KMS
  • RAM Sharing: Share firewall policies across AWS accounts and organizations
  • VPC Reuse: Deploy into existing VPCs or create new ones
  • Protection Features: Optional protection against accidental changes

Rule Group Variables

A firewall policy in AWS Network Firewall comprises one or more references to stateful / stateless rule groups. The difference between these two groups is similar to NACL vs security groups in AWS; where security groups have knowledge 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 blocks) or portsets (a collection of ports). These can 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 uses the contents of the var.firewall_rules to source in the files and merge them together to produce the final ruleset.

IP Prefixes

The module supports the creation of managed prefix lists that can be referenced in Suricata rules. This is useful for managing large sets of IP addresses that need to be referenced in multiple rules.

ip_prefixes = {
  "blocked_ips" = {
    name           = "blocked-ips"
    address_family = "IPv4"
    max_entries    = 1000
    description    = "List of blocked IP addresses"
    entries = [
      {
        cidr        = "192.168.1.0/24"
        description = "Internal blocked range"
      },
      {
        cidr        = "10.0.0.0/8"
        description = "Another blocked range"
      }
    ]
  }
}

These prefix lists can then be referenced in Suricata rules using the $BLOCKED_IPS variable.

External Rule Groups

The module supports referencing external rule groups that have been created outside of this module. This is useful for sharing rule groups across multiple firewall deployments or using AWS managed rule groups.

external_rule_groups = [
  {
    priority = 100
    arn      = "arn:aws:network-firewall:us-west-2:111122223333:stateful-rulegroup/domains"
  }
]

When using external rule groups, the module will not create its own stateful rule group and will instead use the provided external rule groups.

Egress Support

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.public_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
}

Event Logging

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.

Alternatively, you can specify an existing KMS key using the var.cloudwatch_kms variable. This is useful when you want to use a centralized KMS key for all your CloudWatch logs.

module "inspection" {
  source = "../.."

  # ... other variables ...
  
  # Option 1: Create a new KMS key
  create_kms_key = true
  
  # Option 2: Use an existing KMS key
  cloudwatch_kms = "alias/my-existing-key"
}

CloudWatch Dashboard

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/cloudformation/nfw-cloudwatch-dashboard template.

To enable the dashboard, set var.enable_dashboard to true:

module "inspection" {
  source = "../.."

  # ... other variables ...
  
  enable_dashboard = true
}

The dashboard provides comprehensive monitoring of your Network Firewall including:

  • Firewall metrics and performance
  • Flow and alert log analysis
  • Contributor insights for security monitoring
  • Custom widgets for traffic analysis

RAM Sharing

The module supports sharing the firewall policy with other AWS accounts using AWS Resource Access Manager (RAM). This is useful when you want to share the same firewall policy across multiple accounts or organizations.

module "inspection" {
  source = "../.."

  # ... other variables ...
  
  ram_principals = {
    "account-1" = "123456789012"
    "account-2" = "987654321098"
  }
}

The shared firewall policy can then be used by other accounts to create their own Network Firewall instances with the same rules.

Firewall Protection

The module supports enabling protection against accidental changes to the firewall policy and subnets:

module "inspection" {
  source = "../.."

  # ... other variables ...
  
  # Protect against policy changes
  enable_policy_change_protection = true
  
  # Protect against subnet changes
  enable_subnet_change_protection = true
}

When enabled, these protections prevent accidental modifications that could disrupt network traffic.

Stateful Rule Capacity

The module allows you to configure the capacity for stateful rule groups. The default capacity is 5000 rules, but this can be adjusted based on your needs:

module "inspection" {
  source = "../.."

  # ... other variables ...
  
  # Increase capacity for more complex rule sets
  stateful_capacity = 10000
}

The maximum capacity is 30,000 rules per stateful rule group.

Reusing an Existing VPC

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
}

Pipeline Permissions

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 = ["*"]
        }
      ]
    })
  }

IAM Permissions

The following permissions are required to deploy the inspection firewall. The module requires permissions for Network Firewall, VPC management, CloudWatch Logs, KMS (if using encryption), and RAM (if sharing resources).

Required AWS Managed Policies

  • AWSResourceAccessManagerFullAccess (for RAM sharing)
  • AmazonEC2FullAccess (for VPC and Network Firewall resources)
  • CloudWatchLogsFullAccess (for log group management)
  • CloudFormationFullAccess (if deploying the dashboard)
  • ReadOnlyAccess (for resource discovery)

Required Inline Permissions

{
  "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": ["*"]
    }
  ]
}

Providers

Name Version
aws ~> 6.4

Inputs

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({
priority = number
arn = string
}))
null no
firewall_rules A collection of firewall rules to add to the policy
list(object({
name = string
content = string
}))
null no
ip_prefixes A collection of ip sets which can be referenced by the rules
map(object({
name = string
address_family = string
max_entries = number
description = string
entries = list(object({
cidr = string
description = string
}))
}))
{} no
network_cidr_blocks List of CIDR blocks defining the aws environment list(string)
[
"10.0.0.0/8",
"192.168.0.0/24"
]
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

Outputs

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.

About

Provisions a inspection vpc to restrict traffic flow between the networks

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 8