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

Added fields to google_compute_security_policy to support Cloud Armor bot management #13159

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/6857.txt
@@ -0,0 +1,3 @@
```release-note:enhancement
compute: Added fields to resource `google_compute_security_policy` to support Cloud Armor bot management
```
153 changes: 152 additions & 1 deletion google/resource_compute_security_policy.go
Expand Up @@ -292,6 +292,35 @@ func resourceComputeSecurityPolicy() *schema.Resource {
},
Description: `Parameters defining the redirect action. Cannot be specified for any other actions.`,
},
"header_action": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: `Additional actions that are performed on headers.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"request_headers_to_adds": {
Type: schema.TypeList,
Required: true,
Description: `The list of request headers to add or overwrite if they're already present.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"header_name": {
Type: schema.TypeString,
Required: true,
Description: `The name of the header to set.`,
},
"header_value": {
Type: schema.TypeString,
Optional: true,
Description: `The value to set the named header to.`,
},
},
},
},
},
},
},
},
},
Description: `The set of rules that belong to this policy. There must always be a default rule (rule with priority 2147483647 and match "*"). If no rules are provided when creating a security policy, a default rule with action "allow" will be added.`,
Expand Down Expand Up @@ -384,6 +413,21 @@ func resourceComputeSecurityPolicy() *schema.Resource {
},
},
},
"recaptcha_options_config": {
Type: schema.TypeList,
Optional: true,
Description: `reCAPTCHA configuration options to be applied for the security policy.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"redirect_site_key": {
Type: schema.TypeString,
Required: true,
Description: `A field to supply a reCAPTCHA site key to be used for all the rules using the redirect action with the type of GOOGLE_RECAPTCHA under the security policy. The specified site key needs to be created from the reCAPTCHA API. The user is responsible for the validity of the specified site key. If not specified, a Google-managed site key is used.`,
},
},
},
},
},

UseJSONNumber: true,
Expand Down Expand Up @@ -442,6 +486,10 @@ func resourceComputeSecurityPolicyCreate(d *schema.ResourceData, meta interface{

log.Printf("[DEBUG] SecurityPolicy insert request: %#v", securityPolicy)

if v, ok := d.GetOk("recaptcha_options_config"); ok {
securityPolicy.RecaptchaOptionsConfig = expandSecurityPolicyRecaptchaOptionsConfig(v.([]interface{}), d)
}

client := config.NewComputeClient(userAgent)

op, err := client.SecurityPolicies.Insert(project, securityPolicy).Do()
Expand Down Expand Up @@ -514,6 +562,10 @@ func resourceComputeSecurityPolicyRead(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error setting adaptive_protection_config: %s", err)
}

if err := d.Set("recaptcha_options_config", flattenSecurityPolicyRecaptchaOptionConfig(securityPolicy.RecaptchaOptionsConfig)); err != nil {
return fmt.Errorf("Error setting recaptcha_options_config: %s", err)
}

return nil
}

Expand Down Expand Up @@ -555,6 +607,11 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{
securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "AdaptiveProtectionConfig", "adaptiveProtectionConfig.layer7DdosDefenseConfig.enable", "adaptiveProtectionConfig.layer7DdosDefenseConfig.ruleVisibility")
}

if d.HasChange("recaptcha_options_config") {
securityPolicy.RecaptchaOptionsConfig = expandSecurityPolicyRecaptchaOptionsConfig(d.Get("recaptcha_options_config").([]interface{}), d)
securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "RecaptchaOptionsConfig")
}

if len(securityPolicy.ForceSendFields) > 0 {
client := config.NewComputeClient(userAgent)

Expand Down Expand Up @@ -685,6 +742,7 @@ func expandSecurityPolicyRule(raw interface{}) *compute.SecurityPolicyRule {
Match: expandSecurityPolicyMatch(data["match"].([]interface{})),
RateLimitOptions: expandSecurityPolicyRuleRateLimitOptions(data["rate_limit_options"].([]interface{})),
RedirectOptions: expandSecurityPolicyRuleRedirectOptions(data["redirect_options"].([]interface{})),
HeaderAction: expandSecurityPolicyRuleHeaderAction(data["header_action"].([]interface{})),
ForceSendFields: []string{"Description", "Preview"},
}
}
Expand Down Expand Up @@ -739,8 +797,8 @@ func flattenSecurityPolicyRules(rules []*compute.SecurityPolicyRule) []map[strin
"match": flattenMatch(rule.Match),
"rate_limit_options": flattenSecurityPolicyRuleRateLimitOptions(rule.RateLimitOptions),
"redirect_options": flattenSecurityPolicyRedirectOptions(rule.RedirectOptions),
"header_action": flattenSecurityPolicyRuleHeaderAction(rule.HeaderAction),
}

rulesSchema = append(rulesSchema, data)
}
return rulesSchema
Expand Down Expand Up @@ -975,6 +1033,99 @@ func flattenSecurityPolicyRedirectOptions(conf *compute.SecurityPolicyRuleRedire
return []map[string]interface{}{data}
}

func expandSecurityPolicyRecaptchaOptionsConfig(configured []interface{}, d *schema.ResourceData) *compute.SecurityPolicyRecaptchaOptionsConfig {
if len(configured) == 0 || configured[0] == nil {
return nil
}

data := configured[0].(map[string]interface{})

return &compute.SecurityPolicyRecaptchaOptionsConfig{
RedirectSiteKey: data["redirect_site_key"].(string),
ForceSendFields: []string{"RedirectSiteKey"},
}
}

func flattenSecurityPolicyRecaptchaOptionConfig(conf *compute.SecurityPolicyRecaptchaOptionsConfig) []map[string]interface{} {
if conf == nil {
return nil
}

data := map[string]interface{}{
"redirect_site_key": conf.RedirectSiteKey,
}

return []map[string]interface{}{data}
}

func expandSecurityPolicyRuleHeaderAction(configured []interface{}) *compute.SecurityPolicyRuleHttpHeaderAction {
if len(configured) == 0 || configured[0] == nil {
// If header action is unset, return an empty object; this ensures the header action can be cleared
return &compute.SecurityPolicyRuleHttpHeaderAction{}
}

data := configured[0].(map[string]interface{})

return &compute.SecurityPolicyRuleHttpHeaderAction{
RequestHeadersToAdds: expandSecurityPolicyRequestHeadersToAdds(data["request_headers_to_adds"].([]interface{})),
}
}

func expandSecurityPolicyRequestHeadersToAdds(configured []interface{}) []*compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption {
transformed := make([]*compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption, 0, len(configured))

for _, raw := range configured {
transformed = append(transformed, expandSecurityPolicyRequestHeader(raw))
}

return transformed
}

func expandSecurityPolicyRequestHeader(configured interface{}) *compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption {
data := configured.(map[string]interface{})

return &compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption{
HeaderName: data["header_name"].(string),
HeaderValue: data["header_value"].(string),
}
}

func flattenSecurityPolicyRuleHeaderAction(conf *compute.SecurityPolicyRuleHttpHeaderAction) []map[string]interface{} {
if conf == nil || conf.RequestHeadersToAdds == nil {
return nil
}

transformed := map[string]interface{}{
"request_headers_to_adds": flattenSecurityPolicyRequestHeadersToAdds(conf.RequestHeadersToAdds),
}

return []map[string]interface{}{transformed}
}

func flattenSecurityPolicyRequestHeadersToAdds(conf []*compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption) []map[string]interface{} {
if conf == nil || len(conf) == 0 {
return nil
}

transformed := make([]map[string]interface{}, 0, len(conf))
for _, raw := range conf {
transformed = append(transformed, flattenSecurityPolicyRequestHeader(raw))
}

return transformed
}

func flattenSecurityPolicyRequestHeader(conf *compute.SecurityPolicyRuleHttpHeaderActionHttpHeaderOption) map[string]interface{} {
if conf == nil {
return nil
}

return map[string]interface{}{
"header_name": conf.HeaderName,
"header_value": conf.HeaderValue,
}
}

func resourceSecurityPolicyStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*Config)
if err := parseImportId([]string{"projects/(?P<project>[^/]+)/global/securityPolicies/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, d, config); err != nil {
Expand Down