Skip to content

Commit

Permalink
Merge pull request #32400 from ddericco/f-aws_networkfirewall_firewal…
Browse files Browse the repository at this point in the history
…l_policy-home_net_override

Add Network Firewall policy variable support (HOME_NET override)
  • Loading branch information
ewbankkit authored Jul 26, 2023
2 parents f073242 + e960202 commit b5a5670
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .changelog/32400.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_networkfirewall_firewall_policy: Add `firewall_policy.policy_variables` configuration block to support Suricata HOME_NET variable override
```
74 changes: 74 additions & 0 deletions internal/service/networkfirewall/firewall_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package networkfirewall
import (
"context"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -53,6 +54,46 @@ func ResourceFirewallPolicy() *schema.Resource {
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"policy_variables": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"rule_variables": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 32),
validation.StringMatch(regexp.MustCompile(`^[A-Za-z]`), "must begin with alphabetic character"),
validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9_]+$`), "must contain only alphanumeric and underscore characters"),
),
},
"ip_set": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"definition": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
},
},
},
},
},
},
"stateful_default_actions": {
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -335,6 +376,20 @@ func waitFirewallPolicyDeleted(ctx context.Context, conn *networkfirewall.Networ
return nil, err
}

func expandPolicyVariables(tfMap map[string]interface{}) *networkfirewall.PolicyVariables {
if tfMap == nil {
return nil
}

policyVariables := &networkfirewall.PolicyVariables{}

if rvMap, ok := tfMap["rule_variables"].(*schema.Set); ok && rvMap.Len() > 0 {
policyVariables.RuleVariables = expandIPSets(rvMap.List())
}

return policyVariables
}

func expandStatefulEngineOptions(l []interface{}) *networkfirewall.StatefulEngineOptions {
if len(l) == 0 || l[0] == nil {
return nil
Expand Down Expand Up @@ -429,6 +484,10 @@ func expandFirewallPolicy(l []interface{}) *networkfirewall.FirewallPolicy {
StatelessFragmentDefaultActions: flex.ExpandStringSet(lRaw["stateless_fragment_default_actions"].(*schema.Set)),
}

if v, ok := lRaw["policy_variables"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
policy.PolicyVariables = expandPolicyVariables(v.([]interface{})[0].(map[string]interface{}))
}

if v, ok := lRaw["stateful_default_actions"].(*schema.Set); ok && v.Len() > 0 {
policy.StatefulDefaultActions = flex.ExpandStringSet(v)
}
Expand Down Expand Up @@ -457,6 +516,9 @@ func flattenFirewallPolicy(policy *networkfirewall.FirewallPolicy) []interface{}
return []interface{}{}
}
p := map[string]interface{}{}
if policy.PolicyVariables != nil {
p["policy_variables"] = flattenPolicyVariables(policy.PolicyVariables)
}
if policy.StatefulDefaultActions != nil {
p["stateful_default_actions"] = flex.FlattenStringSet(policy.StatefulDefaultActions)
}
Expand All @@ -482,6 +544,18 @@ func flattenFirewallPolicy(policy *networkfirewall.FirewallPolicy) []interface{}
return []interface{}{p}
}

func flattenPolicyVariables(variables *networkfirewall.PolicyVariables) []interface{} {
if variables == nil {
return []interface{}{}
}

m := map[string]interface{}{
"rule_variables": flattenIPSets(variables.RuleVariables),
}

return []interface{}{m}
}

func flattenStatefulEngineOptions(options *networkfirewall.StatefulEngineOptions) []interface{} {
if options == nil {
return []interface{}{}
Expand Down
86 changes: 83 additions & 3 deletions internal/service/networkfirewall/firewall_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ func TestAccNetworkFirewallFirewallPolicy_basic(t *testing.T) {
Steps: []resource.TestStep{
{
Config: testAccFirewallPolicyConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy),
acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("firewall-policy/%s", rName)),
resource.TestCheckResourceAttr(resourceName, "description", ""),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.*", "aws:drop"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.policy_variables.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_default_actions.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_default_actions.*", "aws:pass"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.*", "aws:drop"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "0"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
),
Expand Down Expand Up @@ -116,6 +122,59 @@ func TestAccNetworkFirewallFirewallPolicy_encryptionConfiguration(t *testing.T)
})
}

