Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cockroach/versions/1.4.0/templates/workload-cockroach.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ spec:
{{- if .Values.pgbouncer.enabled }}
inboundAllowType: workload-list
inboundAllowWorkload:
- //gvc/{{ .Values.gvc.name }}/workload/{{ include "cockroach.name" . }}
- //gvc/{{ .Values.gvc.name }}/workload/{{ include "cockroach.pgbouncer.name" . }}
{{- else }}
inboundAllowType: {{ .Values.internal_access.type }}
Expand Down
18 changes: 18 additions & 0 deletions coraza/versions/1.1.1/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v2
name: coraza
description: Coraza web application firewall (WAF) for Control Plane

type: application
version: 1.1.1
appVersion: "20241018"

dependencies:
- name: cpln-common
version: 1.0.0
repository: "oci://ghcr.io/controlplane-com/templates"

annotations:
created: "2025-10-23"
lastModified: "2026-05-13"
category: "security"
createsGvc: false
53 changes: 53 additions & 0 deletions coraza/versions/1.1.1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
## Coraza WAF App

Creates a Coraza Web Application Firewall (WAF) with OWASP Core Rule Set (CRS) integration that proxies traffic to a target workload, providing comprehensive security filtering and protection.

### Configuration

The following values can be configured in your values file:

- `targetWorkload`: The internal name of the workload to proxy traffic to (`WORKLOAD_NAME.GVC_NAME.cpln.local`)
- `targetPort`: The port of the target workload to proxy traffic to
- `WAFPort`: The port on the WAF workload to expose to the internet
- `resources`: Reserved resources for the workload
- `multiZone`: Deploys replicas across multiple zones
- `diskBodyInspection`: When `true` (default), request bodies exceeding the 512KB in-memory limit are buffered to disk at `/tmp/coraza` for full inspection up to 12.5MB. When `false`, all body inspection is kept in memory — bodies up to 12.5MB are held in memory rather than spilling to disk, which avoids disk I/O but increases memory pressure on large requests.

### Logging

All Coraza logging is currently sent to `/dev/stdout` to be readable in the Control Plane built-in logging interface. Logging can be redirected by using the existing environment variables in the workload configuration.

### Advanced Configuration

Coraza configuration is largely specified through environment variables and can be customized by the user once installed. You can modify these environment variables in the workload configuration to adjust Coraza's behavior, logging levels, and security policies according to your specific requirements.

### Usage

The Coraza WAF will act as a reverse proxy, filtering incoming requests before forwarding them to your target workload. Configure the `targetWorkload` and `targetPort` values to point to your application, then the WAF will be accessible on the specified `WAFPort`.

**Important**: The target workload must be configured with internal access set to `same-gvc`, `same-org`, or specifically allow this workload in order for the WAF to reach it.

### Security Features

Coraza provides web application firewall capabilities including:
- Automatic integration of OWASP Core Rule Set (CRS) for comprehensive protection
- Request filtering and validation
- Protection against common web attacks
- Custom rule configuration
- Traffic monitoring and logging

### Custom Rules

After installation, you can add custom rules by editing the created secret with the suffix `coraza-custom-rules`. The secret contains an example rule that blocks requests containing "attack" in the URI:

```
SecRule REQUEST_URI "@rx attack" "id:1001,phase:1,deny,msg:'Blocked attack attempt'"
```

**Note**: After modifying the custom rules secret, you must restart the workload replicas for the changes to take effect. See the Coraza and CRS documentation below for instructions on creating custom rules.

## Additional Resources

