From 863385c1db5dc39a7101aef359c8035797cafeff Mon Sep 17 00:00:00 2001 From: niroz89 Date: Wed, 20 Mar 2024 19:57:07 +0800 Subject: [PATCH 1/6] Change defaults and add test --- examples/response-headers/main.tf | 18 ++++---- modules/response-headers/outputs.tf | 12 +++++ .../examples_response_headers.tftest.hcl | 46 +++++++++++++++++++ modules/response-headers/variables.tf | 20 ++++---- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/examples/response-headers/main.tf b/examples/response-headers/main.tf index 0f50cba..a4a32cb 100644 --- a/examples/response-headers/main.tf +++ b/examples/response-headers/main.tf @@ -2,14 +2,6 @@ module "custom_resp_headers" { source = "../../modules/response-headers" name = "custom-response-headers" - strict_transport_security_header = { - enabled = true - override = true - - max_age = 31536000 - include_subdomains = true - preload = true - } custom_headers = [ { @@ -23,3 +15,13 @@ module "custom_resp_headers" { sampling_rate = 100 } } + +module "override_default" { + source = "../../modules/response-headers" + + name = "custom-response-headers" + + xss_protection_header = { + enabled = false + } +} diff --git a/modules/response-headers/outputs.tf b/modules/response-headers/outputs.tf index 3ce584e..7edbaf0 100644 --- a/modules/response-headers/outputs.tf +++ b/modules/response-headers/outputs.tf @@ -7,3 +7,15 @@ output "etag" { description = "The current version of the response headers policy." value = aws_cloudfront_response_headers_policy.this.etag } + +output "security_headers" { + description = "A configuration for several security-related HTTP response headers." + value = { + content_security_policy = var.content_security_policy_header + content_type_options = var.content_type_options_header + frame_options = var.frame_options_header + referrer_policy = var.referrer_policy_header + strict_transport_security = var.strict_transport_security_header + xss_protection = var.xss_protection_header + } +} diff --git a/modules/response-headers/tests/examples_response_headers.tftest.hcl b/modules/response-headers/tests/examples_response_headers.tftest.hcl index 6c8935d..01154d6 100644 --- a/modules/response-headers/tests/examples_response_headers.tftest.hcl +++ b/modules/response-headers/tests/examples_response_headers.tftest.hcl @@ -8,3 +8,49 @@ run "validate" { source = "../../examples/response-headers" } } + +run "validate_security_defaults" { + command = apply + + module { + source = "../../examples/response-headers" + } + + assert { + condition = module.custom_resp_headers.security_headers.strict_transport_security.enabled == true + error_message = "HSTS should be enabled" + } + + assert { + condition = (module.custom_resp_headers.security_headers.frame_options.enabled == true) && (module.custom_resp_headers.security_headers.frame_options.value == "DENY") + error_message = "Frame options should be enabled and deny" + } + + assert { + condition = module.custom_resp_headers.security_headers.content_type_options.enabled == true + error_message = "Content type options should be enabled" + } + + assert { + condition = (module.custom_resp_headers.security_headers.xss_protection.enabled == true) && (module.custom_resp_headers.security_headers.xss_protection.block == true) + error_message = "XSS protection should be enabled" + } + + assert { + condition = (module.custom_resp_headers.security_headers.referrer_policy.enabled == true) && (module.custom_resp_headers.security_headers.referrer_policy.value == "same-origin") + error_message = "Referrer policy should be enabled with `same-origin`" + } +} + +run "override_defaults" { + command = apply + + module { + source = "../../examples/response-headers" + } + + assert { + condition = module.override_default.security_headers.xss_protection.enabled == false + error_message = "XSS protection can be disabled" + } +} diff --git a/modules/response-headers/variables.tf b/modules/response-headers/variables.tf index d0219ee..5e1f60b 100644 --- a/modules/response-headers/variables.tf +++ b/modules/response-headers/variables.tf @@ -105,7 +105,7 @@ variable "content_type_options_header" { `override` - Whether CloudFront overrides the `X-Content-Type-Options` response header with the header received from the origin. Defaults to `true`. EOF type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) override = optional(bool, true) }) default = {} @@ -124,9 +124,9 @@ variable "frame_options_header" { - `SAMEORIGIN`: The page can only be displayed if all ancestor frames are same origin to the page itself. EOF type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) override = optional(bool, true) - value = optional(string, "") + value = optional(string, "DENY") }) default = {} nullable = false @@ -150,9 +150,9 @@ variable "referrer_policy_header" { - `strict-origin-when-cross-origin`: Send the origin, path, and querystring when performing a same-origin request. For cross-origin requests send the origin (only) when the protocol security level stays same (HTTPS→HTTPS). Don't send the Referer header to less secure destinations (HTTPS→HTTP). EOF type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) override = optional(bool, true) - value = optional(string, "strict-origin-when-cross-origin") + value = optional(string, "same-origin") }) default = {} nullable = false @@ -174,12 +174,12 @@ variable "strict_transport_security_header" { `preload` - Whether CloudFront includes the `preload` directive in the header value. However, it is not part of the HSTS specification and should not be treated as official. Defaults to `false`. EOF type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) override = optional(bool, true) max_age = optional(number, 60 * 60 * 24 * 365) - include_subdomains = optional(bool, false) - preload = optional(bool, false) + include_subdomains = optional(bool, true) + preload = optional(bool, true) }) default = {} nullable = false @@ -198,11 +198,11 @@ variable "xss_protection_header" { `report` - A reporting URI (in the `report` field), which determines whether CloudFront includes the `report='reporting URI'` directive in the header value. You can't specify a reporting URI when block is enabled. EOF type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) override = optional(bool, true) filtering_enabled = optional(bool, true) - block = optional(bool, false) + block = optional(bool, true) report = optional(string, "") }) default = {} From fb24cebd0adc7eeedbdbe666dbad4da030caa515 Mon Sep 17 00:00:00 2001 From: niroz89 Date: Wed, 20 Mar 2024 19:59:28 +0800 Subject: [PATCH 2/6] Update readme --- modules/response-headers/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/response-headers/README.md b/modules/response-headers/README.md index 7aeb91e..3ba5347 100644 --- a/modules/response-headers/README.md +++ b/modules/response-headers/README.md @@ -27,17 +27,17 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [content\_security\_policy\_header](#input\_content\_security\_policy\_header) | A configuration for `Content-Security-Policy` header in HTTP responses sent from CloudFront. The HTTP `Content-Security-Policy` response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks. `content_security_policy_header` as defined below.
`enabled` - Whether to enable `Content-Security-Policy` response header. Defaults to `false`.
`override` - Whether CloudFront overrides the `Content-Security-Policy` response header with the header received from the origin. Defaults to `true`.
`value` - The value for the `Content-Security-Policy` HTTP response header. The `Content-Security-Policy` header value is limited to 1783 characters. |
object({
enabled = optional(bool, false)
override = optional(bool, true)
value = optional(string, "")
})
| `{}` | no | -| [content\_type\_options\_header](#input\_content\_type\_options\_header) | A configuration for `X-Content-Type-Options` header in HTTP responses sent from CloudFront. The `X-Content-Type-Options` response HTTP header is a marker used by the server to indicate that the MIME types advertised in the `Content-Type` headers should be followed and not be changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured. `content_type_options_header` as defined below.
`enabled` - Whether to enable `X-Content-Type-Options` response header. When this setting is `true`, CloudFront adds the `X-Content-Type-Options: nosniff` header to response. (Blocks a request if the request destination is of type style and the MIME type is not text/css, or of type script and the MIME type is not a JavaScript MIME type.) Defaults to `false`.
`override` - Whether CloudFront overrides the `X-Content-Type-Options` response header with the header received from the origin. Defaults to `true`. |
object({
enabled = optional(bool, false)
override = optional(bool, true)
})
| `{}` | no | +| [content\_type\_options\_header](#input\_content\_type\_options\_header) | A configuration for `X-Content-Type-Options` header in HTTP responses sent from CloudFront. The `X-Content-Type-Options` response HTTP header is a marker used by the server to indicate that the MIME types advertised in the `Content-Type` headers should be followed and not be changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured. `content_type_options_header` as defined below.
`enabled` - Whether to enable `X-Content-Type-Options` response header. When this setting is `true`, CloudFront adds the `X-Content-Type-Options: nosniff` header to response. (Blocks a request if the request destination is of type style and the MIME type is not text/css, or of type script and the MIME type is not a JavaScript MIME type.) Defaults to `false`.
`override` - Whether CloudFront overrides the `X-Content-Type-Options` response header with the header received from the origin. Defaults to `true`. |
object({
enabled = optional(bool, true)
override = optional(bool, true)
})
| `{}` | no | | [cors](#input\_cors) | A configuration for a set of HTTP response headers for CORS(Cross-Origin Resource Sharing). `cors` as defined below.
`enabled` - Whether to enable CORS configuration for the response headers policy .
`override` - Whether CloudFront override the response from the origin which contains one of the CORS headers specified in this policy. Defaults to `true`.
`access_control_allow_credentials` - Whether CloudFront adds the `Access-Control-Allow-Credentials` header in responses to CORS requests. When enabled, CloudFront adds the `Access-Control-Allow-Credentials: true` header in responses to CORS requests. Otherwise, CloudFront doesn't add this header to responses. Defaults to `false`.
`access_control_allow_headers` - A set of HTTP header names for CloudFront to include as values for the `Access-Control-Allow-Headers` HTTP response header in responses to CORS preflight requests. Defaults to `["*"]` (All headers).
`access_control_allow_methods` - A set of HTTP methods for CloudFront to include as values for the `Access-Control-Allow-Methods` header in responses to CORS preflight requests. Valid values are `GET`, `DELETE`, `HEAD`, `OPTIONS`, `PATCH`, `POST`, `PUT`, or `ALL`). Defaults to `ALL` (All methods).
`access_control_allow_origins` - A set of the origins that CloudFront can use as values in the `Access-Control-Allow-Origin` response header. Use `*` value to allow CORS requests from all origins. Defaults to `["*"]` (All origins).
`access_control_expose_headers` - A set of HTTP header names for CloudFront to include as values for the `Access-Control-Expose-Headers` header in responses to CORS requests. Defaults to `[]` (None).
`access_control_max_age` - The number of seconds for CloudFront to use as the value for the `Access-Control-Max-Age` header in responses to CORS preflight requests. |
object({
enabled = optional(bool, false)
override = optional(bool, true)

access_control_allow_credentials = optional(bool, false)
access_control_allow_headers = optional(set(string), ["*"])
access_control_allow_methods = optional(set(string), ["ALL"])
access_control_allow_origins = optional(set(string), ["*"])
access_control_expose_headers = optional(set(string), [])
access_control_max_age = optional(number, 600)
})
| `{}` | no | | [custom\_headers](#input\_custom\_headers) | A configuration for specifying the custom HTTP headers in HTTP responses sent from CloudFront. Each item of `custom_headers` as defined below.
`name` - The HTTP response header name.
`value` - The value for the HTTP response header. If a header value is not provided, CloudFront adds the empty header (with no value) to the response.
`override` - Whether CloudFront overrides a response header with the same name received from the origin with the header specifies here. |
list(object({
name = string
value = string
override = optional(bool, false)
}))
| `[]` | no | | [description](#input\_description) | A comment to describe the response headers policy. | `string` | `"Managed by Terraform."` | no | -| [frame\_options\_header](#input\_frame\_options\_header) | A configuration for `X-Frame-Options` header in HTTP responses sent from CloudFront. The `X-Frame-Options` HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a ``, `