From e6f96aa516e675417f18538b53f7799ceda6f2e6 Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Mon, 4 Nov 2024 01:36:56 -0500 Subject: [PATCH] feat: Add flow_timeouts arg to stateful_engine_options block for aws_networkfirewall_firewall_policy --- .../networkfirewall/firewall_policy.go | 50 ++++++++ .../networkfirewall/firewall_policy_test.go | 113 ++++++++++++++++++ ...workfirewall_firewall_policy.html.markdown | 8 ++ 3 files changed, 171 insertions(+) diff --git a/internal/service/networkfirewall/firewall_policy.go b/internal/service/networkfirewall/firewall_policy.go index 3b05e33701ab..a167e91ed940 100644 --- a/internal/service/networkfirewall/firewall_policy.go +++ b/internal/service/networkfirewall/firewall_policy.go @@ -109,6 +109,21 @@ func resourceFirewallPolicy() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "flow_timeouts": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tcp_idle_timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 350, + ValidateFunc: validation.IntBetween(60, 6000), + }, + }, + }, + }, "rule_order": { Type: schema.TypeString, Optional: true, @@ -420,6 +435,9 @@ func expandStatefulEngineOptions(tfList []interface{}) *awstypes.StatefulEngineO tfMap := tfList[0].(map[string]interface{}) + if v, ok := tfMap["flow_timeouts"].([]interface{}); ok && len(v) > 0 { + apiObject.FlowTimeouts = expandFlowTimeouts(v) + } if v, ok := tfMap["rule_order"].(string); ok && v != "" { apiObject.RuleOrder = awstypes.RuleOrder(v) } @@ -430,6 +448,22 @@ func expandStatefulEngineOptions(tfList []interface{}) *awstypes.StatefulEngineO return apiObject } +func expandFlowTimeouts(tfList []interface{}) *awstypes.FlowTimeouts { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + apiObject := &awstypes.FlowTimeouts{} + + tfMap := tfList[0].(map[string]interface{}) + + if v, ok := tfMap["tcp_idle_timeout_seconds"].(int); ok && v > 0 { + apiObject.TcpIdleTimeoutSeconds = aws.Int32(int32(v)) + } + + return apiObject +} + func expandStatefulRuleGroupOverride(tfList []interface{}) *awstypes.StatefulRuleGroupOverride { if len(tfList) == 0 || tfList[0] == nil { return nil @@ -606,6 +640,22 @@ func flattenStatefulEngineOptions(apiObject *awstypes.StatefulEngineOptions) []i "stream_exception_policy": apiObject.StreamExceptionPolicy, } + if apiObject.FlowTimeouts != nil { + tfMap["flow_timeouts"] = flattenFlowTimeouts(apiObject.FlowTimeouts) + } + + return []interface{}{tfMap} +} + +func flattenFlowTimeouts(apiObject *awstypes.FlowTimeouts) []interface{} { + if apiObject == nil { + return []interface{}{} + } + + tfMap := map[string]interface{}{ + "tcp_idle_timeout_seconds": apiObject.TcpIdleTimeoutSeconds, + } + return []interface{}{tfMap} } diff --git a/internal/service/networkfirewall/firewall_policy_test.go b/internal/service/networkfirewall/firewall_policy_test.go index 3613f1172618..573e48956282 100644 --- a/internal/service/networkfirewall/firewall_policy_test.go +++ b/internal/service/networkfirewall/firewall_policy_test.go @@ -226,6 +226,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulEngineOption(t *testing.T) { testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", string(awstypes.RuleOrderStrictOrder)), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.stream_exception_policy", string(awstypes.StreamExceptionPolicyDrop)), ), @@ -257,6 +258,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatefulEngineOption(t *testing. testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy1), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", string(awstypes.RuleOrderDefaultActionOrder)), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.stream_exception_policy", string(awstypes.StreamExceptionPolicyContinue)), ), @@ -276,6 +278,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatefulEngineOption(t *testing. testAccCheckFirewallPolicyRecreated(&firewallPolicy2, &firewallPolicy3), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", string(awstypes.RuleOrderStrictOrder)), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.stream_exception_policy", string(awstypes.StreamExceptionPolicyReject)), ), @@ -307,6 +310,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulEngineOptionsSingle(t *testing testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", string(awstypes.RuleOrderDefaultActionOrder)), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.stream_exception_policy", ""), ), @@ -317,6 +321,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulEngineOptionsSingle(t *testing testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", ""), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.stream_exception_policy", string(awstypes.StreamExceptionPolicyReject)), ), @@ -330,6 +335,56 @@ func TestAccNetworkFirewallFirewallPolicy_statefulEngineOptionsSingle(t *testing }) } +func TestAccNetworkFirewallFirewallPolicy_statefulEngineOptionsFlowTimeouts(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, names.NetworkFirewallServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFirewallPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicyConfig_statefulEngineOptionsDefaultFlowTimeouts(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.0.tcp_idle_timeout_seconds", "350"), + ), + }, + { + Config: testAccFirewallPolicyConfig_statefulEngineOptionsFlowTimeouts(rName, "60"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.0.tcp_idle_timeout_seconds", "60"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccFirewallPolicyConfig_statefulEngineOptionsNoFlowTimeouts(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(ctx, resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.flow_timeouts.#", "0"), + ), + }, + }, + }) +} + func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReference(t *testing.T) { ctx := acctest.Context(t) var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput @@ -1277,6 +1332,64 @@ resource "aws_networkfirewall_firewall_policy" "test" { `, rName, ruleOrder, streamExceptionPolicy) } +func testAccFirewallPolicyConfig_statefulEngineOptionsNoFlowTimeouts(rName string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + + stateful_engine_options { + rule_order = "STRICT_ORDER" + stream_exception_policy = "DROP" + } + } +} +`, rName) +} + +func testAccFirewallPolicyConfig_statefulEngineOptionsDefaultFlowTimeouts(rName string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + + stateful_engine_options { + flow_timeouts {} + rule_order = "STRICT_ORDER" + stream_exception_policy = "DROP" + } + } +} +`, rName) +} + +func testAccFirewallPolicyConfig_statefulEngineOptionsFlowTimeouts(rName, tcpIdleTimeoutSeconds string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + + stateful_engine_options { + flow_timeouts { + tcp_idle_timeout_seconds = %[2]s + } + rule_order = "STRICT_ORDER" + stream_exception_policy = "DROP" + } + } +} +`, rName, tcpIdleTimeoutSeconds) +} + func testAccFirewallPolicyConfig_policyVariables(rName string) string { return fmt.Sprintf(` resource "aws_networkfirewall_firewall_policy" "test" { diff --git a/website/docs/r/networkfirewall_firewall_policy.html.markdown b/website/docs/r/networkfirewall_firewall_policy.html.markdown index ddb8001317b0..41bcade22333 100644 --- a/website/docs/r/networkfirewall_firewall_policy.html.markdown +++ b/website/docs/r/networkfirewall_firewall_policy.html.markdown @@ -152,10 +152,18 @@ The `stateful_engine_options` block supports the following argument: ~> **NOTE:** If the `STRICT_ORDER` rule order is specified, this firewall policy can only reference stateful rule groups that utilize `STRICT_ORDER`. +* `flow_timeouts` - (Optional) Amount of time that can pass without any traffic sent through the firewall before the firewall determines that the connection is idle. + * `rule_order` - Indicates how to manage the order of stateful rule evaluation for the policy. Default value: `DEFAULT_ACTION_ORDER`. Valid values: `DEFAULT_ACTION_ORDER`, `STRICT_ORDER`. * `stream_exception_policy` - Describes how to treat traffic which has broken midstream. Default value: `DROP`. Valid values: `DROP`, `CONTINUE`, `REJECT`. +### Flow Timeouts + +The `flow_timeouts` block supports the following argument: + +* `tcp_idle_timeout_seconds` - Number of seconds that can pass without any TCP traffic sent through the firewall before the firewall determines that the connection is idle. After the idle timeout passes, data packets are dropped, however, the next TCP SYN packet is considered a new flow and is processed by the firewall. Clients or targets can use TCP keepalive packets to reset the idle timeout. Default value: `350`. + ### Stateful Rule Group Reference The `stateful_rule_group_reference` block supports the following arguments: