diff --git a/.changelog/3386.txt b/.changelog/3386.txt new file mode 100644 index 0000000000..0b84eb96da --- /dev/null +++ b/.changelog/3386.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +tencentcloud_waf_attack_white_rule +``` \ No newline at end of file diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 8cb59eddaa..b1e38343cb 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -2248,6 +2248,7 @@ func Provider() *schema.Provider { "tencentcloud_waf_bot_scene_status_config": waf.ResourceTencentCloudWafBotSceneStatusConfig(), "tencentcloud_waf_bot_status_config": waf.ResourceTencentCloudWafBotStatusConfig(), "tencentcloud_waf_bot_scene_ucb_rule": waf.ResourceTencentCloudWafBotSceneUCBRule(), + "tencentcloud_waf_attack_white_rule": waf.ResourceTencentCloudWafAttackWhiteRule(), "tencentcloud_wedata_rule_template": wedata.ResourceTencentCloudWedataRuleTemplate(), "tencentcloud_wedata_datasource": wedata.ResourceTencentCloudWedataDatasource(), "tencentcloud_wedata_function": wedata.ResourceTencentCloudWedataFunction(), diff --git a/tencentcloud/provider.md b/tencentcloud/provider.md index 375b35f6e8..195c24aa0c 100644 --- a/tencentcloud/provider.md +++ b/tencentcloud/provider.md @@ -2190,6 +2190,7 @@ tencentcloud_waf_instance_attack_log_post_config tencentcloud_waf_bot_scene_status_config tencentcloud_waf_bot_status_config tencentcloud_waf_bot_scene_ucb_rule +tencentcloud_waf_attack_white_rule Wedata Data Source diff --git a/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.go b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.go new file mode 100644 index 0000000000..c850be6cc2 --- /dev/null +++ b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.go @@ -0,0 +1,425 @@ +package waf + +import ( + "context" + "fmt" + "log" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + wafv20180125 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf/v20180125" + + tccommon "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/common" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func ResourceTencentCloudWafAttackWhiteRule() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudWafAttackWhiteRuleCreate, + Read: resourceTencentCloudWafAttackWhiteRuleRead, + Update: resourceTencentCloudWafAttackWhiteRuleUpdate, + Delete: resourceTencentCloudWafAttackWhiteRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Domain.", + }, + + "status": { + Type: schema.TypeInt, + Required: true, + Description: "Rule status.", + }, + + "rules": { + Type: schema.TypeList, + Required: true, + Description: "Rule list.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_field": { + Type: schema.TypeString, + Required: true, + Description: "Matching domains.", + }, + "match_method": { + Type: schema.TypeString, + Required: true, + Description: "Matching method.", + }, + "match_content": { + Type: schema.TypeString, + Required: true, + Description: "Matching content.", + }, + "match_params": { + Type: schema.TypeString, + Optional: true, + Description: "Matching params.", + }, + }, + }, + }, + + "signature_ids": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Whitelist of rule IDs.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "type_ids": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "The whitened category rule ID.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "mode": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "0: Whiten according to a specific rule ID, 1: Whiten according to the rule type.", + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Rule name.", + }, + + // computed + "rule_id": { + Type: schema.TypeInt, + Computed: true, + Description: "Rule ID.", + }, + }, + } +} + +func resourceTencentCloudWafAttackWhiteRuleCreate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_waf_attack_white_rule.create")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = wafv20180125.NewAddAttackWhiteRuleRequest() + response = wafv20180125.NewAddAttackWhiteRuleResponse() + domain string + ruleId string + ) + + if v, ok := d.GetOk("domain"); ok { + request.Domain = helper.String(v.(string)) + domain = v.(string) + } + + if v, ok := d.GetOkExists("status"); ok { + request.Status = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("rules"); ok { + for _, item := range v.([]interface{}) { + rulesMap := item.(map[string]interface{}) + userWhiteRuleItem := wafv20180125.UserWhiteRuleItem{} + if v, ok := rulesMap["match_field"].(string); ok && v != "" { + userWhiteRuleItem.MatchField = helper.String(v) + } + + if v, ok := rulesMap["match_method"].(string); ok && v != "" { + userWhiteRuleItem.MatchMethod = helper.String(v) + } + + if v, ok := rulesMap["match_content"].(string); ok && v != "" { + userWhiteRuleItem.MatchContent = helper.String(v) + } + + if v, ok := rulesMap["match_params"].(string); ok && v != "" { + userWhiteRuleItem.MatchParams = helper.String(v) + } + + request.Rules = append(request.Rules, &userWhiteRuleItem) + } + } + + if v, ok := d.GetOk("signature_ids"); ok { + signatureIdsSet := v.(*schema.Set).List() + for i := range signatureIdsSet { + signatureIds := signatureIdsSet[i].(string) + request.SignatureIds = append(request.SignatureIds, helper.String(signatureIds)) + } + } + + if v, ok := d.GetOk("type_ids"); ok { + typeIdsSet := v.(*schema.Set).List() + for i := range typeIdsSet { + typeIds := typeIdsSet[i].(string) + request.TypeIds = append(request.TypeIds, helper.String(typeIds)) + } + } + + if v, ok := d.GetOkExists("mode"); ok { + request.Mode = helper.IntInt64(v.(int)) + } + + if v, ok := d.GetOk("name"); ok { + request.Name = helper.String(v.(string)) + } + + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseWafV20180125Client().AddAttackWhiteRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + if result == nil || result.Response == nil { + return resource.NonRetryableError(fmt.Errorf("Create waf attack white rule failed, Response is nil.")) + } + + response = result + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s create waf attack white rule failed, reason:%+v", logId, reqErr) + return reqErr + } + + if response.Response.RuleId == nil { + return fmt.Errorf("RuleId is nil.") + } + + ruleId = strconv.FormatUint(*response.Response.RuleId, 10) + d.SetId(strings.Join([]string{domain, ruleId}, tccommon.FILED_SP)) + return resourceTencentCloudWafAttackWhiteRuleRead(d, meta) +} + +func resourceTencentCloudWafAttackWhiteRuleRead(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_waf_attack_white_rule.read")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + service = WafService{client: meta.(tccommon.ProviderMeta).GetAPIV3Conn()} + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 2 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + domain := idSplit[0] + ruleId := idSplit[1] + + ruleIdInt := helper.StrToUInt64(ruleId) + respData, err := service.DescribeWafAttackWhiteRuleById(ctx, domain, ruleIdInt) + if err != nil { + return err + } + + if respData == nil { + d.SetId("") + log.Printf("[WARN]%s resource `waf_attack_white_rule` [%s] not found, please check if it has been deleted.\n", logId, d.Id()) + return nil + } + + _ = d.Set("domain", domain) + _ = d.Set("rule_id", ruleIdInt) + + if respData.Status != nil { + _ = d.Set("status", respData.Status) + } + + if respData.MatchInfo != nil { + tmpList := make([]map[string]interface{}, 0, len(respData.MatchInfo)) + for _, item := range respData.MatchInfo { + dMap := make(map[string]interface{}, 0) + if item.MatchField != nil { + dMap["match_field"] = item.MatchField + } + + if item.MatchMethod != nil { + dMap["match_method"] = item.MatchMethod + } + + if item.MatchContent != nil { + dMap["match_content"] = item.MatchContent + } + + if item.MatchParams != nil { + dMap["match_params"] = item.MatchParams + } + + tmpList = append(tmpList, dMap) + } + + _ = d.Set("rules", tmpList) + } + + if respData.SignatureIds != nil { + _ = d.Set("signature_ids", respData.SignatureIds) + } + + if respData.TypeIds != nil { + _ = d.Set("type_ids", respData.TypeIds) + } + + if respData.Mode != nil { + _ = d.Set("mode", respData.Mode) + } + + if respData.Name != nil { + _ = d.Set("name", respData.Name) + } + + return nil +} + +func resourceTencentCloudWafAttackWhiteRuleUpdate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_waf_attack_white_rule.update")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = wafv20180125.NewModifyAttackWhiteRuleRequest() + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 2 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + domain := idSplit[0] + ruleId := idSplit[1] + + if v, ok := d.GetOkExists("status"); ok { + request.Status = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("rules"); ok { + for _, item := range v.([]interface{}) { + rulesMap := item.(map[string]interface{}) + userWhiteRuleItem := wafv20180125.UserWhiteRuleItem{} + if v, ok := rulesMap["match_field"].(string); ok && v != "" { + userWhiteRuleItem.MatchField = helper.String(v) + } + + if v, ok := rulesMap["match_method"].(string); ok && v != "" { + userWhiteRuleItem.MatchMethod = helper.String(v) + } + + if v, ok := rulesMap["match_content"].(string); ok && v != "" { + userWhiteRuleItem.MatchContent = helper.String(v) + } + + if v, ok := rulesMap["match_params"].(string); ok && v != "" { + userWhiteRuleItem.MatchParams = helper.String(v) + } + + request.Rules = append(request.Rules, &userWhiteRuleItem) + } + } + + if v, ok := d.GetOk("signature_ids"); ok { + signatureIdsSet := v.(*schema.Set).List() + for i := range signatureIdsSet { + signatureIds := signatureIdsSet[i].(string) + request.SignatureIds = append(request.SignatureIds, helper.String(signatureIds)) + } + } + + if v, ok := d.GetOk("type_ids"); ok { + typeIdsSet := v.(*schema.Set).List() + for i := range typeIdsSet { + typeIds := typeIdsSet[i].(string) + request.TypeIds = append(request.TypeIds, helper.String(typeIds)) + } + } + + if v, ok := d.GetOkExists("mode"); ok { + request.Mode = helper.IntInt64(v.(int)) + } + + if v, ok := d.GetOk("name"); ok { + request.Name = helper.String(v.(string)) + } + + request.Domain = &domain + request.RuleId = helper.StrToUint64Point(ruleId) + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseWafV20180125Client().ModifyAttackWhiteRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s update waf attack white rule failed, reason:%+v", logId, reqErr) + return reqErr + } + + return resourceTencentCloudWafAttackWhiteRuleRead(d, meta) +} + +func resourceTencentCloudWafAttackWhiteRuleDelete(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_waf_attack_white_rule.delete")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = wafv20180125.NewDeleteAttackWhiteRuleRequest() + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 2 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + domain := idSplit[0] + ruleId := idSplit[1] + + request.Domain = &domain + request.Ids = []*uint64{helper.StrToUint64Point(ruleId)} + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseWafV20180125Client().DeleteAttackWhiteRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s delete waf attack white rule failed, reason:%+v", logId, reqErr) + return reqErr + } + + return nil +} diff --git a/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.md b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.md new file mode 100644 index 0000000000..2896efe519 --- /dev/null +++ b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule.md @@ -0,0 +1,84 @@ +Provides a resource to create a WAF attack white rule + +Example Usage + +Using type_ids + +```hcl +resource "tencentcloud_waf_attack_white_rule" "example" { + domain = "www.demo.com" + name = "tf-example" + status = 1 + mode = 0 + rules { + match_field = "IP" + match_method = "ipmatch" + match_content = "1.1.1.1" + } + + rules { + match_field = "Referer" + match_method = "eq" + match_content = "referer content" + } + + rules { + match_field = "URL" + match_method = "contains" + match_content = "/prefix" + } + + rules { + match_field = "HTTP_METHOD" + match_method = "neq" + match_content = "POST" + } + + rules { + match_field = "GET" + match_method = "ncontains" + match_content = "value" + match_params = "key" + } + + type_ids = [ + "010000000", + "020000000", + "030000000", + "040000000", + "050000000", + "060000000", + "090000000", + "110000000" + ] +} +``` + +Using signature_ids + +```hcl +resource "tencentcloud_waf_attack_white_rule" "example" { + domain = "www.demo.com" + name = "tf-example" + status = 0 + mode = 1 + rules { + match_field = "IP" + match_method = "ipmatch" + match_content = "1.1.1.1" + } + + signature_ids = [ + "60270036", + "10000047" + ] +} +``` + +Import + +WAF attack white rule can be imported using the id, e.g. + +``` +terraform import tencentcloud_waf_attack_white_rule.example www.demo.com#38562 +``` diff --git a/tencentcloud/services/waf/resource_tc_waf_attack_white_rule_test.go b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule_test.go new file mode 100644 index 0000000000..6b27d16fdf --- /dev/null +++ b/tencentcloud/services/waf/resource_tc_waf_attack_white_rule_test.go @@ -0,0 +1,35 @@ +package waf_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + tcacctest "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/acctest" +) + +func TestAccTencentCloudWafAttackWhiteRuleResource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { + tcacctest.AccPreCheck(t) + }, + Providers: tcacctest.AccProviders, + Steps: []resource.TestStep{{ + Config: testAccWafAttackWhiteRule, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttrSet("tencentcloud_waf_attack_white_rule.waf_attack_white_rule", "id")), + }, { + ResourceName: "tencentcloud_waf_attack_white_rule.waf_attack_white_rule", + ImportState: true, + ImportStateVerify: true, + }}, + }) +} + +const testAccWafAttackWhiteRule = ` + +resource "tencentcloud_waf_attack_white_rule" "waf_attack_white_rule" { + rules = { + } +} +` diff --git a/tencentcloud/services/waf/service_tencentcloud_waf.go b/tencentcloud/services/waf/service_tencentcloud_waf.go index 01dc1327b2..68e0679a56 100644 --- a/tencentcloud/services/waf/service_tencentcloud_waf.go +++ b/tencentcloud/services/waf/service_tencentcloud_waf.go @@ -1943,3 +1943,66 @@ func (me *WafService) DeleteWafBotSceneUCBRuleById(ctx context.Context, domain, return } + +func (me *WafService) DescribeWafAttackWhiteRuleById(ctx context.Context, domain string, ruleId uint64) (ret *waf.UserWhiteRule, errRet error) { + logId := tccommon.GetLogId(ctx) + + request := waf.NewDescribeAttackWhiteRuleRequest() + response := waf.NewDescribeAttackWhiteRuleResponse() + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + request.Domain = &domain + + var ( + offset uint64 = 0 + limit uint64 = 20 + wrList []*waf.UserWhiteRule + ) + + for { + request.Offset = &offset + request.Limit = &limit + err := resource.Retry(tccommon.ReadRetryTimeout, func() *resource.RetryError { + ratelimit.Check(request.GetAction()) + result, e := me.client.UseWafV20180125Client().DescribeAttackWhiteRule(request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + response = result + return nil + }) + + if err != nil { + errRet = err + return + } + + if response == nil || len(response.Response.List) < 1 { + break + } + + wrList = append(wrList, response.Response.List...) + if len(response.Response.List) < int(limit) { + break + } + + offset += limit + } + + for _, item := range wrList { + if item.WhiteRuleId != nil && *item.WhiteRuleId == ruleId { + ret = item + break + } + } + + return +} diff --git a/website/docs/r/waf_attack_white_rule.html.markdown b/website/docs/r/waf_attack_white_rule.html.markdown new file mode 100644 index 0000000000..5ec174b8e8 --- /dev/null +++ b/website/docs/r/waf_attack_white_rule.html.markdown @@ -0,0 +1,123 @@ +--- +subcategory: "Web Application Firewall(WAF)" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_waf_attack_white_rule" +sidebar_current: "docs-tencentcloud-resource-waf_attack_white_rule" +description: |- + Provides a resource to create a WAF attack white rule +--- + +# tencentcloud_waf_attack_white_rule + +Provides a resource to create a WAF attack white rule + +## Example Usage + +### Using type_ids + +```hcl +resource "tencentcloud_waf_attack_white_rule" "example" { + domain = "www.demo.com" + name = "tf-example" + status = 1 + mode = 0 + rules { + match_field = "IP" + match_method = "ipmatch" + match_content = "1.1.1.1" + } + + rules { + match_field = "Referer" + match_method = "eq" + match_content = "referer content" + } + + rules { + match_field = "URL" + match_method = "contains" + match_content = "/prefix" + } + + rules { + match_field = "HTTP_METHOD" + match_method = "neq" + match_content = "POST" + } + + rules { + match_field = "GET" + match_method = "ncontains" + match_content = "value" + match_params = "key" + } + + type_ids = [ + "010000000", + "020000000", + "030000000", + "040000000", + "050000000", + "060000000", + "090000000", + "110000000" + ] +} +``` + +### Using signature_ids + +```hcl +resource "tencentcloud_waf_attack_white_rule" "example" { + domain = "www.demo.com" + name = "tf-example" + status = 0 + mode = 1 + rules { + match_field = "IP" + match_method = "ipmatch" + match_content = "1.1.1.1" + } + + signature_ids = [ + "60270036", + "10000047" + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `domain` - (Required, String, ForceNew) Domain. +* `rules` - (Required, List) Rule list. +* `status` - (Required, Int) Rule status. +* `mode` - (Optional, Int) 0: Whiten according to a specific rule ID, 1: Whiten according to the rule type. +* `name` - (Optional, String) Rule name. +* `signature_ids` - (Optional, Set: [`String`]) Whitelist of rule IDs. +* `type_ids` - (Optional, Set: [`String`]) The whitened category rule ID. + +The `rules` object supports the following: + +* `match_content` - (Required, String) Matching content. +* `match_field` - (Required, String) Matching domains. +* `match_method` - (Required, String) Matching method. +* `match_params` - (Optional, String) Matching params. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `rule_id` - Rule ID. + + +## Import + +WAF attack white rule can be imported using the id, e.g. + +``` +terraform import tencentcloud_waf_attack_white_rule.example www.demo.com#38562 +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index dc29863c70..ce1e271111 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -6841,6 +6841,9 @@
  • tencentcloud_waf_anti_info_leak
  • +
  • + tencentcloud_waf_attack_white_rule +
  • tencentcloud_waf_auto_deny_rules