diff --git a/cockroach/versions/1.4.0/templates/workload-cockroach.yaml b/cockroach/versions/1.4.0/templates/workload-cockroach.yaml index 5d2834c..c26355a 100644 --- a/cockroach/versions/1.4.0/templates/workload-cockroach.yaml +++ b/cockroach/versions/1.4.0/templates/workload-cockroach.yaml @@ -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 }} diff --git a/coraza/versions/1.1.1/Chart.yaml b/coraza/versions/1.1.1/Chart.yaml new file mode 100644 index 0000000..e3bb23e --- /dev/null +++ b/coraza/versions/1.1.1/Chart.yaml @@ -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 \ No newline at end of file diff --git a/coraza/versions/1.1.1/README.md b/coraza/versions/1.1.1/README.md new file mode 100644 index 0000000..6789b96 --- /dev/null +++ b/coraza/versions/1.1.1/README.md @@ -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#) \ No newline at end of file diff --git a/coraza/versions/1.1.1/templates/_helpers.tpl b/coraza/versions/1.1.1/templates/_helpers.tpl new file mode 100644 index 0000000..be9ee0b --- /dev/null +++ b/coraza/versions/1.1.1/templates/_helpers.tpl @@ -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 }} diff --git a/coraza/versions/1.1.1/templates/identity.yaml b/coraza/versions/1.1.1/templates/identity.yaml new file mode 100644 index 0000000..aa2418c --- /dev/null +++ b/coraza/versions/1.1.1/templates/identity.yaml @@ -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 }} \ No newline at end of file diff --git a/coraza/versions/1.1.1/templates/policy.yaml b/coraza/versions/1.1.1/templates/policy.yaml new file mode 100644 index 0000000..c560cae --- /dev/null +++ b/coraza/versions/1.1.1/templates/policy.yaml @@ -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" . }} \ No newline at end of file diff --git a/coraza/versions/1.1.1/templates/secret-custom-rules.yaml b/coraza/versions/1.1.1/templates/secret-custom-rules.yaml new file mode 100644 index 0000000..0e02582 --- /dev/null +++ b/coraza/versions/1.1.1/templates/secret-custom-rules.yaml @@ -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'" \ No newline at end of file diff --git a/coraza/versions/1.1.1/templates/secret-startup.yaml b/coraza/versions/1.1.1/templates/secret-startup.yaml new file mode 100644 index 0000000..50c37dd --- /dev/null +++ b/coraza/versions/1.1.1/templates/secret-startup.yaml @@ -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}" \ No newline at end of file diff --git a/coraza/versions/1.1.1/templates/workload.yaml b/coraza/versions/1.1.1/templates/workload.yaml new file mode 100644 index 0000000..868d2e5 --- /dev/null +++ b/coraza/versions/1.1.1/templates/workload.yaml @@ -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 diff --git a/coraza/versions/1.1.1/values.yaml b/coraza/versions/1.1.1/values.yaml new file mode 100644 index 0000000..a28eee5 --- /dev/null +++ b/coraza/versions/1.1.1/values.yaml @@ -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. \ No newline at end of file