Skip to content

Commit ef737dc

Browse files
[EV-6466]: Add Gateway WAF (per-route & namespaced) docs section (#2758)
1 parent 79a3ca2 commit ef737dc

14 files changed

Lines changed: 1768 additions & 0 deletions

File tree

.github/styles/config/vocabularies/CalicoTerminology/accept.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,20 @@ VNet[s]?
144144
[dD]ecap(sulation)?
145145
[rR]epo[s]?
146146
[dD]iags
147+
148+
# Gateway WAF (per-route and namespaced WAF)
149+
[rR]ego
150+
[aA]llowlist(ed|ing|s)?
151+
GlobalWAFPolic(y|ies)
152+
WAFPolic(y|ies)
153+
GlobalWAFPlugins?
154+
WAFPlugins?
155+
GlobalWAFValidationPolic(y|ies)
156+
WAFValidationPolic(y|ies)
157+
targetRefs?
158+
namespaceSelector
159+
paranoiaLevel
160+
coreRuleSet
161+
defaultAction
162+
enforcementMode
163+
SecLang

calico-enterprise/threat/deploying-waf-ingress-gateway.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ This feature is tech preview. Tech preview features may be subject to significan
1313

1414
:::
1515

16+
:::caution
17+
18+
This page describes the original, Gateway-wide WAF for Calico Ingress Gateway. It is superseded by [Gateway WAF](./gateway-waf/overview.mdx), which adds per-route and per-namespace policies, custom rules, and compliance validation. For Gateway-wide protection equivalent to this guide, attach a `WAFPolicy` to your `Gateway` — see [Attach WAF to a route](./gateway-waf/attach-waf-to-a-route.mdx). This page will be removed in a future release.
19+
20+
:::
21+
1622
## Prerequisites
1723

1824
* [Calico Ingress Gateway](../networking/ingress-gateway/about-calico-ingress-gateway.mdx) is set up for your cluster.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
description: Attach a WAFPolicy to a Gateway or HTTPRoute to turn on WAF inspection for that target, layering namespace overrides on the cluster baseline.
3+
---
4+
5+
# Attach WAF to a Gateway or route
6+
7+
:::note
8+
9+
This feature is tech preview. Tech preview features may be subject to significant changes before they become GA.
10+
11+
:::
12+
13+
Create a `WAFPolicy` in your namespace pointing at Gateway API targets — WAF inspection doesn't start automatically when the feature is enabled. It inherits the cluster baseline with namespace overrides layered on top.
14+
15+
## Prerequisites
16+
17+
- The cluster operator has created the `GlobalWAFPolicy` baseline. See [Set a cluster-wide baseline for Gateway WAF](./set-a-baseline.mdx).
18+
- You have write access to `WAFPolicy` in your namespace.
19+
- A `Gateway` or `HTTPRoute` in your namespace is already receiving traffic.
20+
21+
## Choose a target
22+
23+
`WAFPolicy.spec.targetRefs[]` accepts two kinds of targets:
24+
25+
| `kind` in `targetRefs` | WAF covers | Use when |
26+
| :--- | :--- | :--- |
27+
| `Gateway` | Every `HTTPRoute` bound to that Gateway | You want uniform WAF across all routes on a Gateway |
28+
| `HTTPRoute` | Just that one route | You need per-route tuning — different paranoia level, different plugins |
29+
30+
Each `targetRefs` entry generates a separate `EnvoyExtensionPolicy` (max 16 entries).
31+
32+
:::note
33+
34+
`sectionName` scopes a `targetRefs` entry to a specific listener on a `Gateway` (or a named section), so WAF applies to just that listener rather than the whole Gateway.
35+
36+
:::
37+
38+
## Attach a WAFPolicy
39+
40+
1. Create a `WAFPolicy` in your namespace pointing at the target. This example attaches to one `HTTPRoute`, overriding action and paranoia; the unset `coreRuleSet.state` still inherits from the baseline:
41+
42+
```yaml
43+
apiVersion: applicationlayer.projectcalico.org/v3
44+
kind: WAFPolicy
45+
metadata:
46+
name: payments-api-waf
47+
namespace: payments
48+
spec:
49+
targetRefs:
50+
- group: gateway.networking.k8s.io
51+
kind: HTTPRoute
52+
name: payments-api
53+
action: Block # override: Block even if global baseline is Detect
54+
coreRuleSet:
55+
paranoiaLevel: 2 # stricter than the cluster default
56+
# coreRuleSet.state is unset → inherited from GlobalWAFPolicy
57+
```
58+
59+
Apply it with `kubectl apply -f wafpolicy.yaml`. For inheritance details, see the [Overview](./overview.mdx#inheritance-namespaces-override-they-dont-restart).
60+
61+
1. Verify the policy was accepted and programmed:
62+
63+
```bash
64+
kubectl get wafpolicy payments-api-waf -n payments
65+
```
66+
67+
```
68+
NAME ACCEPTED PROGRAMMED AGE
69+
payments-api-waf True True 45s
70+
```
71+
72+
`.status.conditions` reports `Accepted`, `Programmed`, and validation findings. Confirm Envoy Gateway received the filter with `kubectl get envoyextensionpolicies -n payments` (one per `targetRefs` entry).
73+
74+
1. Test that the WAF blocks an attack:
75+
76+
```bash
77+
curl -si "https://<gateway-host>/api?id=1+OR+1%3D1"
78+
```
79+
80+
```
81+
HTTP/1.1 403 Forbidden
82+
content-length: 11
83+
content-type: text/plain
84+
85+
Access denied
86+
```
87+
88+
:::tip
89+
90+
In `Detect` mode the request is allowed (200), but the WAF hit is still recorded in the `tigera_secure_ee_waf` log index (carrying `gateway_name`/`gateway_namespace`). On this release, gateway-WAF hits are not raised as Security Events — see [Observing WAF activity](./troubleshooting.mdx#observing-waf-activity).
91+
92+
:::
93+
94+
:::caution
95+
96+
`ACCEPTED=False` reason `ValidationFailed` means a `GlobalWAFValidationPolicy` is blocking your config — the status message names the policy and rule. Contact your cluster operator or see [Validate configuration with Rego](./validate-config.mdx).
97+
98+
:::
99+
100+
## Conflict resolution
101+
102+
Only one `WAFPolicy` may be active per target. If two in the same namespace target the same Gateway/HTTPRoute, $[prodname] follows [GEP-713](https://gateway-api.sigs.k8s.io/geps/gep-713/): oldest `creationTimestamp` wins; the newer is marked `Accepted=False` reason `Conflicted`, and only the winner's directives are merged. Fix by deleting the newer policy or repointing its `targetRefs`.
103+
104+
## Next steps
105+
106+
- [Write custom rules with plugins](./write-custom-rules.mdx) — add app-specific detections beyond the baseline.
107+
- [Validate configuration with Rego](./validate-config.mdx) — check the merged config against compliance guardrails.
108+
- [Troubleshooting](./troubleshooting.mdx) — diagnose a policy that won't accept or program.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
description: Turn Gateway WAF on or off by setting the operator GatewayAPI CR's extensions.waf.state field.
3+
---
4+
5+
# Enable Gateway WAF
6+
7+
:::note
8+
9+
This feature is tech preview. Tech preview features may be subject to significant changes before they become GA.
10+
11+
:::
12+
13+
Gateway WAF is off by default. Turning it on is a one-line change to the operator's `GatewayAPI` CR — no new workload is deployed, so there's no extra resource footprint to plan for.
14+
15+
## Prerequisites
16+
17+
- $[prodname] is installed with a valid license that includes the Gateway WAF feature.
18+
- $[prodname] Ingress Gateway is already set up. See [About Calico Ingress Gateway](../../networking/ingress-gateway/about-calico-ingress-gateway.mdx).
19+
- Cluster-admin rights — the `GatewayAPI` CR is a cluster-scoped operator resource.
20+
21+
:::note
22+
23+
Customer-installed (BYO) Envoy Gateway is not supported. Gateway WAF requires the Ingress Gateway that $[prodname] manages.
24+
25+
:::
26+
27+
## Enable Gateway WAF
28+
29+
1. Edit the operator `GatewayAPI` CR named `default` and set `spec.extensions.waf.state` to `Enabled` (the default is `Disabled`):
30+
31+
```bash
32+
kubectl apply -f - <<'EOF'
33+
apiVersion: operator.tigera.io/v1
34+
kind: GatewayAPI
35+
metadata:
36+
name: default
37+
spec:
38+
extensions:
39+
waf:
40+
state: Enabled
41+
EOF
42+
```
43+
44+
The operator installs the six WAF CRDs and the validating admission webhook, and wires the WAF reconcilers into the existing `calico-kube-controllers` pod in `calico-system`. No new workload is deployed.
45+
46+
1. Confirm the six CRDs are installed:
47+
48+
```bash
49+
kubectl get crd | grep applicationlayer.projectcalico.org
50+
```
51+
52+
```text
53+
globalwafplugins.applicationlayer.projectcalico.org
54+
globalwafpolicies.applicationlayer.projectcalico.org
55+
globalwafvalidationpolicies.applicationlayer.projectcalico.org
56+
wafplugins.applicationlayer.projectcalico.org
57+
wafpolicies.applicationlayer.projectcalico.org
58+
wafvalidationpolicies.applicationlayer.projectcalico.org
59+
```
60+
61+
All six must be present before you create any WAF resources.
62+
63+
1. Confirm the admission webhook is active:
64+
65+
```bash
66+
kubectl get validatingwebhookconfigurations | grep waf
67+
```
68+
69+
You should see one entry. If it's absent, check the `calico-kube-controllers` pod logs in the `calico-system` namespace.
70+
71+
## Disable Gateway WAF
72+
73+
Set `spec.extensions.waf.state` back to `Disabled` on the same `GatewayAPI` CR to turn the feature off:
74+
75+
```yaml
76+
apiVersion: operator.tigera.io/v1
77+
kind: GatewayAPI
78+
metadata:
79+
name: default
80+
spec:
81+
extensions:
82+
waf:
83+
state: Disabled
84+
```
85+
86+
The operator removes the admission webhook and stops the WAF reconcilers; existing `WAFPolicy`, `GlobalWAFPolicy`, and other WAF resources stay on the cluster but are no longer reconciled or enforced until you re-enable the feature.
87+
88+
## Next steps
89+
90+
- [Tutorial](./tutorial.mdx) — deploy a test app and block your first attack.
91+
- [Set a cluster-wide baseline for Gateway WAF](./set-a-baseline.mdx) — start your production rollout.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
description: Run a full multi-tenant Gateway WAF setup — a cluster baseline, org-wide plugin, and validation policy that stops teams weakening protection while each team tunes independently.
3+
---
4+
5+
# Multi-tenant setup
6+
7+
:::note
8+
9+
This feature is tech preview. Tech preview features may be subject to significant changes before they become GA.
10+
11+
:::
12+
13+
Multi-tenant setup ties the baseline, plugins, and validation guardrails together so a shared cluster stays safe by default while each team still controls its own Gateway.
14+
15+
:::note[Scenario]
16+
17+
A cluster shared by `team-a` and `payments`, with a cluster operator setting the baseline via a `GlobalWAFPolicy`, an org-wide `GlobalWAFPlugin`, and a `GlobalWAFValidationPolicy` that stops teams weakening protection. Each team attaches WAF to its own Gateway and tunes independently.
18+
19+
:::
20+
21+
## Prerequisites
22+
23+
- Gateway WAF is enabled and licensed. See [Enable Gateway WAF](./enable.mdx).
24+
- Cluster-admin rights for the cluster-operator steps; each team needs write access to WAF resources in its own namespace.
25+
26+
## Set it up
27+
28+
1. As cluster operator, create the baseline resources: the `default` `GlobalWAFPolicy`, an `org-security-baseline` `GlobalWAFPlugin`, and a `production-requirements` `GlobalWAFValidationPolicy` (`Enforce`, scoped to `env: production`). Follow the patterns in [Set a cluster-wide baseline](./set-a-baseline.mdx), [Write custom rules](./write-custom-rules.mdx), and [Validate configuration with Rego](./validate-config.mdx).
29+
30+
1. Grant application operators read access to the `Global*` resources so they can see what they're inheriting:
31+
32+
```yaml
33+
apiVersion: rbac.authorization.k8s.io/v1
34+
kind: ClusterRole
35+
metadata:
36+
name: waf-global-reader
37+
rules:
38+
- apiGroups: ["applicationlayer.projectcalico.org"]
39+
resources:
40+
- globalwafpolicies
41+
- globalwafplugins
42+
- globalwafvalidationpolicies
43+
verbs: ["get", "list", "watch"]
44+
---
45+
apiVersion: rbac.authorization.k8s.io/v1
46+
kind: RoleBinding
47+
metadata:
48+
name: waf-global-reader
49+
namespace: team-a # repeat for payments
50+
subjects:
51+
- kind: Group
52+
name: team-a-operators
53+
apiGroup: rbac.authorization.k8s.io
54+
roleRef:
55+
kind: ClusterRole
56+
name: waf-global-reader
57+
apiGroup: rbac.authorization.k8s.io
58+
```
59+
60+
:::note
61+
62+
This grants read on cluster-scoped resources via a namespace-scoped `RoleBinding` — application operators can see but not edit `Global*` resources.
63+
64+
:::
65+
66+
1. Each team labels its namespace so the validation policy applies (`kubectl label namespace team-a env=production`, same for `payments`), then creates a `WAFPolicy` targeting its own Gateway. `team-a` raises paranoia to 2, inheriting `coreRuleSet.state: Enabled`; `payments` runs paranoia 3 plus a namespace `WAFPlugin` blocking GraphQL introspection, using an ID from its own [SecRule ID range](./reference.mdx#secrule-id-ranges).
67+
68+
## What happens if a team tries to weaken the baseline
69+
70+
If `payments` applies `action: Detect`, `production-requirements` (`Enforce`) fires its `must-use-block-mode` critical rule — the controller marks the policy `Programmed=False, Reason=NotAttempted` and surfaces the violation:
71+
72+
```bash
73+
kubectl get wafpolicy payments-waf -n payments \
74+
-o jsonpath='{.status.validation.failures}' | jq
75+
```
76+
77+
No `EnvoyExtensionPolicy` is generated until corrected — restore `action: Block` or request an exception from the cluster operator.
78+
79+
## Result: isolation in practice
80+
81+
- **`team-a`** — Core Rule Set at paranoia 2, own custom rules, own ID range.
82+
- **`payments`** — Core Rule Set at paranoia 3, GraphQL introspection block, a different ID range.
83+
- Neither team can disable the org-wide rule from `GlobalWAFPlugin` or drop below Block mode.
84+
- The cluster operator sees all violations centrally on `GlobalWAFValidationPolicy/production-requirements` status — one place to see which namespaces are non-compliant.
85+
86+
## Next steps
87+
88+
- [Reference](./reference.mdx) — CRDs, Rego validation, SecRule ID ranges, and status conditions.
89+
- [Troubleshooting](./troubleshooting.mdx) — diagnose a policy blocked by a guardrail.

0 commit comments

Comments
 (0)