diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 58c7966848a7..dde0bc336a77 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -919,6 +919,50 @@ func resourceArmApplicationGateway() *schema.Resource { ValidateFunc: validation.IntBetween(1, 128), Default: 128, }, + "disabled_rule_group": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_group_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "crs_20_protocol_violations", + "crs_21_protocol_anomalies", + "crs_23_request_limits", + "crs_30_http_policy", + "crs_35_bad_robots", + "crs_40_generic_attacks", + "crs_41_sql_injection_attacks", + "crs_41_xss_attacks", + "crs_42_tight_security", + "crs_45_trojans", + "REQUEST-911-METHOD-ENFORCEMENT", + "REQUEST-913-SCANNER-DETECTION", + "REQUEST-920-PROTOCOL-ENFORCEMENT", + "REQUEST-921-PROTOCOL-ATTACK", + "REQUEST-930-APPLICATION-ATTACK-LFI", + "REQUEST-931-APPLICATION-ATTACK-RFI", + "REQUEST-932-APPLICATION-ATTACK-RCE", + "REQUEST-933-APPLICATION-ATTACK-PHP", + "REQUEST-941-APPLICATION-ATTACK-XSS", + "REQUEST-942-APPLICATION-ATTACK-SQLI", + "REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION", + }, false), + }, + + "rules": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntAtLeast(1), + }, + }, + }, + }, + }, }, }, }, @@ -2620,6 +2664,7 @@ func expandApplicationGatewayWafConfig(d *schema.ResourceData) *network.Applicat FileUploadLimitInMb: utils.Int32(int32(fileUploadLimitInMb)), RequestBodyCheck: utils.Bool(requestBodyCheck), MaxRequestBodySizeInKb: utils.Int32(int32(maxRequestBodySizeInKb)), + DisabledRuleGroups: expandApplicationGatewayFirewallDisabledRuleGroup(v["disabled_rule_group"].([]interface{})), } } @@ -2645,6 +2690,10 @@ func flattenApplicationGatewayWafConfig(input *network.ApplicationGatewayWebAppl output["rule_set_version"] = *input.RuleSetVersion } + if input.DisabledRuleGroups != nil { + output["disabled_rule_group"] = flattenApplicationGateWayDisabledRuleGroups(input.DisabledRuleGroups) + } + if input.FileUploadLimitInMb != nil { output["file_upload_limit_mb"] = int(*input.FileUploadLimitInMb) } @@ -2662,6 +2711,58 @@ func flattenApplicationGatewayWafConfig(input *network.ApplicationGatewayWebAppl return results } +func expandApplicationGatewayFirewallDisabledRuleGroup(d []interface{}) *[]network.ApplicationGatewayFirewallDisabledRuleGroup { + if len(d) == 0 { + return nil + } + + disabledRuleGroups := make([]network.ApplicationGatewayFirewallDisabledRuleGroup, 0) + for _, disabledRuleGroup := range d { + disabledRuleGroupMap := disabledRuleGroup.(map[string]interface{}) + + ruleGroupName := disabledRuleGroupMap["rule_group_name"].(string) + + ruleGroup := network.ApplicationGatewayFirewallDisabledRuleGroup{ + RuleGroupName: utils.String(ruleGroupName), + } + + rules := make([]int32, 0) + for _, rule := range disabledRuleGroupMap["rules"].([]interface{}) { + rules = append(rules, int32(rule.(int))) + } + + if len(rules) > 0 { + ruleGroup.Rules = &rules + } + + disabledRuleGroups = append(disabledRuleGroups, ruleGroup) + } + return &disabledRuleGroups +} + +func flattenApplicationGateWayDisabledRuleGroups(input *[]network.ApplicationGatewayFirewallDisabledRuleGroup) []interface{} { + ruleGroups := make([]interface{}, 0) + for _, ruleGroup := range *input { + ruleGroupOutput := map[string]interface{}{} + + if ruleGroup.RuleGroupName != nil { + ruleGroupOutput["rule_group_name"] = *ruleGroup.RuleGroupName + } + + ruleOutputs := make([]interface{}, 0) + if rules := ruleGroup.Rules; rules != nil { + for _, rule := range *rules { + ruleOutputs = append(ruleOutputs, rule) + } + } + ruleGroupOutput["rules"] = ruleOutputs + + ruleGroups = append(ruleGroups, ruleGroupOutput) + + } + return ruleGroups +} + func expandApplicationGatewayCustomErrorConfigurations(vs []interface{}) *[]network.ApplicationGatewayCustomError { results := make([]network.ApplicationGatewayCustomError, 0) diff --git a/azurerm/resource_arm_application_gateway_test.go b/azurerm/resource_arm_application_gateway_test.go index 31079575000b..3fcf493ea048 100644 --- a/azurerm/resource_arm_application_gateway_test.go +++ b/azurerm/resource_arm_application_gateway_test.go @@ -536,6 +536,64 @@ func TestAccAzureRMApplicationGateway_connectionDraining(t *testing.T) { }) } +func TestAccAzureRMApplicationGateway_webApplicationFirewall_disabledRuleGroups(t *testing.T) { + resourceName := "azurerm_application_gateway.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationGateway_webApplicationFirewall_disabledRuleGroups(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "WAF_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.tier", "WAF_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.firewall_mode", "Detection"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.rule_set_type", "OWASP"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.rule_set_version", "3.0"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.request_body_check", "true"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.max_request_body_size_kb", "128"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.file_upload_limit_mb", "100"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rule_group_name", "REQUEST-921-PROTOCOL-ATTACK"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.0", "921110"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.1", "921151"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.2", "921180"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.1.rule_group_name", "REQUEST-930-APPLICATION-ATTACK-LFI"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.1.rules.0", "930120"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.1.rules.1", "930130"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.2.rule_group_name", "REQUEST-942-APPLICATION-ATTACK-SQLI"), + ), + }, + { + Config: testAccAzureRMApplicationGateway_webApplicationFirewall_disabledRuleGroups_enabled_some_rules(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationGatewayExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "sku.0.name", "WAF_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.tier", "WAF_v2"), + resource.TestCheckResourceAttr(resourceName, "sku.0.capacity", "1"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.firewall_mode", "Detection"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.rule_set_type", "OWASP"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.rule_set_version", "3.0"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.request_body_check", "true"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.max_request_body_size_kb", "128"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.file_upload_limit_mb", "100"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rule_group_name", "REQUEST-921-PROTOCOL-ATTACK"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.0", "921110"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.1", "921151"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.0.rules.2", "921180"), + resource.TestCheckResourceAttr(resourceName, "waf_configuration.0.disabled_rule_group.1.rule_group_name", "REQUEST-942-APPLICATION-ATTACK-SQLI"), + ), + }, + }, + }) +} + func testCheckAzureRMApplicationGatewayExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -2074,6 +2132,212 @@ resource "azurerm_application_gateway" "test" { } `, template, rInt) } +func testAccAzureRMApplicationGateway_webApplicationFirewall_disabledRuleGroups(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + + +resource "azurerm_public_ip" "test_standard" { + name = "acctest-pubip-%d-standard" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + allocation_method = "Static" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "WAF_v2" + tier = "WAF_v2" + capacity = 1 + } + + waf_configuration { + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 + + disabled_rule_group { + rule_group_name = "REQUEST-921-PROTOCOL-ATTACK" + rules = [921110, 921151, 921180] + } + + disabled_rule_group { + rule_group_name = "REQUEST-930-APPLICATION-ATTACK-LFI" + rules = [930120, 930130] + } + + disabled_rule_group { + rule_group_name = "REQUEST-942-APPLICATION-ATTACK-SQLI" + } + + + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMApplicationGateway_webApplicationFirewall_disabledRuleGroups_enabled_some_rules(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_template(rInt, location) + return fmt.Sprintf(` +%s + +# since these variables are re-used - a locals block makes this more maintainable +locals { + backend_address_pool_name = "${azurerm_virtual_network.test.name}-beap" + frontend_port_name = "${azurerm_virtual_network.test.name}-feport" + frontend_ip_configuration_name = "${azurerm_virtual_network.test.name}-feip" + http_setting_name = "${azurerm_virtual_network.test.name}-be-htst" + listener_name = "${azurerm_virtual_network.test.name}-httplstn" + request_routing_rule_name = "${azurerm_virtual_network.test.name}-rqrt" +} + + +resource "azurerm_public_ip" "test_standard" { + name = "acctest-pubip-%d-standard" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + allocation_method = "Static" +} + +resource "azurerm_application_gateway" "test" { + name = "acctestag-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + + sku { + name = "WAF_v2" + tier = "WAF_v2" + capacity = 1 + } + + waf_configuration { + enabled = true + firewall_mode = "Detection" + rule_set_type = "OWASP" + rule_set_version = "3.0" + request_body_check = true + max_request_body_size_kb = 128 + file_upload_limit_mb = 100 + + disabled_rule_group { + rule_group_name = "REQUEST-921-PROTOCOL-ATTACK" + rules = [921110, 921151, 921180] + } + + disabled_rule_group { + rule_group_name = "REQUEST-942-APPLICATION-ATTACK-SQLI" + } + + + } + + gateway_ip_configuration { + name = "my-gateway-ip-configuration" + subnet_id = "${azurerm_subnet.test.id}" + } + + frontend_port { + name = "${local.frontend_port_name}" + port = 80 + } + + frontend_ip_configuration { + name = "${local.frontend_ip_configuration_name}" + public_ip_address_id = "${azurerm_public_ip.test_standard.id}" + } + + backend_address_pool { + name = "${local.backend_address_pool_name}" + } + + backend_http_settings { + name = "${local.http_setting_name}" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 1 + } + + http_listener { + name = "${local.listener_name}" + frontend_ip_configuration_name = "${local.frontend_ip_configuration_name}" + frontend_port_name = "${local.frontend_port_name}" + protocol = "Http" + } + + request_routing_rule { + name = "${local.request_routing_rule_name}" + rule_type = "Basic" + http_listener_name = "${local.listener_name}" + backend_address_pool_name = "${local.backend_address_pool_name}" + backend_http_settings_name = "${local.http_setting_name}" + } +} +`, template, rInt, rInt) +} func testAccAzureRMApplicationGateway_template(rInt int, location string) string { return fmt.Sprintf(` diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index c90463dc6147..09ca0409a8a4 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -391,6 +391,8 @@ A `waf_configuration` block supports the following: * `rule_set_version` - (Required) The Version of the Rule Set used for this Web Application Firewall. +* `disabled_rule_group` - (Optional) one or more `disabled_rule_group` blocks as defined below. + * `file_upload_limit_mb` - (Optional) The File Upload Limit in MB. Accepted values are in the range `1`MB to `500`MB. Defaults to `100`MB. * `request_body_check` - (Optional) Is Request Body Inspection enabled? Defaults to `true`. @@ -399,6 +401,14 @@ A `waf_configuration` block supports the following: --- +A `disabled_rule_group` block supports the following: + +* `rule_group_name` - (Required) The rule group where specific rules should be disabled. Accepted values are: `crs_20_protocol_violations`, `crs_21_protocol_anomalies`, `crs_23_request_limits`, `crs_30_http_policy`, `crs_35_bad_robots`, `crs_40_generic_attacks`, `crs_41_sql_injection_attacks`, `crs_41_xss_attacks`, `crs_42_tight_security`, `crs_45_trojans`, `REQUEST-911-METHOD-ENFORCEMENT`, `REQUEST-913-SCANNER-DETECTION`, `REQUEST-920-PROTOCOL-ENFORCEMENT`, `REQUEST-921-PROTOCOL-ATTACK`, `REQUEST-930-APPLICATION-ATTACK-LFI`, `REQUEST-931-APPLICATION-ATTACK-RFI`, `REQUEST-932-APPLICATION-ATTACK-RCE`, `REQUEST-933-APPLICATION-ATTACK-PHP`, `REQUEST-941-APPLICATION-ATTACK-XSS`, `REQUEST-942-APPLICATION-ATTACK-SQLI`, `REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION` + +* `rules` - (Optional) A list of rules which should be disabled in that group. Disables all rules in the specified group if `rules` is not specified. + +--- + A `custom_error_configuration` block supports the following: * `status_code` - (Required) Status code of the application gateway customer error. Possible values are `HttpStatus403` and `HttpStatus502`