diff --git a/README.md b/README.md index a75ab7a..4413118 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ No modules. | [cloudflare_dns_record.xnoto_dev_root](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/dns_record) | resource | | [cloudflare_dns_record.xnoto_dev_www](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/dns_record) | resource | | [cloudflare_ruleset.cache_rules](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/ruleset) | resource | +| [cloudflare_ruleset.response_headers](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/ruleset) | resource | | [cloudflare_zero_trust_access_application.alertmanager](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource | | [cloudflare_zero_trust_access_application.grafana_alerts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource | | [cloudflare_zero_trust_access_application.k3s](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource | @@ -101,3 +102,27 @@ This root owns only the bootstrap tunnel DNS required to reach the Kubernetes API before cluster workloads are available. `TunnelBinding` owns workload tunnel DNS, including the MCP gateway endpoints. Do not add a workload hostname to this OpenTofu root. + +## OpenCode HSTS + +`cf-opencode-hsts.tf` configures the zone response-header ruleset with a rule +matching only HTTPS requests to `opencode.makeitwork.cloud`. It sets +`Strict-Transport-Security: max-age=86400` (one day), without +`includeSubDomains` or `preload`. Native OpenCode authentication and existing +HTTPS redirection are unchanged. No Cloudflare Access, workload DNS/tunnel, +or AWX cleanup is included. + +This root owns the response-header entrypoint. Review and preserve its full +rule list before adding other header rules; do not create a competing +entrypoint or adopt an existing one without reviewing its rules. PR CI plans +validate the proposed changes; `main` runs a fresh environment-associated +apply, not the saved PR plan. Merge and apply authorization are separate. + +After approved apply, verify the HTTPS UI and unauthenticated API responses +carry the header, native phone login and session continuity still work, and +sibling hostnames are unaffected. A successful plan is not live verification. + +For rollback, first serve `max-age=0` over HTTPS through this rule, then remove +it after the rollback has been reviewed and applied. Simply removing the +header does not clear browser policies; they otherwise expire up to one day +after their last receipt. Preserve HTTPS throughout. diff --git a/cf-opencode-hsts.tf b/cf-opencode-hsts.tf new file mode 100644 index 0000000..b142597 --- /dev/null +++ b/cf-opencode-hsts.tf @@ -0,0 +1,25 @@ +resource "cloudflare_ruleset" "response_headers" { + zone_id = local.zone_id + name = "Response header transforms" + description = "Hostname-scoped response security headers" + kind = "zone" + phase = "http_response_headers_transform" + + rules = [ + { + ref = "opencode_hsts" + action = "rewrite" + action_parameters = { + headers = { + "strict-transport-security" = { + operation = "set" + value = "max-age=86400" + } + } + } + expression = "(http.host eq \"opencode.makeitwork.cloud\" and http.request.scheme eq \"https\")" + description = "One-day HSTS for the OpenCode HTTPS hostname only" + enabled = true + } + ] +}