diff --git a/Makefile b/Makefile
index 4ece674..85cd503 100644
--- a/Makefile
+++ b/Makefile
@@ -77,7 +77,9 @@ test: _pull-tf
echo "------------------------------------------------------------"; \
echo "# Terraform init"; \
echo "------------------------------------------------------------"; \
- if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" hashicorp/terraform:$(TF_VERSION) \
+ if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" \
+ --workdir "$${DOCKER_PATH}" --network host \
+ hashicorp/terraform:$(TF_VERSION) \
init \
-lock=false \
-upgrade \
@@ -88,22 +90,30 @@ test: _pull-tf
echo "OK"; \
else \
echo "Failed"; \
- docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
+ docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" \
+ --workdir "$${DOCKER_PATH}" --network none --entrypoint=rm \
+ hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
exit 1; \
fi; \
echo; \
echo "------------------------------------------------------------"; \
echo "# Terraform validate"; \
echo "------------------------------------------------------------"; \
- if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" hashicorp/terraform:$(TF_VERSION) \
+ if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" \
+ --workdir "$${DOCKER_PATH}" --network host \
+ hashicorp/terraform:$(TF_VERSION) \
validate \
$(ARGS) \
.; then \
echo "OK"; \
- docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
+ docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" \
+ --workdir "$${DOCKER_PATH}" --network none --entrypoint=rm \
+ hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
else \
echo "Failed"; \
- docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
+ docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t" \
+ --workdir "$${DOCKER_PATH}" --network none --entrypoint=rm \
+ hashicorp/terraform:$(TF_VERSION) -rf .terraform/ || true; \
exit 1; \
fi; \
echo; \
@@ -117,7 +127,7 @@ _gen-main:
@echo "------------------------------------------------------------"
@echo "# Main module"
@echo "------------------------------------------------------------"
- @if docker run $$(tty -s && echo "-it" || echo) --rm \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='' \
-e DELIM_CLOSE='' \
@@ -128,7 +138,7 @@ _gen-main:
echo "Failed"; \
exit 1; \
fi
- @if docker run $$(tty -s && echo "-it" || echo) --rm \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='' \
-e DELIM_CLOSE='' \
@@ -139,7 +149,7 @@ _gen-main:
echo "Failed"; \
exit 1; \
fi
- @if docker run $$(tty -s && echo "-it" || echo) --rm \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='' \
-e DELIM_CLOSE='' \
@@ -150,7 +160,7 @@ _gen-main:
echo "Failed"; \
exit 1; \
fi
- @if docker run $$(tty -s && echo "-it" || echo) --rm \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='' \
-e DELIM_CLOSE='' \
@@ -161,7 +171,7 @@ _gen-main:
echo "Failed"; \
exit 1; \
fi
- @if docker run $$(tty -s && echo "-it" || echo) --rm \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='' \
-e DELIM_CLOSE='' \
@@ -180,7 +190,7 @@ _gen-examples:
echo "------------------------------------------------------------"; \
echo "# $${DOCKER_PATH}"; \
echo "------------------------------------------------------------"; \
- if docker run $$(tty -s && echo "-it" || echo) --rm \
+ if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='$(DELIM_START)' \
-e DELIM_CLOSE='$(DELIM_CLOSE)' \
@@ -200,7 +210,7 @@ _gen-modules:
echo "------------------------------------------------------------"; \
echo "# $${DOCKER_PATH}"; \
echo "------------------------------------------------------------"; \
- if docker run $$(tty -s && echo "-it" || echo) --rm \
+ if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
-v $(CURRENT_DIR):/data \
-e DELIM_START='$(DELIM_START)' \
-e DELIM_CLOSE='$(DELIM_CLOSE)' \
@@ -218,12 +228,12 @@ _lint-files: _pull-fl
@echo "################################################################################"
@echo "# File-lint"
@echo "################################################################################"
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-cr --text --ignore '$(FL_IGNORE_PATHS)' --path .
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-crlf --text --ignore '$(FL_IGNORE_PATHS)' --path .
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-single-newline --text --ignore '$(FL_IGNORE_PATHS)' --path .
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-space --text --ignore '$(FL_IGNORE_PATHS)' --path .
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8 --text --ignore '$(FL_IGNORE_PATHS)' --path .
- @docker run $$(tty -s && echo "-it" || echo) --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8-bom --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-cr --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-crlf --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-single-newline --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-space --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8 --text --ignore '$(FL_IGNORE_PATHS)' --path .
+ @docker run $$(tty -s && echo "-it" || echo) --rm --network none -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8-bom --text --ignore '$(FL_IGNORE_PATHS)' --path .
_lint-fmt: _pull-tf
@# Lint all Terraform files
@@ -234,7 +244,9 @@ _lint-fmt: _pull-tf
@echo "------------------------------------------------------------"
@echo "# *.tf files"
@echo "------------------------------------------------------------"
- @if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/t:ro" --workdir "/t" hashicorp/terraform:$(TF_VERSION) \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network host \
+ -v "$(CURRENT_DIR):/t:ro" --workdir "/t" \
+ hashicorp/terraform:$(TF_VERSION) \
fmt -recursive -check=true -diff=true -write=true -list=true .; then \
echo "OK"; \
else \
@@ -245,7 +257,9 @@ _lint-fmt: _pull-tf
@echo "------------------------------------------------------------"
@echo "# *.tfvars files"
@echo "------------------------------------------------------------"
- @if docker run $$(tty -s && echo "-it" || echo) --rm --entrypoint=/bin/sh -v "$(CURRENT_DIR):/t:ro" --workdir "/t" hashicorp/terraform:$(TF_VERSION) \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network host \
+ --entrypoint=/bin/sh -v "$(CURRENT_DIR):/t:ro" --workdir "/t" \
+ hashicorp/terraform:$(TF_VERSION) \
-c "find . -name '*.tfvars' -type f -print0 | xargs -0 -n1 terraform fmt -check=true -write=true -diff=true -list=true"; then \
echo "OK"; \
else \
@@ -259,7 +273,9 @@ _lint-json: _pull-jl
@echo "################################################################################"
@echo "# Jsonlint"
@echo "################################################################################"
- @if docker run $$(tty -s && echo "-it" || echo) --rm -v "$(CURRENT_DIR):/data:ro" cytopia/jsonlint:$(JL_VERSION) \
+ @if docker run $$(tty -s && echo "-it" || echo) --rm --network none \
+ -v "$(CURRENT_DIR):/data:ro" \
+ cytopia/jsonlint:$(JL_VERSION) \
-t ' ' -i '*.terraform/*' '*.json'; then \
echo "OK"; \
else \
diff --git a/README.md b/README.md
index a5a4c4f..bbf9138 100644
--- a/README.md
+++ b/README.md
@@ -49,10 +49,36 @@ Description: Cloudflare domain to apply rules for.
Type: `string`
+### [name](#input\_name)
+
+Description: Name of the ruleset.
+
+Type: `string`
+
+### [kind](#input\_kind)
+
+Description: Type of Ruleset to create.
+
+Type: `string`
+
+### [phase](#input\_phase)
+
+Description: Point in the request/response lifecycle where the ruleset will be created.
+
+Type: `string`
+
## Optional Inputs
The following input variables are optional (have default values):
+### [description](#input\_description)
+
+Description: Brief summary of the ruleset and its intended use.
+
+Type: `string`
+
+Default: `null`
+
### [rules](#input\_rules)
Description: List of Cloudflare firewall rule objects.
@@ -61,11 +87,11 @@ Type:
```hcl
list(object({
- description = string
- enabled = bool
- action = string
expression = string
- products = list(string)
+ action = optional(string)
+ description = optional(string)
+ enabled = optional(bool, true)
+ products = optional(list(string), [])
}))
```
diff --git a/examples/bots/README.md b/examples/bots/README.md
new file mode 100644
index 0000000..0aed5f7
--- /dev/null
+++ b/examples/bots/README.md
@@ -0,0 +1,39 @@
+# Example
+
+This example will create multiple rulesets for `http_request_firewall_custom` phase.
+
+
+## Requirements
+
+No requirements.
+
+## Providers
+
+No providers.
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [rulesets](#module\_rulesets) | ./../../ | n/a |
+
+## Resources
+
+No resources.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [api\_token](#input\_api\_token) | The Cloudflare API token. | `string` | n/a | yes |
+| [domain](#input\_domain) | Cloudflare domain name to create | `string` | `"example.com"` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [records](#output\_records) | Cloudflare Zone DNS Records |
+
+
+
+Copyright (c) 2024 **[Flaconi GmbH](https://github.com/flaconi)**
diff --git a/examples/bots/main.tf b/examples/bots/main.tf
new file mode 100644
index 0000000..3939d40
--- /dev/null
+++ b/examples/bots/main.tf
@@ -0,0 +1,43 @@
+module "rulesets" {
+ source = "./../../"
+ api_token = var.api_token
+ domain = var.domain
+ name = "default"
+ kind = "zone"
+ phase = "http_request_firewall_custom"
+ rules = [
+ {
+ description = "User-Agent: skip"
+ enabled = true
+ action = "skip"
+ expression = <<-EOT
+ (http.user_agent contains "Bot/" and http.request.uri.path eq "/api/v1")
+ EOT
+ products = ["waf"]
+ },
+ {
+ description = "User-Agent: log"
+ enabled = false
+ action = "log"
+ expression = <<-EOT
+ (http.user_agent contains "Bot/" and http.request.uri.path eq "/api/v1")
+ EOT
+ products = []
+ },
+ {
+ description = "Bots: log"
+ action = "log"
+ expression = <<-EOT
+ (cf.bot_management.score eq 2)
+ EOT
+ },
+ {
+ description = "Bots: challenge"
+ enabled = false
+ action = "managed_challenge"
+ expression = <<-EOT
+ (cf.bot_management.score eq 9)
+ EOT
+ },
+ ]
+}
diff --git a/examples/bots/outputs.tf b/examples/bots/outputs.tf
new file mode 100644
index 0000000..de4f7bb
--- /dev/null
+++ b/examples/bots/outputs.tf
@@ -0,0 +1,4 @@
+output "records" {
+ description = "Cloudflare Zone DNS Records"
+ value = module.rulesets.rules
+}
diff --git a/examples/bots/variables.tf b/examples/bots/variables.tf
new file mode 100644
index 0000000..59baa16
--- /dev/null
+++ b/examples/bots/variables.tf
@@ -0,0 +1,10 @@
+variable "api_token" {
+ description = "The Cloudflare API token."
+ type = string
+}
+
+variable "domain" {
+ description = "Cloudflare domain name to create"
+ type = string
+ default = "example.com"
+}
diff --git a/main.tf b/main.tf
index 8e4e00b..97c8760 100644
--- a/main.tf
+++ b/main.tf
@@ -1,8 +1,9 @@
-resource "cloudflare_ruleset" "http_request_firewall_custom" {
- zone_id = lookup(data.cloudflare_zones.domain.zones[0], "id")
- name = "default"
- kind = "zone"
- phase = "http_request_firewall_custom"
+resource "cloudflare_ruleset" "this" {
+ zone_id = lookup(data.cloudflare_zones.domain.zones[0], "id")
+ name = var.name
+ kind = var.kind
+ phase = var.phase
+ description = var.description
dynamic "rules" {
for_each = local.rules
diff --git a/outputs.tf b/outputs.tf
index 6cafae1..bba0e47 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -5,5 +5,5 @@ output "domain" {
output "rules" {
description = "Created Cloudflare rules for the current zone."
- value = cloudflare_ruleset.http_request_firewall_custom.rules
+ value = cloudflare_ruleset.this.rules
}
diff --git a/variables.tf b/variables.tf
index 1a3296a..398726a 100644
--- a/variables.tf
+++ b/variables.tf
@@ -9,14 +9,49 @@ variable "domain" {
type = string
}
+variable "name" {
+ description = "Name of the ruleset."
+ type = string
+}
+
+variable "kind" {
+ description = "Type of Ruleset to create."
+ type = string
+
+ # Ensure we specify only allowed kind values
+ # https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/ruleset#kind
+ validation {
+ condition = can(contains(["custom", "managed", "root", "zone"], var.kind))
+ error_message = "Only the following kind types are allowed: custom, managed, root, zone."
+ }
+}
+
+variable "phase" {
+ description = "Point in the request/response lifecycle where the ruleset will be created."
+ type = string
+
+ # Ensure we specify only allowed kind values
+ # https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/ruleset#phase
+ validation {
+ condition = can(contains(["ddos_l4", "ddos_l7", "http_config_settings", "http_custom_errors", "http_log_custom_fields", "http_ratelimit", "http_request_cache_settings", "http_request_dynamic_redirect", "http_request_firewall_custom", "http_request_firewall_managed", "http_request_late_transform", "http_request_origin", "http_request_redirect", "http_request_sanitize", "http_request_sbfm", "http_request_transform", "http_response_compression", "http_response_firewall_managed", "http_response_headers_transform", "magic_transit"], var.phase))
+ error_message = "Only the following phase types are allowed: ddos_l4, ddos_l7, http_config_settings, http_custom_errors, http_log_custom_fields, http_ratelimit, http_request_cache_settings, http_request_dynamic_redirect, http_request_firewall_custom, http_request_firewall_managed, http_request_late_transform, http_request_origin, http_request_redirect, http_request_sanitize, http_request_sbfm, http_request_transform, http_response_compression, http_response_firewall_managed, http_response_headers_transform, magic_transit."
+ }
+}
+
+variable "description" {
+ description = "Brief summary of the ruleset and its intended use."
+ type = string
+ default = null
+}
+
variable "rules" {
description = "List of Cloudflare firewall rule objects."
type = list(object({
- description = string
- enabled = bool
- action = string
expression = string
- products = list(string)
+ action = optional(string)
+ description = optional(string)
+ enabled = optional(bool, true)
+ products = optional(list(string), [])
}))
default = []