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

add support for Cloud Armor Edge Policies #5794

Merged
merged 6 commits into from
Mar 24, 2022
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
5 changes: 4 additions & 1 deletion mmv1/products/compute/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,10 @@ objects:
send_empty_value: true
description: |
Serve existing content from the cache (if available) when revalidating content with the origin, or when an error is encountered when refreshing the cache.

- !ruby/object:Api::Type::String
name: 'edgeSecurityPolicy'
description: |
The security policy associated with this backend bucket.
- !ruby/object:Api::Type::Array
name: 'customResponseHeaders'
description: |
Expand Down
11 changes: 11 additions & 0 deletions mmv1/products/compute/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,15 @@ overrides: !ruby/object:Overrides::ResourceOverrides
vars:
backend_bucket_name: "image-backend-bucket-full"
bucket_name: "image-store-bucket-full"
- !ruby/object:Provider::Terraform::Examples
name: "backend_bucket_security_policy"
primary_resource_id: "image_backend"
vars:
backend_bucket_name: "image-backend-bucket"
bucket_name: "image-store-bucket"
custom_code: !ruby/object:Provider::Terraform::CustomCode
post_create: 'templates/terraform/post_create/compute_backend_bucket_security_policy.go.erb'
post_update: 'templates/terraform/post_create/compute_backend_bucket_security_policy.go.erb'
properties:
id: !ruby/object:Overrides::Terraform::PropertyOverride
exclude: true
Expand All @@ -186,6 +195,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides
send_empty_value: true
cdnPolicy.negativeCachingPolicy.ttl: !ruby/object:Overrides::Terraform::PropertyOverride
send_empty_value: true
edgeSecurityPolicy: !ruby/object:Overrides::Terraform::PropertyOverride
diff_suppress_func: 'compareSelfLinkOrResourceName'
BackendBucketSignedUrlKey: !ruby/object:Overrides::Terraform::ResourceOverride
exclude_import: true
exclude_validator: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "google_compute_backend_bucket" "<%= ctx[:primary_resource_id] %>" {
name = "<%= ctx[:vars]['backend_bucket_name'] %>"
description = "Contains beautiful images"
bucket_name = google_storage_bucket.<%= ctx[:primary_resource_id] %>.name
enable_cdn = true
edge_security_policy = google_compute_security_policy.policy.id
}

resource "google_storage_bucket" "<%= ctx[:primary_resource_id] %>" {
name = "<%= ctx[:vars]['bucket_name'] %>"
location = "EU"
}

