Skip to content

Commit

Permalink
Merge pull request #1688 from WowVeryLogin/ddavydov/add-managed-heade…
Browse files Browse the repository at this point in the history
…rs-support

Add Managed Headers resource
  • Loading branch information
jacobbednarz authored Jun 22, 2022
2 parents 3bc686d + 96f1b35 commit 587fb93
Show file tree
Hide file tree
Showing 9 changed files with 471 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .changelog/1688.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_managed_headers
```
71 changes: 71 additions & 0 deletions docs/resources/managed_headers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
page_title: "cloudflare_managed_headers Resource - Cloudflare"
subcategory: ""
description: |-
The Cloudflare Managed Headers https://developers.cloudflare.com/rules/transform/managed-transforms/
allows you to add or remove some predefined headers to one's requests or origin responses.
---

# cloudflare_managed_headers (Resource)

The [Cloudflare Managed Headers](https://developers.cloudflare.com/rules/transform/managed-transforms/)
allows you to add or remove some predefined headers to one's requests or origin responses.

~> You can configure Managed Headers using the dashboard (https://api.cloudflare.com/#managed-headers-api-properties)
Terraform will override your configuration if it exists.

## Example Usage

```terraform
# Enable security headers using Managed Meaders
resource "cloudflare_managed_headers" "example" {
zone_id = "cb029e245cfdd66dc8d2e570d5dd3322"
managed_request_headers {
id = "add_true_client_ip_headers"
enabled = true
}
managed_response_headers {
id = "remove_x-powered-by_header"
enabled = true
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `zone_id` (String) The zone identifier to target for the resource.

### Optional

- `managed_request_headers` (Block Set) The list of managed request headers. (see [below for nested schema](#nestedblock--managed_request_headers))
- `managed_response_headers` (Block Set) The list of managed response headers. (see [below for nested schema](#nestedblock--managed_response_headers))

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedblock--managed_request_headers"></a>
### Nested Schema for `managed_request_headers`

Required:

- `enabled` (Boolean) Whether the headers rule is active.
- `id` (String) Unique headers rule identifier.


<a id="nestedblock--managed_response_headers"></a>
### Nested Schema for `managed_response_headers`

Required:

- `enabled` (Boolean) Whether the headers rule is active.
- `id` (String) Unique headers rule identifier.

## Import

Import is not supported for this resource.
12 changes: 6 additions & 6 deletions docs/resources/ruleset.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ resource "cloudflare_ruleset" "custom_fields_logging_example" {

- `kind` (String) Type of Ruleset to create. Available values: `custom`, `managed`, `root`, `schema`, `zone`.
- `name` (String) Name of the ruleset.
- `phase` (String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_main`, `http_request_sanitize`, `http_request_transform`, `http_request_origin`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
- `phase` (String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_cache_settings`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_late_transform_managed`, `http_request_main`, `http_request_origin`, `http_request_redirect`, `http_request_sanitize`, `http_request_transform`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.

### Optional

Expand All @@ -297,7 +297,7 @@ Required:

Optional:

- `action` (String) Action to perform in the ruleset rule. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
- `action` (String) Action to perform in the ruleset rule. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `action_parameters` (Block List, Max: 1) List of parameters that configure the behavior of the ruleset rule action. (see [below for nested schema](#nestedblock--rules--action_parameters))
- `description` (String) Brief summary of the ruleset rule and its intended use.
- `enabled` (Boolean) Whether the rule is active.
Expand All @@ -324,7 +324,7 @@ Optional:
- `matched_data` (Block List, Max: 1) List of properties to configure WAF payload logging. (see [below for nested schema](#nestedblock--rules--action_parameters--matched_data))
- `origin` (Block List, Max: 1) List of properties to change request origin. (see [below for nested schema](#nestedblock--rules--action_parameters--origin))
- `overrides` (Block List, Max: 1) List of override configurations to apply to the ruleset. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides))
- `phases` (Set of String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_main`, `http_request_sanitize`, `http_request_transform`, `http_request_origin`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
- `phases` (Set of String) Point in the request/response lifecycle where the ruleset will be created. Available values: `ddos_l4`, `ddos_l7`, `http_log_custom_fields`, `http_request_cache_settings`, `http_request_firewall_custom`, `http_request_firewall_managed`, `http_request_late_transform`, `http_request_late_transform_managed`, `http_request_main`, `http_request_origin`, `http_request_redirect`, `http_request_sanitize`, `http_request_transform`, `http_response_firewall_managed`, `http_response_headers_transform`, `magic_transit`, `http_ratelimit`, `http_request_sbfm`.
- `products` (Set of String) Products to target with the actions. Available values: `bic`, `hot`, `ratelimit`, `securityLevel`, `uablock`, `waf`, `zonelockdown`.
- `request_fields` (Set of String) List of request headers to include as part of custom fields logging, in lowercase.
- `response` (Block List) List of parameters that configure the response given to end users. (see [below for nested schema](#nestedblock--rules--action_parameters--response))
Expand Down Expand Up @@ -368,7 +368,7 @@ Optional:

Optional:

- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `categories` (Block List) List of tag-based overrides. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides--categories))
- `enabled` (Boolean, Deprecated) Defines if the current ruleset-level override enables or disables the ruleset.
- `rules` (Block List) List of rule-based overrides. (see [below for nested schema](#nestedblock--rules--action_parameters--overrides--rules))
Expand All @@ -379,7 +379,7 @@ Optional:

Optional:

- `action` (String) Action to perform in the tag-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
- `action` (String) Action to perform in the tag-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `category` (String) Tag name to apply the ruleset rule override to.
- `enabled` (Boolean, Deprecated) Defines if the current tag-level override enables or disables the ruleset rules with the specified tag.
- `status` (String) Defines if the current tag-level override enables or disables the ruleset rules with the specified tag. Available values: `enabled`, `disabled`. Defaults to `""`.
Expand All @@ -390,7 +390,7 @@ Optional:

Optional:

- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `managed_challenge`, `log`, `log_custom_field`, `rewrite`, `score`, `skip`, `route`.
- `action` (String) Action to perform in the rule-level override. Available values: `block`, `challenge`, `ddos_dynamic`, `execute`, `force_connection_close`, `js_challenge`, `log`, `log_custom_field`, `managed_challenge`, `redirect`, `rewrite`, `route`, `score`, `set_cache_settings`, `skip`.
- `enabled` (Boolean, Deprecated) Defines if the current rule-level override enables or disables the rule.
- `id` (String) Rule ID to apply the override to.
- `score_threshold` (Number) Anomaly score threshold to apply in the ruleset rule override. Only applicable to modsecurity-based rulesets.
Expand Down
14 changes: 14 additions & 0 deletions examples/resources/managed_headers/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Enable security headers using Managed Meaders
resource "cloudflare_managed_headers" "add_security_headers_example" {
zone_id = "cb029e245cfdd66dc8d2e570d5dd3322"

managed_request_headers {
id = "add_true_client_ip_headers"
enabled = true
}

managed_response_headers {
id = "remove_x-powered-by_header"
enabled = true
}
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_logpush_job": resourceCloudflareLogpushJob(),
"cloudflare_logpush_ownership_challenge": resourceCloudflareLogpushOwnershipChallenge(),
"cloudflare_magic_firewall_ruleset": resourceCloudflareMagicFirewallRuleset(),
"cloudflare_managed_headers": resourceCloudflareManagedHeaders(),
"cloudflare_notification_policy_webhooks": resourceCloudflareNotificationPolicyWebhooks(),
"cloudflare_notification_policy": resourceCloudflareNotificationPolicy(),
"cloudflare_origin_ca_certificate": resourceCloudflareOriginCACertificate(),
Expand Down
183 changes: 183 additions & 0 deletions internal/provider/resource_cloudflare_managed_headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package provider

import (
"context"
"errors"
"fmt"

cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCloudflareManagedHeaders() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareManagedHeadersSchema(),
CreateContext: resourceCloudflareManagedHeadersCreate,
ReadContext: resourceCloudflareManagedHeadersRead,
UpdateContext: resourceCloudflareManagedHeadersUpdate,
DeleteContext: resourceCloudflareManagedHeadersDelete,
SchemaVersion: 0,
Description: `
The [Cloudflare Managed Headers](https://developers.cloudflare.com/rules/transform/managed-transforms/)
allows you to add or remove some predefined headers to one's requests or origin responses.`,
}
}

func resourceCloudflareManagedHeadersCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
zoneID := d.Get("zone_id").(string)
d.SetId(zoneID)
return resourceCloudflareManagedHeadersUpdate(ctx, d, meta)
}

func resourceCloudflareManagedHeadersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)
headers, err := client.ListZoneManagedHeaders(ctx, cloudflare.ListManagedHeadersParams{
ZoneID: zoneID,
})
if err != nil {
return diag.FromErr(fmt.Errorf("error reading managed headers: %w", err))
}

// Filter out headers that are not enabled. This will eventually move into
// the API endpoint or the SDK.
var enabledRequestHeaders []cloudflare.ManagedHeader
var enabledResponseHeaders []cloudflare.ManagedHeader

for _, header := range headers.ManagedRequestHeaders {
if header.Enabled {
enabledRequestHeaders = append(enabledRequestHeaders, header)
}
}

for _, header := range headers.ManagedResponseHeaders {
if header.Enabled {
enabledResponseHeaders = append(enabledResponseHeaders, header)
}
}

if err := d.Set("managed_request_headers", buildResourceFromManagedHeaders(enabledRequestHeaders)); err != nil {
return diag.FromErr(err)
}
if err := d.Set("managed_response_headers", buildResourceFromManagedHeaders(enabledResponseHeaders)); err != nil {
return diag.FromErr(err)
}
return nil
}

func buildResourceFromManagedHeaders(headers []cloudflare.ManagedHeader) interface{} {
headersState := []map[string]interface{}{}
for _, header := range headers {
headersState = append(headersState, map[string]interface{}{
"id": header.ID,
"enabled": header.Enabled,
})
}

return headersState
}

func resourceCloudflareManagedHeadersUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

mh, err := buildManagedHeadersFromResource(d)
if err != nil {
return diag.FromErr(fmt.Errorf("error building managed headers from resource: %w", err))
}
if _, err := client.UpdateZoneManagedHeaders(ctx, cloudflare.UpdateManagedHeadersParams{
ManagedHeaders: mh,
ZoneID: zoneID,
}); err != nil {
return diag.FromErr(fmt.Errorf("error updating managed headers: %w", err))
}
return resourceCloudflareManagedHeadersRead(ctx, d, meta)
}

// receives the resource config and builds a managed headers struct.
func buildManagedHeadersFromResource(d *schema.ResourceData) (cloudflare.ManagedHeaders, error) {
requestHeaders, ok := d.Get("managed_request_headers").(*schema.Set)
if !ok {
return cloudflare.ManagedHeaders{}, errors.New("unable to create interface array type assertion")
}
reqHeaders, err := buildManagedHeadersListFromResource(requestHeaders)
if err != nil {
return cloudflare.ManagedHeaders{}, err
}

responseHeaders, ok := d.Get("managed_response_headers").(*schema.Set)
if !ok {
return cloudflare.ManagedHeaders{}, errors.New("unable to create interface array type assertion")
}
respHeaders, err := buildManagedHeadersListFromResource(responseHeaders)
if err != nil {
return cloudflare.ManagedHeaders{}, err
}

return cloudflare.ManagedHeaders{
ManagedRequestHeaders: reqHeaders,
ManagedResponseHeaders: respHeaders,
}, nil
}

func buildManagedHeadersListFromResource(resource *schema.Set) ([]cloudflare.ManagedHeader, error) {
headers := make([]cloudflare.ManagedHeader, 0, len(resource.List()))
for _, header := range resource.List() {
h, ok := header.(map[string]interface{})
if !ok {
return nil, errors.New("unable to create interface map type assertion for managed header")
}
id, ok := h["id"].(string)
if !ok {
return nil, errors.New("unable to create string type assertion for managed header ID")
}
enabled, ok := h["enabled"].(bool)
if !ok {
return nil, errors.New("unable to create bool type assertion for managed header enabled")
}

if enabled {
headers = append(headers, cloudflare.ManagedHeader{
ID: id,
Enabled: enabled,
})
}
}
return headers, nil
}

func resourceCloudflareManagedHeadersDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

headers, err := client.ListZoneManagedHeaders(ctx, cloudflare.ListManagedHeadersParams{
ZoneID: zoneID,
})
if err != nil {
return diag.FromErr(fmt.Errorf("error reading managed headers: %w", err))
}

requestHeaders := make([]cloudflare.ManagedHeader, 0, len(headers.ManagedRequestHeaders))
for _, header := range headers.ManagedRequestHeaders {
header.Enabled = false
requestHeaders = append(requestHeaders, header)
}
responseHeaders := make([]cloudflare.ManagedHeader, 0, len(headers.ManagedResponseHeaders))
for _, header := range headers.ManagedResponseHeaders {
header.Enabled = false
responseHeaders = append(responseHeaders, header)
}

if _, err := client.UpdateZoneManagedHeaders(ctx, cloudflare.UpdateManagedHeadersParams{
ManagedHeaders: cloudflare.ManagedHeaders{
ManagedRequestHeaders: requestHeaders,
ManagedResponseHeaders: responseHeaders,
},
ZoneID: zoneID,
}); err != nil {
return diag.FromErr(fmt.Errorf("error deleting managed headers with ID %q: %w", d.Id(), err))
}

return nil
}
Loading

0 comments on commit 587fb93

Please sign in to comment.