func TestAccNetworkFirewallFirewallPolicy_policyVariables(t *testing.T) {
ctx := acctest.Context(t)
var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_networkfirewall_firewall_policy.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckFirewallPolicyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccFirewallPolicyConfig_policyVariables(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.policy_variables.#", "1"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.policy_variables.0.rule_variables.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.policy_variables.0.rule_variables.*", map[string]string{
"key": "HOME_NET",
"ip_set.#": "1",
"ip_set.0.definition.#": "2",
}),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.policy_variables.0.rule_variables.*.ip_set.0.definition.*", "10.0.0.0/16"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.policy_variables.0.rule_variables.*.ip_set.0.definition.*", "10.0.1.0/24"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.*", "aws:drop"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_default_actions.*", "aws:pass"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccFirewallPolicyConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.policy_variables.#", "0"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_fragment_default_actions.*", "aws:drop"),
resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_default_actions.#", "1"),
resource.TestCheckTypeSetElemAttr(resourceName, "firewall_policy.0.stateless_default_actions.*", "aws:pass"),
),
},
},
})
}

func TestAccNetworkFirewallFirewallPolicy_statefulDefaultActions(t *testing.T) {
ctx := acctest.Context(t)
var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput
Expand Down Expand Up @@ -1175,6 +1234,27 @@ resource "aws_networkfirewall_firewall_policy" "test" {
`, rName, ruleOrder, streamExceptionPolicy)
}

func testAccFirewallPolicyConfig_policyVariables(rName string) string {
return fmt.Sprintf(`
resource "aws_networkfirewall_firewall_policy" "test" {
name = %[1]q
firewall_policy {
policy_variables {
rule_variables {
key = "HOME_NET"
ip_set {
definition = ["10.0.0.0/16", "10.0.1.0/24"]
}
}
}
stateless_fragment_default_actions = ["aws:drop"]
stateless_default_actions = ["aws:pass"]
}
}
`, rName)
}

func testAccFirewallPolicyConfig_ruleOrderOnly(rName, ruleOrder string) string {
return fmt.Sprintf(`
resource "aws_networkfirewall_firewall_policy" "test" {
Expand Down
46 changes: 46 additions & 0 deletions website/docs/r/networkfirewall_firewall_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,36 @@ resource "aws_networkfirewall_firewall_policy" "example" {
}
```

## Policy with a HOME_NET Override

```terraform
resource "aws_networkfirewall_firewall_policy" "example" {
name = "example"
firewall_policy {
policy_variables {
rule_variables {
key = "HOME_NET"
ip_set {
definition = ["10.0.0.0/16", "10.1.0.0/24"]
}
}
}
stateless_default_actions = ["aws:pass"]
stateless_fragment_default_actions = ["aws:drop"]
stateless_rule_group_reference {
priority = 1
resource_arn = aws_networkfirewall_rule_group.example.arn
}
}
tags = {
Tag1 = "Value1"
Tag2 = "Value2"
}
}
```

## Policy with a Custom Action for Stateless Inspection

```terraform
Expand Down Expand Up @@ -81,6 +111,8 @@ This resource supports the following arguments:

The `firewall_policy` block supports the following arguments:

* `policy_variables` - (Optional). Contains variables that you can use to override default Suricata settings in your firewall policy. See [Rule Variables](#rule-variables) for details.

* `stateful_default_actions` - (Optional) 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`.

* `stateful_engine_options` - (Optional) A configuration block that defines options on how the policy handles stateful rules. See [Stateful Engine Options](#stateful-engine-options) below for details.
Expand All @@ -97,6 +129,20 @@ In addition, you can specify custom actions that are compatible with your standa

* `stateless_rule_group_reference` - (Optional) Set of configuration blocks containing references to the stateless rule groups that are used in the policy. See [Stateless Rule Group Reference](#stateless-rule-group-reference) below for details.

### Rule Variables

The `rule_variables` block supports the following arguments:

* `key` - (Required) An alphanumeric string to identify the `ip_set`. Valid values: `HOME_NET`

* `ip_set` - (Required) A configuration block that defines a set of IP addresses. See [IP Set](#ip-set) below for details.

### IP Set

The `ip_set` block supports the following argument:

* `definition` - (Required) Set of IPv4 or IPv6 addresses in CIDR notation to use for the Suricata `HOME_NET` variable.

### Stateful Engine Options

The `stateful_engine_options` block supports the following argument:
Expand Down

0 comments on commit b5a5670

Please sign in to comment.