diff --git a/client/firewall_config.go b/client/firewall_config.go index 4fa03916..51f3855a 100644 --- a/client/firewall_config.go +++ b/client/firewall_config.go @@ -16,7 +16,8 @@ type FirewallConfig struct { CRS map[string]CoreRuleSet `json:"crs,omitempty"` } type ManagedRule struct { - Active bool `json:"active"` + Active bool `json:"active"` + Action string `json:"action,omitempty"` } type FirewallRule struct { diff --git a/docs/resources/firewall_config.md b/docs/resources/firewall_config.md index 0891e48c..ba7f8b5d 100644 --- a/docs/resources/firewall_config.md +++ b/docs/resources/firewall_config.md @@ -157,6 +157,11 @@ resource "vercel_firewall_config" "managed" { rfi = { action = "deny" } gen = { action = "deny" } } + + bot_filter { + action = "log" + active = true + } } } @@ -230,8 +235,18 @@ Read-Only: Optional: +- `bot_filter` (Block, Optional) Enable the bot_filter managed ruleset and select action (see [below for nested schema](#nestedblock--managed_rulesets--bot_filter)) - `owasp` (Block, Optional) Enable the owasp managed rulesets and select ruleset behaviors (see [below for nested schema](#nestedblock--managed_rulesets--owasp)) + +### Nested Schema for `managed_rulesets.bot_filter` + +Optional: + +- `action` (String) +- `active` (Boolean) + + ### Nested Schema for `managed_rulesets.owasp` @@ -404,7 +419,7 @@ Required: Optional: -- `action_duration` (String) Forward persistence of a rule aciton +- `action_duration` (String) Forward persistence of a rule action - `rate_limit` (Attributes) Behavior or a rate limiting action. Required if action is rate_limit (see [below for nested schema](#nestedatt--rules--rule--action--rate_limit)) - `redirect` (Attributes) How to redirect a request. Required if action is redirect (see [below for nested schema](#nestedatt--rules--rule--action--redirect)) diff --git a/examples/resources/vercel_firewall_config/resource.tf b/examples/resources/vercel_firewall_config/resource.tf index 4fff6455..4b2099eb 100644 --- a/examples/resources/vercel_firewall_config/resource.tf +++ b/examples/resources/vercel_firewall_config/resource.tf @@ -142,6 +142,11 @@ resource "vercel_firewall_config" "managed" { rfi = { action = "deny" } gen = { action = "deny" } } + + bot_filter { + action = "log" + active = true + } } } diff --git a/vercel/resource_firewall_config.go b/vercel/resource_firewall_config.go index f2d44c84..7a3bcb4a 100644 --- a/vercel/resource_firewall_config.go +++ b/vercel/resource_firewall_config.go @@ -170,6 +170,17 @@ Define Custom Rules to shape the way your traffic is handled by the Vercel Edge }, }, }, + "bot_filter": schema.SingleNestedBlock{ + Description: "Enable the bot_filter managed ruleset and select action", + Attributes: map[string]schema.Attribute{ + "active": schema.BoolAttribute{ + Optional: true, + }, + "action": schema.StringAttribute{ + Optional: true, + }, + }, + }, }, }, "rules": schema.SingleNestedBlock{ @@ -255,7 +266,7 @@ Define Custom Rules to shape the way your traffic is handled by the Vercel Edge }, }, "action_duration": schema.StringAttribute{ - Description: "Forward persistence of a rule aciton", + Description: "Forward persistence of a rule action", Optional: true, }, }, @@ -443,7 +454,8 @@ type FirewallConfig struct { } type FirewallManagedRulesets struct { - OWASP *CRSRule `tfsdk:"owasp"` + OWASP *CRSRule `tfsdk:"owasp"` + BotFilter *BotFilterConfig `tfsdk:"bot_filter"` } type CRSRule struct { @@ -479,6 +491,11 @@ type CRSRuleConfig struct { Action types.String `tfsdk:"action"` } +type BotFilterConfig struct { + Active types.Bool `tfsdk:"active"` + Action types.String `tfsdk:"action"` +} + type FirewallRules struct { Rules []FirewallRule `tfsdk:"rule"` } @@ -756,7 +773,7 @@ func fromCRS(conf map[string]client.CoreRuleSet, refMr *FirewallManagedRulesets) if refMr != nil && refMr.OWASP != nil { ref = refMr.OWASP } - if conf == nil || ref == nil { + if conf == nil || ref == nil || refMr.OWASP == nil { return nil } return &CRSRule{ @@ -838,10 +855,20 @@ func fromClient(conf client.FirewallConfig, state FirewallConfig) (FirewallConfi cfg.IPRules = &IPRules{Rules: ipRules} } - managedRulesets := &FirewallManagedRulesets{} - if conf.ManagedRulesets != nil && conf.CRS != nil { + if len(conf.ManagedRulesets) > 0 { + managedRulesets := &FirewallManagedRulesets{} cfg.ManagedRulesets = managedRulesets - cfg.ManagedRulesets.OWASP = fromCRS(conf.CRS, state.ManagedRulesets) + if conf.CRS != nil && state.ManagedRulesets != nil { + cfg.ManagedRulesets.OWASP = fromCRS(conf.CRS, state.ManagedRulesets) + } + botFilter, botFilterExist := conf.ManagedRulesets["bot_filter"] + if botFilterExist { + botFilterConf := &BotFilterConfig{ + Active: types.BoolValue(botFilter.Active), + Action: types.StringValue(botFilter.Action), + } + cfg.ManagedRulesets.BotFilter = botFilterConf + } } return cfg, nil @@ -870,6 +897,13 @@ func (f *FirewallConfig) toClient() (client.FirewallConfig, error) { } } } + botFilter := f.ManagedRulesets.BotFilter + if botFilter != nil { + conf.ManagedRulesets["bot_filter"] = client.ManagedRule{ + Active: botFilter.Active.ValueBool(), + Action: botFilter.Action.ValueString(), + } + } } if f.Rules != nil && len(f.Rules.Rules) > 0 { for _, rule := range f.Rules.Rules { diff --git a/vercel/resource_firewall_config_test.go b/vercel/resource_firewall_config_test.go index 07e10ba7..3a84b19b 100644 --- a/vercel/resource_firewall_config_test.go +++ b/vercel/resource_firewall_config_test.go @@ -169,6 +169,15 @@ func TestAcc_FirewallConfigResource(t *testing.T) { "vercel_firewall_config.ips", "ip_rules.rule.2.hostname", "*"), + + resource.TestCheckResourceAttr( + "vercel_firewall_config.botfilter", + "managed_rulesets.bot_filter.action", + "challenge"), + resource.TestCheckResourceAttr( + "vercel_firewall_config.botfilter", + "managed_rulesets.bot_filter.active", + "true"), ), }, { @@ -186,6 +195,11 @@ func TestAcc_FirewallConfigResource(t *testing.T) { ResourceName: "vercel_firewall_config.ips", ImportStateIdFunc: getFirewallImportID("vercel_firewall_config.ips"), }, + { + ImportState: true, + ResourceName: "vercel_firewall_config.botfilter", + ImportStateIdFunc: getFirewallImportID("vercel_firewall_config.botfilter"), + }, { Config: testAccFirewallConfigResourceUpdated(name, teamIDConfig(t)), Check: resource.ComposeAggregateTestCheckFunc( @@ -329,6 +343,15 @@ func TestAcc_FirewallConfigResource(t *testing.T) { "vercel_firewall_config.ips", "ip_rules.rule.2.hostname", "*"), + + resource.TestCheckResourceAttr( + "vercel_firewall_config.botfilter", + "managed_rulesets.bot_filter.action", + "deny"), + resource.TestCheckResourceAttr( + "vercel_firewall_config.botfilter", + "managed_rulesets.bot_filter.active", + "false"), ), }, }, @@ -485,6 +508,23 @@ resource "vercel_firewall_config" "ips" { } } +resource "vercel_project" "botfilter" { + name = "test-acc-%[1]s-botfilter" + %[2]s +} + +resource "vercel_firewall_config" "botfilter" { + project_id = vercel_project.botfilter.id + %[2]s + + managed_rulesets { + bot_filter { + action = "challenge" + active = true + } + } +} + `, name, teamID) } @@ -655,5 +695,22 @@ resource "vercel_firewall_config" "neg" { } } } + +resource "vercel_project" "botfilter" { + name = "test-acc-%[1]s-botfilter" + %[2]s +} + +resource "vercel_firewall_config" "botfilter" { + project_id = vercel_project.botfilter.id + %[2]s + + managed_rulesets { + bot_filter { + action = "deny" + active = false + } + } +} `, name, teamID) }