From 2927119acec6b9ee52f8e2ca04e5bce1de18d8db Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Fri, 2 May 2025 11:11:06 -0400 Subject: [PATCH 1/8] Add firewall config bot filter support --- client/firewall_config.go | 3 +- .../vercel_firewall_config/resource.tf | 5 ++ vercel/resource_firewall_config.go | 46 +++++++++++++--- vercel/resource_firewall_config_test.go | 52 +++++++++++++++++++ 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/client/firewall_config.go b/client/firewall_config.go index 4fa03916..e04d7e6d 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"` } type FirewallRule struct { 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..13eafebe 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 rulesets 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 { + cfg.ManagedRulesets.OWASP = fromCRS(conf.CRS, state.ManagedRulesets) + } + v, exist := conf.ManagedRulesets["bot_filter"] + if exist { + botFilterConf := &BotFilterConfig{ + Active: types.BoolValue(v.Active), + Action: types.StringValue(v.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..d1f2b5e1 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,17 @@ resource "vercel_firewall_config" "neg" { } } } + +resource "vercel_firewall_config" "botfilter" { + project_id = vercel_project.botfilter.id + %[2]s + + managed_rulesets { + bot_filter { + action = "deny" + active = false + } + } +} `, name, teamID) } From 287704dd7e054cffcfc2b7c9abf571bf24d0ee56 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Fri, 2 May 2025 11:13:18 -0400 Subject: [PATCH 2/8] typo --- vercel/resource_firewall_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel/resource_firewall_config.go b/vercel/resource_firewall_config.go index 13eafebe..e3f63781 100644 --- a/vercel/resource_firewall_config.go +++ b/vercel/resource_firewall_config.go @@ -171,7 +171,7 @@ 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 rulesets and select action", + Description: "Enable the bot_filter managed ruleset and select action", Attributes: map[string]schema.Attribute{ "active": schema.BoolAttribute{ Optional: true, From 5431c4e814f7aefb2a562d6ad158ada81f714a03 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Fri, 2 May 2025 11:17:28 -0400 Subject: [PATCH 3/8] cleanup --- vercel/resource_firewall_config.go | 8 ++++---- vercel/resource_firewall_config_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vercel/resource_firewall_config.go b/vercel/resource_firewall_config.go index e3f63781..eaf5fac5 100644 --- a/vercel/resource_firewall_config.go +++ b/vercel/resource_firewall_config.go @@ -861,11 +861,11 @@ func fromClient(conf client.FirewallConfig, state FirewallConfig) (FirewallConfi if conf.CRS != nil { cfg.ManagedRulesets.OWASP = fromCRS(conf.CRS, state.ManagedRulesets) } - v, exist := conf.ManagedRulesets["bot_filter"] - if exist { + botFilter, botFilterExist := conf.ManagedRulesets["bot_filter"] + if botFilterExist { botFilterConf := &BotFilterConfig{ - Active: types.BoolValue(v.Active), - Action: types.StringValue(v.Action), + Active: types.BoolValue(botFilter.Active), + Action: types.StringValue(botFilter.Action), } cfg.ManagedRulesets.BotFilter = botFilterConf } diff --git a/vercel/resource_firewall_config_test.go b/vercel/resource_firewall_config_test.go index d1f2b5e1..f23848ab 100644 --- a/vercel/resource_firewall_config_test.go +++ b/vercel/resource_firewall_config_test.go @@ -702,7 +702,7 @@ resource "vercel_firewall_config" "botfilter" { managed_rulesets { bot_filter { - action = "deny" + action = "log" active = false } } From 7f662ffd81d7c46bcd94bf3c893746229875c424 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Fri, 2 May 2025 11:24:04 -0400 Subject: [PATCH 4/8] generate docs --- docs/resources/firewall_config.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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)) From affc7900759be56ddfa2023c069583194cc7fbe0 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Fri, 2 May 2025 11:34:29 -0400 Subject: [PATCH 5/8] add missing project to tests --- vercel/resource_firewall_config_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vercel/resource_firewall_config_test.go b/vercel/resource_firewall_config_test.go index f23848ab..049d1852 100644 --- a/vercel/resource_firewall_config_test.go +++ b/vercel/resource_firewall_config_test.go @@ -696,6 +696,11 @@ 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 From 07b6f92b2d3c308e382ba2c51b4099c2e724c411 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Sat, 3 May 2025 04:18:54 -0400 Subject: [PATCH 6/8] fix test action --- vercel/resource_firewall_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel/resource_firewall_config_test.go b/vercel/resource_firewall_config_test.go index 049d1852..3a84b19b 100644 --- a/vercel/resource_firewall_config_test.go +++ b/vercel/resource_firewall_config_test.go @@ -707,7 +707,7 @@ resource "vercel_firewall_config" "botfilter" { managed_rulesets { bot_filter { - action = "log" + action = "deny" active = false } } From 60e7a8ead370811d4905bb27e995f5e09a95a540 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Sat, 3 May 2025 04:19:05 -0400 Subject: [PATCH 7/8] fix condition --- vercel/resource_firewall_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel/resource_firewall_config.go b/vercel/resource_firewall_config.go index eaf5fac5..7a3bcb4a 100644 --- a/vercel/resource_firewall_config.go +++ b/vercel/resource_firewall_config.go @@ -858,7 +858,7 @@ func fromClient(conf client.FirewallConfig, state FirewallConfig) (FirewallConfi if len(conf.ManagedRulesets) > 0 { managedRulesets := &FirewallManagedRulesets{} cfg.ManagedRulesets = managedRulesets - if conf.CRS != nil { + if conf.CRS != nil && state.ManagedRulesets != nil { cfg.ManagedRulesets.OWASP = fromCRS(conf.CRS, state.ManagedRulesets) } botFilter, botFilterExist := conf.ManagedRulesets["bot_filter"] From 5ad667b8a936c6a0c86715575e8b9bbc5e4c3f09 Mon Sep 17 00:00:00 2001 From: OhMyVolk Date: Sat, 3 May 2025 04:19:14 -0400 Subject: [PATCH 8/8] default empty action for ManagedRule --- client/firewall_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/firewall_config.go b/client/firewall_config.go index e04d7e6d..51f3855a 100644 --- a/client/firewall_config.go +++ b/client/firewall_config.go @@ -17,7 +17,7 @@ type FirewallConfig struct { } type ManagedRule struct { Active bool `json:"active"` - Action string `json:"action"` + Action string `json:"action,omitempty"` } type FirewallRule struct {