Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding unknownClientsChallenge and blockNonEssentialBots #459

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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