- [OWASP Coraza Docs](https://coraza.io/docs/tutorials/introduction/)
- [OWASP CRS Docs](https://coreruleset.org/docs/)
- [Coraza Caddy README](https://github.com/coreruleset/coraza-crs-docker#)
46 changes: 46 additions & 0 deletions coraza/versions/1.1.1/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{{/* Resource Naming */}}

{{/*
Coraza Workload Name
*/}}
{{- define "coraza.name" -}}
{{- printf "%s-coraza-waf" .Release.Name }}
{{- end }}

{{/*
Coraza Secret Custom Rules Name
*/}}
{{- define "coraza.secretRules.name" -}}
{{- printf "%s-coraza-custom-rules" .Release.Name }}
{{- end }}

{{/*
Coraza Secret Startup Name
*/}}
{{- define "coraza.secretStartup.name" -}}
{{- printf "%s-coraza-startup" .Release.Name }}
{{- end }}

{{/*
Coraza Identity Name
*/}}
{{- define "coraza.identity.name" -}}
{{- printf "%s-coraza-identity" .Release.Name }}
{{- end }}

{{/*
Coraza Policy Name
*/}}
{{- define "coraza.policy.name" -}}
{{- printf "%s-coraza-policy" .Release.Name }}
{{- end }}


{{/* Labeling */}}

{{/*
Common labels
*/}}
{{- define "coraza.tags" -}}
{{- include "cpln-common.tags" . }}
{{- end }}
6 changes: 6 additions & 0 deletions coraza/versions/1.1.1/templates/identity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
kind: identity
gvc: {{ .Values.global.cpln.gvc }}
name: {{ include "coraza.identity.name" . }}
description: Coraza identity
tags: {{- include "coraza.tags" . | nindent 4 }}
14 changes: 14 additions & 0 deletions coraza/versions/1.1.1/templates/policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
kind: policy
name: {{ include "coraza.policy.name" . }}
description: Coraza WAF policy
tags: {{- include "coraza.tags" . | nindent 4 }}
bindings:
- permissions:
- reveal
principalLinks:
- //gvc/{{ .Values.global.cpln.gvc }}/identity/{{ include "coraza.identity.name" . }}
targetKind: secret
targetLinks:
- //secret/{{ include "coraza.secretStartup.name" . }}
- //secret/{{ include "coraza.secretRules.name" . }}
15 changes: 15 additions & 0 deletions coraza/versions/1.1.1/templates/secret-custom-rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
kind: secret
name: {{ include "coraza.secretRules.name" . }}
description: Coraza WAF custom rules
tags: {{- include "coraza.tags" . | nindent 4 }}
type: opaque
data:
encoding: plain
payload: |-
# Add your custom rules here

# Example Rule: block requests containing "attack" in the URI
# Test by querying the endpoint with /attack

SecRule REQUEST_URI "@rx attack" "id:1001,phase:1,deny,msg:'Blocked attack attempt'"
88 changes: 88 additions & 0 deletions coraza/versions/1.1.1/templates/secret-startup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
kind: secret
name: {{ include "coraza.secretStartup.name" . }}
description: Coraza WAF startup script
tags: {{- include "coraza.tags" . | nindent 4 }}
type: opaque
data:
encoding: plain
payload: >
#!/bin/sh

set -u

# Script sends patch request to Caddy admin API to configure proper header for reverse proxy to target workload

# Caddy admin API URL and port

ADMIN_URL="127.0.0.1"

ADMIN_PORT="2019"

ROUTE_PATH="/config/apps/http/servers/srv0/routes/0/handle/1"

PATCH_FILE="/tmp/proxy_patch.json"


{{ if .Values.diskBodyInspection }}
mkdir -p /tmp/coraza
{{ end }}

echo "[INFO] Waiting for Caddy admin API..."

until wget -q --spider "http://${ADMIN_URL}:${ADMIN_PORT}/config/"
2>/dev/null; do
sleep 1
done

echo "[INFO] Caddy admin API is up."


# Create patch file with proper header

cat > "${PATCH_FILE}" <<'EOF'

{
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"Host": ["{{ .Values.targetWorkload }}"]
}
}
},
"trusted_proxies": ["192.168.0.0/16","172.16.0.0/12","10.0.0.0/8","127.0.0.1/8","fd00::/8","::1"],
"upstreams": [
{ "dial": "{{ .Values.targetWorkload }}:{{ .Values.targetPort }}" }
]
}

EOF


echo "[INFO] Applying PATCH via netcat..."

LEN=$(wc -c < "${PATCH_FILE}")

# Requests patch command to admin API using netcat

