Overview
Coraza is an open-source web application firewall that ships with the OWASP Core Rule Set (CRS). This template deploys the coraza-crs image, which runs Coraza inside the Caddy web server, as a reverse proxy in front of a workload you already run. Requests enter the WAF, are inspected against CRS and any rules you add, and only then are forwarded to the workload behind it. The template does not deploy the workload it protects, and it only inspects traffic that is actually routed through it.This template does not create a GVC. It deploys into an existing GVC that you already have, and the workload it protects must already exist in that GVC.
What Gets Created
Prerequisites
1
The workload to protect
The workload you intend to protect must already exist in the same GVC, and the WAF must be able to reach it. Its internal firewall must be
same-gvc, same-org, or a workload list that includes RELEASE_NAME-coraza-waf; an internal firewall of none blocks the WAF. Locking the Protected Workload shows the workload-list form.Set targetWorkload to its fully qualified internal address, WORKLOAD_NAME.GVC_NAME.cpln.local, and targetPort to the port it serves. Do not use the short workload name.Installation
Install the template into the GVC that holds the workload to protect.targetWorkload is required; add --set targetPort=... as well if the workload does not serve on 8080:
UI
Browse, install, and manage templates visually
CLI
Manage templates from your terminal
Terraform
Declare templates in your Terraform configurations
Pulumi
Declare templates in your Pulumi programs
Configuration
Image
- Only
*-caddy-alpine-*builds work. The-nginx-and-apache-builds ofcoraza-crsship no Caddy binary, so this template cannot configure them. Upstream publishes a Caddy build for each CRS release; take the newest of those. - Pin a digest or a datecode, never a moving tag. Tags such as
4.25-caddy-alpine-ltsandcaddy-alpineare repointed upstream, which changes the image under a deployment nobody touched. - The Core Rule Set version comes from the image, so new CRS releases are picked up only when you change
image.
Proxy Target
targetWorkload— the fully qualified internal address of the workload to protect,WORKLOAD_NAME.GVC_NAME.cpln.local.targetPort— the port that workload serves on. The WAF forwards to it over plain HTTP.WAFPort— the container port the WAF listens on, behind its public endpoint.
Resources and Timeout
resources.cpu and timeoutSeconds together set the largest request body the WAF accepts. As a rule of thumb:
resources.cpu first (it inspects the same body faster), then timeoutSeconds, and raise resources.memory with them, since memory use grows with body size. Do not go below about 250m CPU. Two limits you cannot raise from values: Coraza stops inspecting bodies above 12.5 MiB (SecRequestBodyLimit, fixed in the image), and timeoutSeconds also caps how long your own workload may take to answer, so keep it above your slowest response.
Bodies larger than the in-memory limit of Coraza are buffered to disk under /tmp/coraza and inspected in full; there is nothing to enable.
Placement
multiZone— whentrue, spreads replicas across availability zones. Not every location supports multi-zone, so confirm your location does before enabling it.
Rule Engine and Tuning
The chart runs the Coraza rule engine in blocking mode with the CRS defaults baked into the image. It exposes no value for the CRS paranoia level or for a detection-only mode; tuning is done with your own rules in theRELEASE_NAME-coraza-custom-rules secret.
That secret is loaded after the Core Rule Set. It ships with one example rule, which blocks any request whose URI contains attack:
920450 blocks any request carrying an Expect: 100-continue header, which some HTTP clients add automatically for large uploads. Removing that one rule leaves the rest of CRS in force. Give your own rules IDs outside the CRS ranges: a rule whose ID collides with a CRS rule silently overrides it.
Changing Custom Rules shows how to apply an edit.
Logging
Coraza writes its access, audit and debug logs to standard output, so they are readable in the built-in logging interface alongside the[INFO] and [FATAL] lines from the startup hook:
Connecting
Read the canonical endpoint:
/; the second returns 403 from the WAF.
Locking the Protected Workload
The WAF only protects traffic that passes through it. If the protected workload keeps its own public endpoint, clients can skip the WAF entirely. Close the protected workload to the internet and allow only the WAF on its internal firewall. In the Console, open the firewall settings of the protected workload, or set these fields in its workload spec:publicAccess.enabled: false and an internal access list naming RELEASE_NAME-coraza-waf), so a later upgrade of that template does not reopen it. A firewall change can take a few minutes to take effect, so re-test the own endpoint of the protected workload before relying on the lock.
Operations
Changing Configuration
Changeimage, targetWorkload, targetPort, WAFPort, resources, timeoutSeconds or multiZone in your values file, then upgrade:
Changing Custom Rules
Custom rules are not a Helm value. Edit theRELEASE_NAME-coraza-custom-rules secret in the Console, or from the CLI. Read the current rules first:
Upgrading from 1.1.1 or Earlier
Version 1.2.0 changed three things:diskBodyInspection(added in 1.1.0) was removed. Large-body disk buffering now always happens, so delete the key from your values file if it is there.- The
resourcesdefaults rose from50m/128Mito500m/512Mi. If your values file pins the old figures, raise them, or POST bodies above a few tens of KB return504. timeoutSecondswas added, defaulting to30. Earlier versions used a fixed 5-second timeout.
cpln helm upgrade is the supported path.
Scaling and Availability
The WAF is stateless. It autoscales on CPU between one and three replicas, which the chart fixes; there is no value to change those bounds. While a single replica is running, a redeployment or a replica restart briefly interrupts traffic to the protected workload. SetmultiZone: true to spread replicas across zones where your location supports it.
Troubleshooting
The WAF restarts with PostStartHook failed
The WAF restarts with PostStartHook failed
Symptom: The workload never becomes ready, and the deployment reports
PostStartHook failed.Cause: The startup hook could not configure the reverse proxy and exits on purpose, so the WAF restarts rather than serve uninspected traffic. The usual cause is an image that is not a *-caddy-alpine-* build, or a moving tag that upstream repointed.Fix: Find the [FATAL] line that names the failed check, then pin a Caddy build by digest or datecode:The container exits immediately with code 127
The container exits immediately with code 127
Symptom: The last log line is
Launching caddy run ..., the container exits with exitCode: 127, and there is no [FATAL] line.Cause: image points at an nginx or apache build of coraza-crs, which has no Caddy binary.Fix: Set image to a *-caddy-alpine-* build and upgrade. Read the exit code here:Large POST requests return 504
Large POST requests return 504
Symptom: GET requests work, but larger POST or upload requests return
504 from the WAF.Cause: Inspecting the body took longer than timeoutSeconds at the configured resources.cpu.Fix: Raise resources.cpu, then timeoutSeconds, and resources.memory with them, as described in Resources and Timeout.Uploads return 403 regardless of content
Uploads return 403 regardless of content
Symptom: Large uploads are rejected with
403, while the same body succeeds without the WAF.Cause: The client sends Expect: 100-continue, which CRS rule 920450 blocks.Fix: Add SecRuleRemoveById 920450 to the custom rules secret and redeploy, as described in Changing Custom Rules.Requests through the WAF fail while the protected workload is healthy
Requests through the WAF fail while the protected workload is healthy
Symptom: The WAF is ready, but requests to its endpoint fail instead of returning the response of the protected workload.Cause: The WAF cannot reach
targetWorkload:targetPort. Typical reasons are a short workload name instead of the fully qualified address, a wrong targetPort, or an internal firewall on the protected workload that does not allow RELEASE_NAME-coraza-waf.Fix: Set targetWorkload to WORKLOAD_NAME.GVC_NAME.cpln.local, check the port, allow the WAF on the internal firewall of the protected workload, then upgrade. Read the WAF logs for the upstream error:Important Notes
- Lock the protected workload so it is reachable only through the WAF; otherwise requests bypass inspection.
- Set
targetWorkloadto the fully qualifiedWORKLOAD_NAME.GVC_NAME.cpln.localaddress, never the short name. - Pin
imageby digest or datecode, and only to a*-caddy-alpine-*build. - Force a redeployment after editing the custom rules secret; running replicas keep the rules they started with.
- Size
resources.cpuandtimeoutSecondstogether; a body too large to inspect in time returns504from the WAF. - Do not override the
commandorargsof the container; they carry the startup hook diagnostics into the logs.
External References
OWASP Coraza Documentation
Official Coraza WAF documentation and tutorials
OWASP CRS Documentation
Core Rule Set documentation and rule reference
seclang Directives
Directive reference for writing custom rules
Coraza CRS Docker
Image source, supported variants, and available tags
Coraza Template
View the source files, default values, and chart definition