Skip to content

Commit 42c1f01

Browse files
committed
feat: Add implementation for root module combining firewall and policy
1 parent 0ee7223 commit 42c1f01

File tree

10 files changed

+760
-23
lines changed

10 files changed

+760
-23
lines changed

README.md

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ Terraform module which creates AWS network firewall resources.
66

77
See [`examples`](https://github.com/clowdhaus/terraform-aws-network-firewall/tree/main/examples) directory for working examples to reference:
88

9+
### Firewall & Firewall Policy
910
```hcl
1011
module "network_firewall" {
1112
source = "clowdhaus/network-firewall/aws"
1213
14+
1315
tags = {
1416
Terraform = "true"
1517
Environment = "dev"
@@ -37,19 +39,64 @@ No providers.
3739

3840
## Modules
3941

40-
No modules.
42+
| Name | Source | Version |
43+
|------|--------|---------|
44+
| <a name="module_firewall"></a> [firewall](#module\_firewall) | ./modules/firewall | n/a |
45+
| <a name="module_policy"></a> [policy](#module\_policy) | ./modules/policy | n/a |
4146

4247
## Resources
4348

4449
No resources.
4550

4651
## Inputs
4752

48-
No inputs.
53+
| Name | Description | Type | Default | Required |
54+
|------|-------------|------|---------|:--------:|
55+
| <a name="input_create"></a> [create](#input\_create) | Controls if resources should be created | `bool` | `true` | no |
56+
| <a name="input_create_logging_configuration"></a> [create\_logging\_configuration](#input\_create\_logging\_configuration) | Controls if a Logging Configuration should be created | `bool` | `false` | no |
57+
| <a name="input_create_policy"></a> [create\_policy](#input\_create\_policy) | Controls if policy should be created | `bool` | `true` | no |
58+
| <a name="input_create_policy_resource_policy"></a> [create\_policy\_resource\_policy](#input\_create\_policy\_resource\_policy) | Controls if a resource policy should be created | `bool` | `false` | no |
59+
| <a name="input_delete_protection"></a> [delete\_protection](#input\_delete\_protection) | A boolean flag indicating whether it is possible to delete the firewall. Defaults to `true` | `bool` | `true` | no |
60+
| <a name="input_description"></a> [description](#input\_description) | A friendly description of the firewall | `string` | `""` | no |
61+
| <a name="input_encryption_configuration"></a> [encryption\_configuration](#input\_encryption\_configuration) | KMS encryption configuration settings | `any` | `{}` | no |
62+
| <a name="input_firewall_policy_arn"></a> [firewall\_policy\_arn](#input\_firewall\_policy\_arn) | The ARN of the Firewall Policy to use | `string` | `""` | no |
63+
| <a name="input_firewall_policy_change_protection"></a> [firewall\_policy\_change\_protection](#input\_firewall\_policy\_change\_protection) | A boolean flag indicating whether it is possible to change the associated firewall policy. Defaults to `false` | `bool` | `null` | no |
64+
| <a name="input_logging_configuration_destination_config"></a> [logging\_configuration\_destination\_config](#input\_logging\_configuration\_destination\_config) | A list of min 1, max 2 configuration blocks describing the destination for the logging configuration | `any` | `[]` | no |
65+
| <a name="input_name"></a> [name](#input\_name) | A friendly name of the firewall | `string` | `""` | no |
66+
| <a name="input_policy_attach_resource_policy"></a> [policy\_attach\_resource\_policy](#input\_policy\_attach\_resource\_policy) | Controls if a resource policy should be attached to the firewall policy | `bool` | `false` | no |
67+
| <a name="input_policy_description"></a> [policy\_description](#input\_policy\_description) | A friendly description of the firewall policy | `string` | `null` | no |
68+
| <a name="input_policy_encryption_configuration"></a> [policy\_encryption\_configuration](#input\_policy\_encryption\_configuration) | KMS encryption configuration settings | `any` | `{}` | no |
69+
| <a name="input_policy_name"></a> [policy\_name](#input\_policy\_name) | A friendly name of the firewall policy | `string` | `""` | no |
70+
| <a name="input_policy_ram_resource_associations"></a> [policy\_ram\_resource\_associations](#input\_policy\_ram\_resource\_associations) | A map of RAM resource associations for the created firewall policy | `map(string)` | `{}` | no |
71+
| <a name="input_policy_resource_policy"></a> [policy\_resource\_policy](#input\_policy\_resource\_policy) | The policy JSON to use for the resource policy; required when `create_resource_policy` is `false` | `string` | `""` | no |
72+
| <a name="input_policy_resource_policy_actions"></a> [policy\_resource\_policy\_actions](#input\_policy\_resource\_policy\_actions) | A list of IAM actions allowed in the resource policy | `list(string)` | `[]` | no |
73+
| <a name="input_policy_resource_policy_principals"></a> [policy\_resource\_policy\_principals](#input\_policy\_resource\_policy\_principals) | A list of IAM principals allowed in the resource policy | `list(string)` | `[]` | no |
74+
| <a name="input_policy_stateful_default_actions"></a> [policy\_stateful\_default\_actions](#input\_policy\_stateful\_default\_actions) | Set of actions to take on a packet if it does not match any stateful rules in the policy. This can only be specified if the policy has a `stateful_engine_options` block with a rule\_order value of `STRICT_ORDER`. You can specify one of either or neither values of `aws:drop_strict` or `aws:drop_established`, as well as any combination of `aws:alert_strict` and `aws:alert_established` | `list(string)` | `[]` | no |
75+
| <a name="input_policy_stateful_engine_options"></a> [policy\_stateful\_engine\_options](#input\_policy\_stateful\_engine\_options) | A configuration block that defines options on how the policy handles stateful rules. See [Stateful Engine Options](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy#stateful-engine-options) for details | `any` | `{}` | no |
76+
| <a name="input_policy_stateful_rule_group_reference"></a> [policy\_stateful\_rule\_group\_reference](#input\_policy\_stateful\_rule\_group\_reference) | Set of configuration blocks containing references to the stateful rule groups that are used in the policy. See [Stateful Rule Group Reference](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy#stateful-rule-group-reference) for details | `any` | `{}` | no |
77+
| <a name="input_policy_stateless_custom_action"></a> [policy\_stateless\_custom\_action](#input\_policy\_stateless\_custom\_action) | Set of configuration blocks describing the custom action definitions that are available for use in the firewall policy's `stateless_default_actions` | `any` | `{}` | no |
78+
| <a name="input_policy_stateless_default_actions"></a> [policy\_stateless\_default\_actions](#input\_policy\_stateless\_default\_actions) | Set of actions to take on a packet if it does not match any of the stateless rules in the policy. You must specify one of the standard actions including: `aws:drop`, `aws:pass`, or `aws:forward_to_sfe` | `list(string)` | <pre>[<br> "aws:pass"<br>]</pre> | no |
79+
| <a name="input_policy_stateless_fragment_default_actions"></a> [policy\_stateless\_fragment\_default\_actions](#input\_policy\_stateless\_fragment\_default\_actions) | Set of actions to take on a fragmented packet if it does not match any of the stateless rules in the policy. You must specify one of the standard actions including: `aws:drop`, `aws:pass`, or `aws:forward_to_sfe` | `list(string)` | <pre>[<br> "aws:pass"<br>]</pre> | no |
80+
| <a name="input_policy_stateless_rule_group_reference"></a> [policy\_stateless\_rule\_group\_reference](#input\_policy\_stateless\_rule\_group\_reference) | Set of configuration blocks containing references to the stateless rule groups that are used in the policy. See [Stateless Rule Group Reference](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy#stateless-rule-group-reference) for details | `any` | `{}` | no |
81+
| <a name="input_policy_tags"></a> [policy\_tags](#input\_policy\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
82+
| <a name="input_subnet_change_protection"></a> [subnet\_change\_protection](#input\_subnet\_change\_protection) | A boolean flag indicating whether it is possible to change the associated subnet(s). Defaults to `true` | `bool` | `true` | no |
83+
| <a name="input_subnet_mapping"></a> [subnet\_mapping](#input\_subnet\_mapping) | Set of configuration blocks describing the public subnets. Each subnet must belong to a different Availability Zone in the VPC. AWS Network Firewall creates a firewall endpoint in each subnet | `any` | `{}` | no |
84+
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
85+
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | The unique identifier of the VPC where AWS Network Firewall should create the firewall | `string` | `""` | no |
4986

5087
## Outputs
5188

52-
No outputs.
89+
| Name | Description |
90+
|------|-------------|
91+
| <a name="output_arn"></a> [arn](#output\_arn) | The Amazon Resource Name (ARN) that identifies the firewall |
92+
| <a name="output_id"></a> [id](#output\_id) | The Amazon Resource Name (ARN) that identifies the firewall |
93+
| <a name="output_logging_configuration_id"></a> [logging\_configuration\_id](#output\_logging\_configuration\_id) | The Amazon Resource Name (ARN) of the associated firewall |
94+
| <a name="output_policy_arn"></a> [policy\_arn](#output\_policy\_arn) | The Amazon Resource Name (ARN) that identifies the firewall policy |
95+
| <a name="output_policy_id"></a> [policy\_id](#output\_policy\_id) | The Amazon Resource Name (ARN) that identifies the firewall policy |
96+
| <a name="output_policy_resource_policy_id"></a> [policy\_resource\_policy\_id](#output\_policy\_resource\_policy\_id) | The Amazon Resource Name (ARN) of the firewall policy associated with the resource policy |
97+
| <a name="output_policy_update_token"></a> [policy\_update\_token](#output\_policy\_update\_token) | A string token used when updating a firewall policy |
98+
| <a name="output_status"></a> [status](#output\_status) | Nested list of information about the current status of the firewall |
99+
| <a name="output_update_token"></a> [update\_token](#output\_update\_token) | A string token used when updating a firewall |
53100
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
54101

55102
## License

examples/complete/README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Complete AWS Network Firewall Example
22

3-
Configuration in this directory creates:
3+
Configuration in this directory creates the following as separate module definitions:
44

5-
- <XXX>
5+
- AWS Network Firewall & Firewall Policy
6+
- AWS Network Firewall Rule Group
67

78
## Usage
89

@@ -34,13 +35,21 @@ Note that this example may create resources which will incur monetary charges on
3435

3536
| Name | Source | Version |
3637
|------|--------|---------|
38+
| <a name="module_network_firewall"></a> [network\_firewall](#module\_network\_firewall) | ../.. | n/a |
39+
| <a name="module_network_firewall_disabled"></a> [network\_firewall\_disabled](#module\_network\_firewall\_disabled) | ../.. | n/a |
40+
| <a name="module_network_firewall_rule_group_stateful"></a> [network\_firewall\_rule\_group\_stateful](#module\_network\_firewall\_rule\_group\_stateful) | ../../modules/rule-group | n/a |
41+
| <a name="module_network_firewall_rule_group_stateless"></a> [network\_firewall\_rule\_group\_stateless](#module\_network\_firewall\_rule\_group\_stateless) | ../../modules/rule-group | n/a |
3742
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |
3843

3944
## Resources
4045

4146
| Name | Type |
4247
|------|------|
48+
| [aws_cloudwatch_log_group.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
49+
| [aws_s3_bucket.network_firewall_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
50+
| [aws_s3_bucket_policy.network_firewall_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
4351
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
52+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
4453

4554
## Inputs
4655

examples/complete/main.tf

Lines changed: 216 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ provider "aws" {
22
region = local.region
33
}
44

5+
data "aws_caller_identity" "current" {}
56
data "aws_availability_zones" "available" {}
67

78
locals {
8-
region = "us-east-1"
9-
name = "network-firewall-ex-${basename(path.cwd)}"
9+
region = "us-east-1"
10+
name = "network-firewall-ex-${basename(path.cwd)}"
11+
account_id = data.aws_caller_identity.current.account_id
1012

1113
vpc_cidr = "10.0.0.0/16"
12-
azs = slice(data.aws_availability_zones.available.names, 0, 3)
14+
num_azs = 3
15+
azs = slice(data.aws_availability_zones.available.names, 0, local.num_azs)
1316

1417
tags = {
1518
Name = local.name
@@ -22,19 +25,161 @@ locals {
2225
# network firewall Module
2326
################################################################################
2427

25-
# module "network_firewall" {
26-
# source = "../.."
28+
module "network_firewall" {
29+
source = "../.."
2730

28-
# create = false
31+
# Firewall
32+
name = local.name
33+
description = "Example network firewall"
2934

30-
# tags = local.tags
31-
# }
35+
# Only for example
36+
delete_protection = false
37+
firewall_policy_change_protection = false
38+
subnet_change_protection = false
3239

33-
# module "network_firewall_disabled" {
34-
# source = "../.."
40+
vpc_id = module.vpc.vpc_id
41+
subnet_mapping = { for i in range(0, local.num_azs) :
42+
"subnet-${i}" => {
43+
subnet_id = element(module.vpc.public_subnets, i)
44+
ip_address_type = "IPV4"
45+
}
46+
}
47+
48+
# Logging configuration
49+
create_logging_configuration = true
50+
logging_configuration_destination_config = [
51+
{
52+
log_destination = {
53+
logGroup = aws_cloudwatch_log_group.logs.name
54+
}
55+
log_destination_type = "CloudWatchLogs"
56+
log_type = "ALERT"
57+
},
58+
{
59+
log_destination = {
60+
bucketName = aws_s3_bucket.network_firewall_logs.id
61+
prefix = local.name
62+
}
63+
log_destination_type = "S3"
64+
log_type = "FLOW"
65+
}
66+
]
67+
68+
# Policy
69+
policy_name = local.name
70+
policy_description = "Example network firewall policy"
3571

36-
# create = false
37-
# }
72+
policy_stateful_rule_group_reference = {
73+
one = { resource_arn = module.network_firewall_rule_group_stateful.arn }
74+
}
75+
76+
policy_stateless_default_actions = ["aws:pass"]
77+
policy_stateless_fragment_default_actions = ["aws:drop"]
78+
policy_stateless_rule_group_reference = {
79+
one = {
80+
priority = 1
81+
resource_arn = module.network_firewall_rule_group_stateless.arn
82+
}
83+
}
84+
85+
tags = local.tags
86+
}
87+
88+
module "network_firewall_disabled" {
89+
source = "../.."
90+
91+
create = false
92+
}
93+
94+
################################################################################
95+
# Network Firewall Rule Group
96+
################################################################################
97+
98+
module "network_firewall_rule_group_stateful" {
99+
source = "../../modules/rule-group"
100+
101+
name = "${local.name}-stateful"
102+
description = "Stateful Inspection for denying access to a domain"
103+
type = "STATEFUL"
104+
capacity = 100
105+
106+
rule_group = {
107+
rules_source = {
108+
rules_source_list = {
109+
generated_rules_type = "DENYLIST"
110+
target_types = ["HTTP_HOST"]
111+
targets = ["test.example.com"]
112+
}
113+
}
114+
}
115+
116+
# Resource Policy
117+
create_resource_policy = true
118+
attach_resource_policy = true
119+
resource_policy_principals = ["arn:aws:iam::${local.account_id}:root"]
120+
121+
tags = local.tags
122+
}
123+
124+
module "network_firewall_rule_group_stateless" {
125+
source = "../../modules/rule-group"
126+
127+
name = "${local.name}-stateless"
128+
description = "Stateless Inspection with a Custom Action"
129+
type = "STATELESS"
130+
capacity = 100
131+
132+
rule_group = {
133+
rules_source = {
134+
stateless_rules_and_custom_actions = {
135+
custom_action = [{
136+
action_definition = {
137+
publish_metric_action = {
138+
dimension = [{
139+
value = "2"
140+
}]
141+
}
142+
}
143+
action_name = "ExampleMetricsAction"
144+
}]
145+
stateless_rule = [{
146+
priority = 1
147+
rule_definition = {
148+
actions = ["aws:pass", "ExampleMetricsAction"]
149+
match_attributes = {
150+
source = [{
151+
address_definition = "1.2.3.4/32"
152+
}]
153+
source_port = [{
154+
from_port = 443
155+
to_port = 443
156+
}]
157+
destination = [{
158+
address_definition = "124.1.1.5/32"
159+
}]
160+
destination_port = [{
161+
from_port = 443
162+
to_port = 443
163+
}]
164+
protocols = [6]
165+
tcp_flag = [{
166+
flags = ["SYN"]
167+
masks = ["SYN", "ACK"]
168+
}]
169+
}
170+
}
171+
}]
172+
}
173+
}
174+
}
175+
176+
# Resource Policy
177+
create_resource_policy = true
178+
attach_resource_policy = true
179+
resource_policy_principals = ["arn:aws:iam::${local.account_id}:root"]
180+
181+
tags = local.tags
182+
}
38183

39184
################################################################################
40185
# Supporting Resources
@@ -56,3 +201,62 @@ module "vpc" {
56201

57202
tags = local.tags
58203
}
204+
205+
resource "aws_cloudwatch_log_group" "logs" {
206+
name = "${local.name}-logs"
207+
retention_in_days = 7
208+
209+
tags = local.tags
210+
}
211+
212+
resource "aws_s3_bucket" "network_firewall_logs" {
213+
bucket = "${local.name}-network-firewall-logs-${local.account_id}"
214+
force_destroy = true
215+
216+
tags = local.tags
217+
}
218+
219+
# Logging configuration automatically adds this policy if not present
220+
resource "aws_s3_bucket_policy" "network_firewall_logs" {
221+
bucket = aws_s3_bucket.network_firewall_logs.id
222+
policy = jsonencode({
223+
Version = "2012-10-17"
224+
Statement = [
225+
{
226+
Action = "s3:PutObject"
227+
Condition = {
228+
ArnLike = {
229+
"aws:SourceArn" = "arn:aws:logs:${local.region}:${local.account_id}:*"
230+
}
231+
StringEquals = {
232+
"aws:SourceAccount" = local.account_id
233+
"s3:x-amz-acl" = "bucket-owner-full-control"
234+
}
235+
}
236+
Effect = "Allow"
237+
Principal = {
238+
Service = "delivery.logs.amazonaws.com"
239+
}
240+
Resource = "${aws_s3_bucket.network_firewall_logs.arn}/${local.name}/AWSLogs/${local.account_id}/*"
241+
Sid = "AWSLogDeliveryWrite"
242+
},
243+
{
244+
Action = "s3:GetBucketAcl"
245+
Condition = {
246+
ArnLike = {
247+
"aws:SourceArn" = "arn:aws:logs:${local.region}:${local.account_id}:*"
248+
}
249+
StringEquals = {
250+
"aws:SourceAccount" = local.account_id
251+
}
252+
}
253+
Effect = "Allow"
254+
Principal = {
255+
Service = "delivery.logs.amazonaws.com"
256+
}
257+
Resource = aws_s3_bucket.network_firewall_logs.arn
258+
Sid = "AWSLogDeliveryAclCheck"
259+
},
260+
]
261+
})
262+
}

examples/separate/main.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ module "network_firewall" {
3232
description = "Example network firewall"
3333

3434
# Only for example
35-
delete_protection = false
36-
37-
firewall_policy_arn = module.network_firewall_policy.arn
35+
delete_protection = false
3836
firewall_policy_change_protection = false
3937
subnet_change_protection = false
4038

39+
firewall_policy_arn = module.network_firewall_policy.arn
40+
4141
vpc_id = module.vpc.vpc_id
4242
subnet_mapping = { for i in range(0, local.num_azs) :
4343
"subnet-${i}" => {

0 commit comments

Comments
 (0)