RESPONSE=$(
{
printf "PATCH %s HTTP/1.1\r\nHost: %s:%s\r\nContent-Type: application/json\r\nContent-Length: %s\r\nConnection: close\r\n\r\n" \
"${ROUTE_PATH}" "${ADMIN_URL}" "${ADMIN_PORT}" "${LEN}"
cat "${PATCH_FILE}"
} | nc ${ADMIN_URL} ${ADMIN_PORT} || true
)


echo "$RESPONSE"


if echo "$RESPONSE" | grep -q "200 OK"; then
echo "[INFO] Patch succeeded."
else
echo "[WARN] Patch may have failed — see response above."
fi


rm -f "${PATCH_FILE}"
82 changes: 82 additions & 0 deletions coraza/versions/1.1.1/templates/workload.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
kind: workload
name: {{ include "coraza.name" . }}
description: Coraza Web Application Firewall (WAF)
tags: {{- include "coraza.tags" . | nindent 4 }}
spec:
type: standard
containers:
- name: coraza-crs
cpu: {{ .Values.resources.cpu | quote }}
env:
- name: ACCESSLOG
value: /dev/stdout
- name: BACKEND
value: http://{{ .Values.targetWorkload }}:{{ .Values.targetPort }}
- name: CORAZA_AUDIT_LOG
value: /dev/stdout
- name: CORAZA_AUDIT_LOG_PARTS
value: ABDEFHIJZ
- name: CORAZA_DEBUG_LOG
value: /dev/stdout
- name: CORAZA_DEBUG_LOGLEVEL
value: '1'
- name: CORAZA_RULE_ENGINE
value: 'On'
{{- if not .Values.diskBodyInspection }}
- name: CORAZA_REQ_BODY_NOFILES_LIMIT
value: '13107200'
{{- end }}
- name: PORT
value: "{{ .Values.WAFPort }}"
image: {{ .Values.image }}
inheritEnv: false
lifecycle:
postStart:
exec:
command:
- /bin/sh
- /opt/coraza/startup.sh
memory: {{ .Values.resources.memory | quote }}
ports:
- number: {{ .Values.WAFPort }}
protocol: http
volumes:
- path: /opt/coraza/rules.d/custom.conf
recoveryPolicy: retain
uri: cpln://secret/{{ include "coraza.secretRules.name" . }}
- path: /opt/coraza/startup.sh
recoveryPolicy: retain
uri: cpln://secret/{{ include "coraza.secretStartup.name" . }}
defaultOptions:
multiZone:
enabled: {{ .Values.multiZone }}
autoscaling:
maxConcurrency: 0
maxScale: 3
metric: cpu
minScale: 1
scaleToZeroDelay: 300
target: 100
capacityAI: false
debug: false
suspend: false
timeoutSeconds: 5
firewallConfig:
external:
inboundAllowCIDR:
- 0.0.0.0/0
inboundBlockedCIDR: []
outboundAllowCIDR: []
outboundAllowHostname: []
outboundAllowPort: []
outboundBlockedCIDR: []
internal:
inboundAllowType: same-gvc
inboundAllowWorkload: []
identityLink: //gvc/{{ .Values.global.cpln.gvc }}/identity/{{ include "coraza.identity.name" . }}
loadBalancer:
direct:
enabled: false
ports: []
replicaDirect: false
supportDynamicTags: false
16 changes: 16 additions & 0 deletions coraza/versions/1.1.1/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
image: ghcr.io/coreruleset/coraza-crs@sha256:eed7280e0de4820507b500b1ee10de820c175165d5cce329609bf34f32977af8 # Coraza GitHub image

# MUST BE CHANGED
targetWorkload: my-workload.my-gvc.cpln.local # Workload internal name of the workload to proxy traffic to

targetPort: 8080 # Port of the workload to proxy traffic to

WAFPort: 80 # Port on the WAF workload to expose to the internet

resources:
cpu: 50m
memory: 128Mi

multiZone: false

diskBodyInspection: true # When true, request bodies exceeding the in-memory limit are buffered to disk for inspection. Disable to keep all body inspection in memory.
Loading