Skip to content

Commit

Permalink
Adding unknownClientsChallenge and blockNonEssentialBots to the waf s…
Browse files Browse the repository at this point in the history
…ecurity ddos rules (#459)
  • Loading branch information
Pavel-Koev authored Oct 1, 2024
1 parent 588ad54 commit 7701e1c
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 29 deletions.
10 changes: 6 additions & 4 deletions examples/example.tf
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,12 @@ resource "incapsula_security_rule_exception" "example-waf-cross-site-scripting-r

# api.acl.ddos Security Rule (one instance per site)
resource "incapsula_waf_security_rule" "example-waf-ddos-rule" {
site_id = incapsula_site.example-site.id
rule_id = "api.threats.ddos"
activation_mode = "api.threats.ddos.activation_mode.on"
ddos_traffic_threshold = "5000"
site_id = incapsula_site.example-site.id
rule_id = "api.threats.ddos"
activation_mode = "api.threats.ddos.activation_mode.on"
ddos_traffic_threshold = "5000"
unknown_clients_challenge = "none"
block_non_essential_bots = "false"
}

# api.threats.ddos Security Rule Sample Exception
Expand Down
4 changes: 3 additions & 1 deletion examples/security_rules.tf
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ resource "incapsula_waf_security_rule" "example-waf-ddos-rule" {
site_id = incapsula_site.example-site.id
rule_id = "api.threats.ddos"
activation_mode = "api.threats.ddos.activation_mode.on"
ddos_traffic_threshold = "5000"
ddos_traffic_threshold = "5000"
unknown_clients_challenge = "none"
block_non_essential_bots = "false"
}

####################################################################
Expand Down
22 changes: 12 additions & 10 deletions incapsula/client_site.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,18 @@ type SiteStatusResponse struct {
Security struct {
Waf struct {
Rules []struct {
Action string `json:"action,omitempty"`
ActionText string `json:"action_text,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
BlockBadBots bool `json:"block_bad_bots,omitempty"`
ChallengeSuspectedBots bool `json:"challenge_suspected_bots,omitempty"`
ActivationMode string `json:"activation_mode,omitempty"`
ActivationModeText string `json:"activation_mode_text,omitempty"`
DdosTrafficThreshold int `json:"ddos_traffic_threshold,omitempty"`
Exceptions []struct {
Action string `json:"action,omitempty"`
ActionText string `json:"action_text,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
BlockBadBots bool `json:"block_bad_bots,omitempty"`
ChallengeSuspectedBots bool `json:"challenge_suspected_bots,omitempty"`
ActivationMode string `json:"activation_mode,omitempty"`
ActivationModeText string `json:"activation_mode_text,omitempty"`
DdosTrafficThreshold int `json:"ddos_traffic_threshold,omitempty"`
UnknownClientsChallenge string `json:"unknown_clients_challenge,omitempty"`
BlockNonEssentialBots bool `json:"block_non_essential_bots,omitempty"`
Exceptions []struct {
Values []struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Expand Down
11 changes: 9 additions & 2 deletions incapsula/client_waf_security_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const botAccessControlRuleID = "api.threats.bot_access_control"
const customRuleDefaultActionID = "api.threats.customRule"

// ConfigureWAFSecurityRule adds an WAF rule
func (c *Client) ConfigureWAFSecurityRule(siteID int, ruleID, securityRuleAction, activationMode, ddosTrafficThreshold, blockBadBots, challengeSuspectedBots string) (*SiteStatusResponse, error) {
func (c *Client) ConfigureWAFSecurityRule(siteID int, ruleID, securityRuleAction, activationMode, ddosTrafficThreshold, unknownClientsChallenge, blockNonEssentialBots, blockBadBots, challengeSuspectedBots string) (*SiteStatusResponse, error) {
// Base URL values
values := url.Values{
"site_id": {strconv.Itoa(siteID)},
Expand All @@ -37,7 +37,14 @@ func (c *Client) ConfigureWAFSecurityRule(siteID int, ruleID, securityRuleAction
} else if ruleID == ddosRuleID {
values.Add("activation_mode", activationMode)
values.Add("ddos_traffic_threshold", ddosTrafficThreshold)
log.Printf("[INFO] Configuring Incapsula WAF rule id (%s) with activation mode (%s) and DDoS traffic threshold (%s) for site id (%d)\n", ruleID, activationMode, ddosTrafficThreshold, siteID)

if unknownClientsChallenge != "" {
values.Add("unknown_clients_challenge", unknownClientsChallenge)
}
if blockNonEssentialBots != "" {
values.Add("block_non_essential_bots", blockNonEssentialBots)
}
log.Printf("[INFO] Configuring Incapsula WAF rule id (%s) with activation mode (%s), DDoS traffic threshold (%s), unknown clients challenge (%s) and block non essential bots (%s) for site id (%d)\n", ruleID, activationMode, ddosTrafficThreshold, unknownClientsChallenge, blockNonEssentialBots, siteID)
} else if ruleID == botAccessControlRuleID {
values.Add("block_bad_bots", blockBadBots)
values.Add("challenge_suspected_bots", challengeSuspectedBots)
Expand Down
24 changes: 14 additions & 10 deletions incapsula/client_waf_security_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestClientConfigureWAFSecurityRuleBadConnection(t *testing.T) {
siteID := 1234
ruleID := "api.threats.backdoor"
securityRuleAction := "badRuleAction"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -50,7 +50,7 @@ func TestClientConfigureWAFSecurityRuleBadJSON(t *testing.T) {
siteID := 1234
ruleID := "api.threats.backdoor"
securityRuleAction := "badRuleAction"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestClientConfigureWAFSecurityRuleInvalidRuleID(t *testing.T) {
siteID := 1234
ruleID := "bad_rule_id"
securityRuleAction := "bad_rule_action"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -106,7 +106,7 @@ func TestClientConfigureWAFSecurityRuleInvalidRuleAction(t *testing.T) {
siteID := 1234
ruleID := "api.threats.backdoor"
securityRuleAction := "bad_rule_action"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -135,7 +135,9 @@ func TestClientConfigureWAFSecurityRuleInvalidRule_activationMode(t *testing.T)
ruleID := backdoorRuleID
activationMode := "api.threats.ddos.activation_mode.on"
ddosTrafficThreshold := "123"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", activationMode, ddosTrafficThreshold, "", "")
unknownClientsChallenge := "none"
blockNonEssentialBots := "false"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", activationMode, ddosTrafficThreshold, unknownClientsChallenge, blockNonEssentialBots, "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -164,7 +166,9 @@ func TestClientConfigureWAFSecurityRuleInvalidRule_ddosThreshold(t *testing.T) {
ruleID := ddosRuleID
activationMode := "api.threats.ddos.activation_mode.on"
ddosTrafficThreshold := "123"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", activationMode, ddosTrafficThreshold, "", "")
unknownClientsChallenge := "none"
blockNonEssentialBots := "false"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", activationMode, ddosTrafficThreshold, unknownClientsChallenge, blockNonEssentialBots, "", "")
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -193,7 +197,7 @@ func TestClientConfigureWAFSecurityRuleInvalidRule_blockBadBots(t *testing.T) {
ruleID := botAccessControlRuleID
challengeSuspectedBots := "true"
blockBadBots := "123"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", "", "", blockBadBots, challengeSuspectedBots)
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", "", "", "", "", blockBadBots, challengeSuspectedBots)
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -222,7 +226,7 @@ func TestClientConfigureWAFSecurityRuleInvalidRule_challengeSuspectedBots(t *tes
ruleID := botAccessControlRuleID
challengeSuspectedBots := "123"
blockBadBots := "true"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", "", "", blockBadBots, challengeSuspectedBots)
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, "", "", "", "", "", blockBadBots, challengeSuspectedBots)
if err == nil {
t.Errorf("Should have received an error")
}
Expand Down Expand Up @@ -254,7 +258,7 @@ func TestClientConfigureWAFSecurityRuleValidRule(t *testing.T) {
siteID := 1234
ruleID := backdoorRuleID
securityRuleAction := "api.threats.action.quarantine_url"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err != nil {
t.Errorf("Should not have received an error")
}
Expand All @@ -279,7 +283,7 @@ func TestClientConfigureWAFSecurityRuleResultCodeStringValidRule(t *testing.T) {
siteID := 1234
ruleID := backdoorRuleID
securityRuleAction := "api.threats.action.quarantine_url"
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "")
configureWAFSecurityRuleResponse, err := client.ConfigureWAFSecurityRule(siteID, ruleID, securityRuleAction, "", "", "", "", "", "")
if err != nil {
t.Errorf("Should not have received an error")
}
Expand Down
43 changes: 41 additions & 2 deletions incapsula/resource_waf_security_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package incapsula

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"log"
"strconv"
"strings"
Expand All @@ -18,6 +19,8 @@ const remoteFileInclusionRuleIDDefaultAction = "api.threats.action.block_request
const sqlInjectionRuleIDDefaultAction = "api.threats.action.block_request"
const ddosRuleIDDefaultActivationMode = "api.threats.ddos.activation_mode.auto"
const ddosRuleIDDefaultDDOSTrafficThreshold = "1000"
const ddosRuleIDDefaultDDOSUnknownClientsChallenge = "cookies"
const ddosRuleIDDefaultDDOSBlockNonEssentialBots = "false"
const botAccessControlBlockBadBotsDefaultAction = "true"
const botAccessControlChallengeSuspectedBotsDefaultAction = "false"

Expand Down Expand Up @@ -79,6 +82,20 @@ func resourceWAFSecurityRule() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"unknown_clients_challenge": {
Description: "Defines a method used for challenging suspicious bots. Possible values: none, cookies, javascript, captcha",
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"none", "cookies", "javascript", "captcha"}, false),
},
"block_non_essential_bots": {
Description: "If non-essential bots should be blocked or not. Possible values: true, false",
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"true", "false"}, false),
},

// Required for rule_id: api.threats.bot_access_control
"block_bad_bots": {
Expand Down Expand Up @@ -111,6 +128,8 @@ func resourceWAFSecurityRuleCreate(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not create Incapsula WAF Rule rule_id (%s) and security_rule_action (%s) on site_id (%d), %s\n", ruleID, d.Get("security_rule_action").(string), d.Get("site_id").(int), err)
Expand All @@ -123,11 +142,13 @@ func resourceWAFSecurityRuleCreate(d *schema.ResourceData, m interface{}) error
"",
d.Get("activation_mode").(string),
d.Get("ddos_traffic_threshold").(string),
d.Get("unknown_clients_challenge").(string),
d.Get("block_non_essential_bots").(string),
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not create Incapsula WAF Rule rule_id (%s) with activation_mode (%s) and ddos_traffic_threshold (%s) on site_id (%d), %s\n", ruleID, d.Get("activation_mode").(string), d.Get("ddos_traffic_threshold").(string), d.Get("site_id").(int), err)
log.Printf("[ERROR] Could not create Incapsula WAF Rule rule_id (%s) with activation_mode (%s), ddos_traffic_threshold (%s), unknown_clients_challenge (%s) and block_non_essential_bots (%s) on site_id (%d), %s\n", ruleID, d.Get("activation_mode").(string), d.Get("ddos_traffic_threshold").(string), d.Get("unknown_clients_challenge").(string), d.Get("block_non_essential_bots").(string), d.Get("site_id").(int), err)
return err
}
} else if ruleID == botAccessControlRuleID {
Expand All @@ -137,6 +158,8 @@ func resourceWAFSecurityRuleCreate(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
d.Get("block_bad_bots").(string),
d.Get("challenge_suspected_bots").(string),
)
Expand Down Expand Up @@ -197,6 +220,8 @@ func resourceWAFSecurityRuleRead(d *schema.ResourceData, m interface{}) error {
case ddosRuleID:
d.Set("activation_mode", entry.ActivationMode)
d.Set("ddos_traffic_threshold", strconv.FormatInt(int64(entry.DdosTrafficThreshold), 10))
d.Set("unknown_clients_challenge", entry.UnknownClientsChallenge)
d.Set("block_non_essential_bots", strconv.FormatBool(entry.BlockNonEssentialBots))
case botAccessControlRuleID:
d.Set("block_bad_bots", strconv.FormatBool(entry.BlockBadBots))
d.Set("challenge_suspected_bots", strconv.FormatBool(entry.ChallengeSuspectedBots))
Expand Down Expand Up @@ -260,6 +285,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with security_rule_action (%s) on site_id (%d) %s\n", ruleID, backdoorRuleIDDefaultAction, d.Get("site_id").(int), err)
Expand All @@ -274,6 +301,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with security_rule_action (%s) on site_id (%d) %s\n", ruleID, crossSiteScriptingRuleIDDefaultAction, d.Get("site_id").(int), err)
Expand All @@ -288,6 +317,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with security_rule_action (%s) on site_id (%d) %s\n", ruleID, illegalResourceAccessRuleIDDefaultAction, d.Get("site_id").(int), err)
Expand All @@ -302,6 +333,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with security_rule_action (%s) on site_id (%d) %s\n", ruleID, remoteFileInclusionRuleIDDefaultAction, d.Get("site_id").(int), err)
Expand All @@ -316,6 +349,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with security_rule_action (%s) on site_id (%d) %s\n", ruleID, sqlInjectionRuleIDDefaultAction, d.Get("site_id").(int), err)
Expand All @@ -328,11 +363,13 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
ddosRuleIDDefaultActivationMode,
ddosRuleIDDefaultDDOSTrafficThreshold,
ddosRuleIDDefaultDDOSUnknownClientsChallenge,
ddosRuleIDDefaultDDOSBlockNonEssentialBots,
"",
"",
)
if err != nil {
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with default_activation_mode (%s) and ddos_traffic_threshold (%s) on site_id (%d) %s\n", ruleID, ddosRuleIDDefaultActivationMode, ddosRuleIDDefaultDDOSTrafficThreshold, d.Get("site_id").(int), err)
log.Printf("[ERROR] Could not reset Incapsula WAF Rule rule_id (%s) with default_activation_mode (%s), ddos_traffic_threshold (%s), unknown_clients_challenge (%s) and block_non_essential_bots (%s) on site_id (%d) %s\n", ruleID, ddosRuleIDDefaultActivationMode, ddosRuleIDDefaultDDOSTrafficThreshold, ddosRuleIDDefaultDDOSUnknownClientsChallenge, ddosRuleIDDefaultDDOSBlockNonEssentialBots, d.Get("site_id").(int), err)
return err
}
case botAccessControlRuleID:
Expand All @@ -342,6 +379,8 @@ func resourceWAFSecurityRuleDelete(d *schema.ResourceData, m interface{}) error
"",
"",
"",
"",
"",
botAccessControlBlockBadBotsDefaultAction,
botAccessControlChallengeSuspectedBotsDefaultAction,
)
Expand Down
4 changes: 4 additions & 0 deletions incapsula/resource_waf_security_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ resource "incapsula_waf_security_rule" "example-waf-ddos-rule" {
rule_id = "api.threats.ddos"
activation_mode = "api.threats.ddos.activation_mode.on"
ddos_traffic_threshold = "5000"
unknown_clients_challenge = "none"
block_non_essential_bots = "false"
}`, certificateName, siteResourceName,
)
}
Expand Down Expand Up @@ -271,6 +273,8 @@ resource "incapsula_waf_security_rule" "example-waf-ddos-rule" {
rule_id = "api.threats.ddos"
activation_mode = "bad_activation_mode"
ddos_traffic_threshold = "1234"
unknown_clients_challenge = "none"
block_non_essential_bots = "false"
}`, certificateName, siteResourceName,
)
}
Expand Down
4 changes: 4 additions & 0 deletions website/docs/r/waf_security_rule.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ resource "incapsula_waf_security_rule" "example-waf-ddos-rule" {
rule_id = "api.threats.ddos"
activation_mode = "api.threats.ddos.activation_mode.on" # (api.threats.ddos.activation_mode.auto | api.threats.ddos.activation_mode.off | api.threats.ddos.activation_mode.on)
ddos_traffic_threshold = "5000" # valid values are 10, 20, 50, 100, 200, 500, 750, 1000, 2000, 3000, 4000, 5000
unknown_clients_challenge = "none" # valid values are none, cookies, javascript, captcha (optional, default: cookies)
block_non_essential_bots = "false" # true | false (optional, default: false)
}
```

Expand All @@ -43,6 +45,8 @@ The following arguments are supported:
* `security_rule_action` - (Optional) The action that should be taken when a threat is detected, for example: api.threats.action.block_ip. See above examples for `rule_id` and `action` combinations.
* `activation_mode` - (Optional) The mode of activation for ddos on a site. Possible values: api.threats.ddos.activation_mode.off, api.threats.ddos.activation_mode.auto, api.threats.ddos.activation_mode.on.
* `ddos_traffic_threshold` - (Optional) Consider site to be under DDoS if the request rate is above this threshold. The valid values are 10, 20, 50, 100, 200, 500, 750, 1000, 2000, 3000, 4000, 5000.
* `unknown_clients_challenge` - (Optional) Defines a method used for challenging suspicious bots. This argument is valid for the rule_id api.threats.ddos argument value only. If this argument is not provided, then the value stays as it is in the system. Possible values: none, cookies, javascript, captcha
* `block_non_essential_bots` - (Optional) If non-essential bots (bots determined to be legitimate by Imperva's client classification mechanism, such as site helpers and search engines) should be blocked or not. This argument is valid for the rule_id api.threats.ddos argument value only. If this argument is not provided, then the value stays as it is in the system. Possible values: true, false
* `block_bad_bots` - (Optional) Whether or not to block bad bots. Possible values: true, false.
* `challenge_suspected_bots` - (Optional) Whether or not to send a challenge to clients that are suspected to be bad bots (CAPTCHA for example). Possible values: true, false.

Expand Down

0 comments on commit 7701e1c

Please sign in to comment.