diff --git a/.changelog/39996.txt b/.changelog/39996.txt new file mode 100644 index 00000000000..f85dfb72264 --- /dev/null +++ b/.changelog/39996.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_networkfirewall_firewall_policy: Add `stateful_engine_options.flow_timeouts` argument +``` diff --git a/internal/service/networkfirewall/firewall_policy.go b/internal/service/networkfirewall/firewall_policy.go index 3b05e33701a..a167e91ed94 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 3613f117261..573e4895628 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 ddb8001317b..41bcade2233 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: