Skip to content

Commit 3f843ba

Browse files
feat: initial version to allow network firewall support
1 parent 573f574 commit 3f843ba

File tree

4 files changed

+658
-1
lines changed

4 files changed

+658
-1
lines changed

main.tf

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,26 @@ locals {
66
len_redshift_subnets = max(length(var.redshift_subnets), length(var.redshift_subnet_ipv6_prefixes))
77
len_intra_subnets = max(length(var.intra_subnets), length(var.intra_subnet_ipv6_prefixes))
88
len_outpost_subnets = max(length(var.outpost_subnets), length(var.outpost_subnet_ipv6_prefixes))
9+
len_firewall_subnets = max(length(var.firewall_subnets), length(var.firewall_subnet_ipv6_prefixes))
910

1011
max_subnet_length = max(
1112
local.len_private_subnets,
1213
local.len_public_subnets,
1314
local.len_elasticache_subnets,
1415
local.len_database_subnets,
1516
local.len_redshift_subnets,
17+
local.len_firewall_subnets,
1618
)
1719

20+
// TODO - comment what this line does
21+
firewall_sync_states = try(module.firewall[0].status[0].sync_states, {})
22+
firewall_vpce = {
23+
for state in local.firewall_sync_states: state.availability_zone => {
24+
cidr_block = one([ for subnet in aws_subnet.firewall : subnet.cidr_block if subnet.id == state.attachment[0].subnet_id ])
25+
endpoint_id = state.attachment[0].endpoint_id
26+
}
27+
}
28+
1829
# Use `local.vpc_id` to give a hint to Terraform that subnets should be deleted before secondary CIDR blocks can be free!
1930
vpc_id = try(aws_vpc_ipv4_cidr_block_association.this[0].vpc_id, aws_vpc.this[0].id, "")
2031

@@ -153,7 +164,7 @@ resource "aws_route_table_association" "public" {
153164
}
154165

155166
resource "aws_route" "public_internet_gateway" {
156-
count = local.create_public_subnets && var.create_igw ? local.num_public_route_tables : 0
167+
count = local.create_public_subnets && var.create_igw && !(var.enable_network_firewall && local.len_firewall_subnets > 0) ? local.num_public_route_tables : 0
157168

158169
route_table_id = aws_route_table.public[count.index].id
159170
destination_cidr_block = "0.0.0.0/0"
@@ -164,6 +175,32 @@ resource "aws_route" "public_internet_gateway" {
164175
}
165176
}
166177

178+
resource "aws_route_table_association" "public_internet_gateway" {
179+
count = local.create_vpc && var.create_igw && var.enable_network_firewall && local.len_firewall_subnets > 0 ? 1 : 0
180+
181+
gateway_id = aws_internet_gateway.this[0].id
182+
route_table_id = aws_route_table.internet_gateway[0].id
183+
}
184+
185+
resource "aws_route_table_association" "firewall" {
186+
count = local.create_vpc && local.len_firewall_subnets > 0 ? local.len_firewall_subnets : 0
187+
188+
subnet_id = element(aws_subnet.firewall.*.id, count.index)
189+
route_table_id = aws_route_table.firewall[0].id
190+
}
191+
192+
resource "aws_route" "public_firewall" {
193+
count = local.create_vpc && var.enable_network_firewall ? local.len_public_subnets : 0
194+
195+
route_table_id = element(aws_route_table.public.*.id, count.index)
196+
destination_cidr_block = "0.0.0.0/0"
197+
vpc_endpoint_id = local.firewall_vpce[aws_subnet.public[count.index].availability_zone].endpoint_id
198+
199+
timeouts {
200+
create = "5m"
201+
}
202+
}
203+
167204
resource "aws_route" "public_internet_gateway_ipv6" {
168205
count = local.create_public_subnets && var.create_igw && var.enable_ipv6 ? local.num_public_route_tables : 0
169206

@@ -223,6 +260,145 @@ resource "aws_network_acl_rule" "public_outbound" {
223260
ipv6_cidr_block = lookup(var.public_outbound_acl_rules[count.index], "ipv6_cidr_block", null)
224261
}
225262

263+
################################################################################
264+
# Firewall Subnets
265+
################################################################################
266+
267+
locals {
268+
create_firewall_subnets = local.create_vpc && local.len_firewall_subnets > 0
269+
}
270+
271+
resource "aws_subnet" "firewall" {
272+
count = local.create_firewall_subnets ? local.len_firewall_subnets : 0
273+
274+
vpc_id = local.vpc_id
275+
cidr_block = var.firewall_subnets[count.index]
276+
availability_zone = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) > 0 ? element(var.azs, count.index) : null
277+
availability_zone_id = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) == 0 ? element(var.azs, count.index) : null
278+
assign_ipv6_address_on_creation = var.enable_ipv6 && var.firewall_subnet_ipv6_native ? true : var.firewall_subnet_assign_ipv6_address_on_creation
279+
280+
ipv6_cidr_block = var.enable_ipv6 && length(var.firewall_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, var.firewall_subnet_ipv6_prefixes[count.index]) : null
281+
282+
tags = merge(
283+
{
284+
"Name" = format(
285+
"%s-${var.firewall_subnet_suffix}-%s",
286+
var.name,
287+
element(var.azs, count.index),
288+
)
289+
},
290+
var.tags,
291+
var.firewall_subnet_tags,
292+
)
293+
}
294+
295+
296+
resource "aws_route_table" "firewall" {
297+
count = local.create_vpc && var.create_firewall_subnet_route_table && local.len_firewall_subnets > 0 ? 1 : 0
298+
299+
vpc_id = local.vpc_id
300+
301+
tags = merge(
302+
{
303+
"Name" = "${var.name}-${var.firewall_subnet_suffix}"
304+
},
305+
var.tags,
306+
var.firewall_route_table_tags,
307+
)
308+
}
309+
310+
resource "aws_route" "firewall_internet_gateway" {
311+
count = local.create_vpc && var.create_igw && local.len_firewall_subnets > 0 ? 1 : 0
312+
313+
route_table_id = aws_route_table.firewall[count.index].id
314+
destination_cidr_block = "0.0.0.0/0"
315+
gateway_id = aws_internet_gateway.this[0].id
316+
317+
timeouts {
318+
create = "5m"
319+
}
320+
}
321+
322+
resource "aws_route_table" "internet_gateway" {
323+
count = local.create_vpc && var.enable_network_firewall && local.len_firewall_subnets > 0 ? 1 : 0
324+
vpc_id = local.vpc_id
325+
326+
tags = merge(
327+
{
328+
"Name" = format("%s-internet-gateway-${var.firewall_subnet_suffix}", var.name)
329+
},
330+
var.tags,
331+
var.public_route_table_tags,
332+
)
333+
}
334+
335+
resource "aws_route" "internet_gateway_firewall" {
336+
count = local.create_vpc && var.enable_network_firewall && local.len_firewall_subnets > 0 ? local.len_public_subnets : 0
337+
338+
route_table_id = aws_route_table.internet_gateway[0].id
339+
destination_cidr_block = aws_subnet.public[count.index].cidr_block
340+
341+
342+
vpc_endpoint_id = local.firewall_vpce[aws_subnet.public[count.index].availability_zone].endpoint_id // TODO testing
343+
#vpc_endpoint_id = element(local.firewall_endpoint_ids_ordered_by_azs, count.index)
344+
}
345+
346+
################################################################################
347+
# Firewall Network ACLs
348+
################################################################################
349+
350+
locals {
351+
create_firewall_network_acl = local.create_firewall_subnets && var.firewall_dedicated_network_acl
352+
}
353+
354+
resource "aws_network_acl" "firewall" {
355+
count = local.create_firewall_network_acl ? 1 : 0
356+
357+
vpc_id = local.vpc_id
358+
subnet_ids = aws_subnet.firewall[*].id
359+
360+
tags = merge(
361+
{ "Name" = "${var.name}-${var.firewall_subnet_suffix}" },
362+
var.tags,
363+
var.firewall_acl_tags,
364+
)
365+
}
366+
367+
resource "aws_network_acl_rule" "firewall_inbound" {
368+
count = local.create_firewall_network_acl ? length(var.firewall_inbound_acl_rules) : 0
369+
370+
network_acl_id = aws_network_acl.firewall[0].id
371+
372+
egress = false
373+
rule_number = var.firewall_inbound_acl_rules[count.index]["rule_number"]
374+
rule_action = var.firewall_inbound_acl_rules[count.index]["rule_action"]
375+
from_port = lookup(var.firewall_inbound_acl_rules[count.index], "from_port", null)
376+
to_port = lookup(var.firewall_inbound_acl_rules[count.index], "to_port", null)
377+
icmp_code = lookup(var.firewall_inbound_acl_rules[count.index], "icmp_code", null)
378+
icmp_type = lookup(var.firewall_inbound_acl_rules[count.index], "icmp_type", null)
379+
protocol = var.firewall_inbound_acl_rules[count.index]["protocol"]
380+
cidr_block = lookup(var.firewall_inbound_acl_rules[count.index], "cidr_block", null)
381+
ipv6_cidr_block = lookup(var.firewall_inbound_acl_rules[count.index], "ipv6_cidr_block", null)
382+
}
383+
384+
resource "aws_network_acl_rule" "firewall_outbound" {
385+
count = local.create_firewall_network_acl ? length(var.firewall_outbound_acl_rules) : 0
386+
387+
network_acl_id = aws_network_acl.firewall[0].id
388+
389+
egress = true
390+
rule_number = var.firewall_outbound_acl_rules[count.index]["rule_number"]
391+
rule_action = var.firewall_outbound_acl_rules[count.index]["rule_action"]
392+
from_port = lookup(var.firewall_outbound_acl_rules[count.index], "from_port", null)
393+
to_port = lookup(var.firewall_outbound_acl_rules[count.index], "to_port", null)
394+
icmp_code = lookup(var.firewall_outbound_acl_rules[count.index], "icmp_code", null)
395+
icmp_type = lookup(var.firewall_outbound_acl_rules[count.index], "icmp_type", null)
396+
protocol = var.firewall_outbound_acl_rules[count.index]["protocol"]
397+
cidr_block = lookup(var.firewall_outbound_acl_rules[count.index], "cidr_block", null)
398+
ipv6_cidr_block = lookup(var.firewall_outbound_acl_rules[count.index], "ipv6_cidr_block", null)
399+
}
400+
401+
226402
################################################################################
227403
# Private Subnets
228404
################################################################################

network-firewall.tf

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
locals {
2+
#aws_managed_rules_prefix_arn = "arn:aws:network-firewall:${data.aws_region.current.name}:aws-managed:stateful-rulegroup"
3+
aws_managed_rules_prefix_arn = "arn:aws:network-firewall:us-east-2:aws-managed:stateful-rulegroup" // TODO - review this region
4+
5+
// TODO - Review these rules
6+
firewall_managed_rules = distinct(concat([
7+
"AbusedLegitMalwareDomainsStrictOrder",
8+
"BotNetCommandAndControlDomainsStrictOrder",
9+
"AbusedLegitBotNetCommandAndControlDomainsStrictOrder",
10+
"MalwareDomainsStrictOrder",
11+
"ThreatSignaturesIOCStrictOrder",
12+
"ThreatSignaturesPhishingStrictOrder",
13+
"ThreatSignaturesBotnetWebStrictOrder",
14+
"ThreatSignaturesEmergingEventsStrictOrder",
15+
"ThreatSignaturesDoSStrictOrder",
16+
"ThreatSignaturesMalwareWebStrictOrder",
17+
"ThreatSignaturesExploitsStrictOrder",
18+
"ThreatSignaturesWebAttacksStrictOrder",
19+
"ThreatSignaturesScannersStrictOrder",
20+
"ThreatSignaturesBotnetStrictOrder",
21+
"ThreatSignaturesMalwareStrictOrder",
22+
"ThreatSignaturesMalwareCoinminingStrictOrder",
23+
"ThreatSignaturesFUPStrictOrder",
24+
"ThreatSignaturesSuspectStrictOrder",
25+
"ThreatSignaturesBotnetWindowsStrictOrder",
26+
], var.firewall_managed_rules))
27+
28+
name = "${var.name}-network-firewall"
29+
}
30+
31+
module "firewall" {
32+
source = "terraform-aws-modules/network-firewall/aws"
33+
34+
count = var.create_network_firewall ? 1 : 0
35+
36+
# Firewall
37+
name = local.name
38+
description = var.description
39+
40+
# Only for example
41+
delete_protection = var.delete_protection
42+
firewall_policy_change_protection = var.firewall_policy_change_protection
43+
subnet_change_protection = var.subnet_change_protection
44+
45+
vpc_id = aws_vpc.this[0].id
46+
subnet_mapping = { for subnet_id in aws_subnet.firewall.*.id :
47+
"subnet-${subnet_id}" => {
48+
subnet_id = subnet_id
49+
ip_address_type = "IPV4"
50+
}
51+
}
52+
53+
# Logging configuration
54+
create_logging_configuration = true
55+
logging_configuration_destination_config = [
56+
{
57+
log_destination = {
58+
logGroup = module.logs_alerts[0].cloudwatch_log_group_name
59+
}
60+
log_destination_type = "CloudWatchLogs"
61+
log_type = "ALERT"
62+
},
63+
{
64+
log_destination = {
65+
logGroup = module.logs_flow[0].cloudwatch_log_group_name
66+
}
67+
log_destination_type = "CloudWatchLogs"
68+
log_type = "FLOW"
69+
},
70+
]
71+
72+
encryption_configuration = {
73+
key_id = module.kms[0].key_arn
74+
type = "CUSTOMER_KMS"
75+
}
76+
77+
# Policy
78+
policy_name = local.name
79+
policy_description = "Default network firewall policy for ${local.name}"
80+
81+
# policy_stateful_rule_group_reference = {}
82+
policy_stateful_rule_group_reference = {
83+
for i, rule_group in local.firewall_managed_rules : rule_group => {
84+
resource_arn = "${local.aws_managed_rules_prefix_arn}/${rule_group}",
85+
priority = i + 1,
86+
}
87+
}
88+
89+
policy_stateful_engine_options = {
90+
rule_order = "STRICT_ORDER"
91+
}
92+
policy_stateless_default_actions = ["aws:forward_to_sfe"]
93+
policy_stateless_fragment_default_actions = ["aws:forward_to_sfe"]
94+
95+
tags = var.tags // TODO - review these tags
96+
97+
depends_on = [module.kms]
98+
}
99+
100+
module "logs_alerts" {
101+
source = "git::https://github.com/withclutch/terraform-modules-registry?ref=aws-log-group_v1.194"
102+
103+
count = var.create_network_firewall ? 1 : 0
104+
105+
name = "${local.name}-alerts"
106+
tenant = var.tenant
107+
region = var.region
108+
environment = var.environment
109+
110+
retention_in_days = var.logs_retention_in_days
111+
kms_key_arn = var.logs_kms_key_arn
112+
create_datadog_subscription_filter = true
113+
}
114+
115+
// TODO review if this module is really necessary
116+
module "logs_flow" {
117+
source = "git::https://github.com/withclutch/terraform-modules-registry?ref=aws-log-group_v1.194"
118+
119+
count = var.create_network_firewall ? 1 : 0
120+
121+
name = "${local.name}-flow" // TODO - review this name
122+
tenant = var.tenant
123+
region = var.region
124+
environment = var.environment
125+
126+
retention_in_days = var.logs_retention_in_days
127+
kms_key_arn = var.logs_kms_key_arn
128+
create_datadog_subscription_filter = false
129+
}
130+
131+
module "kms" {
132+
source = "git::https://github.com/withclutch/terraform-modules-registry?ref=aws-kms_v1.194"
133+
134+
count = var.create_network_firewall ? 1 : 0
135+
136+
description = "KMS key used for ${var.name} AWS Network Firewall"
137+
name = var.name // TODO - review this name
138+
region = var.region
139+
environment = var.environment
140+
namespace = var.namespace
141+
tenant = var.tenant
142+
tags = var.tags
143+
}
144+

0 commit comments

Comments
 (0)