resource "google_compute_security_policy" "policy" {
name = "<%= ctx[:vars]['bucket_name'] %>"
description = "basic security policy"
type = "CLOUD_ARMOR_EDGE"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// security_policy isn't set by Create / Update
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exists in the post_create, but what if the user goes from this field being unset on a bucket to setting it? Should the field be ForceNew or do we also need this in the Update call?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this is used as post_update as well, makes sense

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the update part of this be done by setting update_url on this specific field in the terraform.yaml? It allows calling a specific endpoint for a specific field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would complicate things further by having two separate pathways to do the same thing. This way unifies the codepaths. I would be against such a change.

if o, n := d.GetChange("edge_security_policy"); o.(string) != n.(string) {
pol, err := ParseSecurityPolicyFieldValue(n.(string), d, config)
if err != nil {
return errwrap.Wrapf("Error parsing Backend Service security policy: {{err}}", err)
}

spr := emptySecurityPolicyReference()
spr.SecurityPolicy = pol.RelativeLink()
op, err := config.NewComputeClient(userAgent).BackendBuckets.SetEdgeSecurityPolicy(project, obj["name"].(string), spr).Do()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this through the HTTP client directly? It feels a little awkward to add a dependency on the client libraries to a mmv1-based resource

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if err != nil {
return errwrap.Wrapf("Error setting Backend Service security policy: {{err}}", err)
}
// This uses the create timeout for simplicity, though technically this code appears in both create and update
waitErr := computeOperationWaitTime(config, op, project, "Setting Backend Service Security Policy", userAgent, d.Timeout(schema.TimeoutCreate))
if waitErr != nil {
return waitErr
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ func resourceComputeSecurityPolicy() *schema.Resource {
Description: `The project in which the resource belongs. If it is not provided, the provider project is used.`,
},

"type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: `The type indicates the intended use of the security policy. CLOUD_ARMOR - Cloud Armor backend security policies can be configured to filter incoming HTTP requests targeting backend services. They filter requests before they hit the origin servers. CLOUD_ARMOR_EDGE - Cloud Armor edge security policies can be configured to filter incoming HTTP requests targeting backend services (including Cloud CDN-enabled) as well as backend buckets (Cloud Storage). They filter requests before the request is served from Google's cache.`,
},

"rule": {
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -153,7 +160,7 @@ func resourceComputeSecurityPolicy() *schema.Resource {
Computed: true,
Description: `When set to true, the action specified above is not enforced. Stackdriver logs for requests that trigger a preview action are annotated as such.`,
},

<% unless version == 'ga' -%>
"rate_limit_options": {
Type: schema.TypeList,
Expand Down Expand Up @@ -355,6 +362,11 @@ func resourceComputeSecurityPolicyCreate(d *schema.ResourceData, meta interface{
Name: sp,
Description: d.Get("description").(string),
}

if v, ok := d.GetOk("type"); ok {
securityPolicy.Type = v.(string)
}

if v, ok := d.GetOk("rule"); ok {
securityPolicy.Rules = expandSecurityPolicyRules(v.(*schema.Set).List())
}
Expand Down Expand Up @@ -424,6 +436,9 @@ func resourceComputeSecurityPolicyRead(d *schema.ResourceData, meta interface{})
if err := d.Set("description", securityPolicy.Description); err != nil {
return fmt.Errorf("Error setting description: %s", err)
}
if err := d.Set("type", securityPolicy.Type); err != nil {
return fmt.Errorf("Error setting type: %s", err)
}
if err := d.Set("rule", flattenSecurityPolicyRules(securityPolicy.Rules)); err != nil {
return err
}
Expand Down Expand Up @@ -459,19 +474,24 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{

sp := d.Get("name").(string)


securityPolicy := &compute.SecurityPolicy{
Fingerprint: d.Get("fingerprint").(string),
}

if d.HasChange("type") {
securityPolicy.Type = d.Get("type").(string)
securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "Type")
}

if d.HasChange("description") {
securityPolicy := &compute.SecurityPolicy{
Description: d.Get("description").(string),
Fingerprint: d.Get("fingerprint").(string),
ForceSendFields: []string{"Description"},
}
securityPolicy.Description = d.Get("description").(string)
securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "Description")
}

<% if version == 'ga' -%>
client := config.NewComputeClient(userAgent)
<% else -%>
if len(securityPolicy.ForceSendFields) > 0 {
client := config.NewComputeClient(userAgent)
<% end -%>


op, err := client.SecurityPolicies.Patch(project, sp, securityPolicy).Do()

if err != nil {
Expand All @@ -484,6 +504,7 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{
}
}


if d.HasChange("rule") {
o, n := d.GetChange("rule")
oSet := o.(*schema.Set)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,37 @@ func TestAccComputeBackendBucket_withCdnPolicy(t *testing.T) {
})
}

func TestAccComputeBackendBucket_withSecurityPolicy(t *testing.T) {
t.Parallel()

bucketName := fmt.Sprintf("tf-test-%s", randString(t, 10))
polName := fmt.Sprintf("tf-test-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeBackendServiceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeBackendBucket_withSecurityPolicy(bucketName, polName, "google_compute_security_policy.policy.self_link"),
},
{
ResourceName: "google_compute_backend_bucket.image_backend",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccComputeBackendBucket_withSecurityPolicy(bucketName, polName, "\"\""),
},
{
ResourceName: "google_compute_backend_bucket.image_backend",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccComputeBackendBucket_basic(backendName, storageName string) string {
return fmt.Sprintf(`
resource "google_compute_backend_bucket" "foobar" {
Expand Down Expand Up @@ -201,3 +232,27 @@ resource "google_storage_bucket" "bucket" {
}
`, backendName, age, max_ttl, ttl, ttl, ttl, code, ttl, storageName)
}

func testAccComputeBackendBucket_withSecurityPolicy(bucketName, polName, polLink string) string {
return fmt.Sprintf(`
resource "google_compute_backend_bucket" "image_backend" {
name = "%s"
description = "Contains beautiful images"
bucket_name = google_storage_bucket.image_bucket.name
enable_cdn = true
edge_security_policy = %s
}

resource "google_storage_bucket" "image_bucket" {
name = "%s"
location = "EU"
}


resource "google_compute_security_policy" "policy" {
name = "%s"
description = "basic security policy"
type = "CLOUD_ARMOR_EDGE"
}
`, bucketName, polLink, bucketName, polName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func testAccComputeSecurityPolicy_basic(spName string) string {
resource "google_compute_security_policy" "policy" {
name = "%s"
description = "basic security policy"
type = "CLOUD_ARMOR_EDGE"
}
`, spName)
}
Expand Down Expand Up @@ -402,7 +403,7 @@ resource "google_compute_security_policy" "policy" {

rule {
action = "throttle"
priority = 100
priority = 100
match {
versioned_expr = "SRC_IPS_V1"
config {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ The following arguments are supported:

* `adaptive_protection_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) Configuration for [Google Cloud Armor Adaptive Protection](https://cloud.google.com/armor/docs/adaptive-protection-overview?hl=en). Structure is [documented below](#nested_adaptive_protection_config).

* `type` - The type indicates the intended use of the security policy.
* CLOUD_ARMOR - Cloud Armor backend security policies can be configured to filter incoming HTTP requests targeting backend services.
They filter requests before they hit the origin servers.
* CLOUD_ARMOR_EDGE - Cloud Armor edge security policies can be configured to filter incoming HTTP requests targeting backend services
(including Cloud CDN-enabled) as well as backend buckets (Cloud Storage).
They filter requests before the request is served from Google's cache.

<a name="nested_rule"></a>The `rule` block supports:

* `action` - (Required) Action to take when `match` matches the request. Valid values:
Expand Down
13 changes: 13 additions & 0 deletions mmv1/third_party/validator/compute_security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ func GetComputeSecurityPolicyApiObject(d TerraformResourceData, config *Config)
} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
obj["name"] = nameProp
}
typeProp, err := expandComputeSecurityPolicyName(d.Get("type"), d, config)
if err != nil {
return nil, err
} else if v, ok := d.GetOkExists("type"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, typeProp)) {
obj["type"] = typeProp
}
rulesProp, err := expandComputeSecurityPolicyRules(d.Get("rule"), d, config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -76,6 +82,13 @@ func expandComputeSecurityPolicyRules(v interface{}, d TerraformResourceData, co
transformed["description"] = transformedDescription
}

transformedType, err := expandComputeSecurityPolicyRulesDescription(original["type"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) {
transformed["type"] = transformedType
}

transformedPriority, err := expandComputeSecurityPolicyRulesPriority(original["priority"], d, config)
if err != nil {
return nil, err
Expand Down