Multi-tenant setup
This feature is tech preview. Tech preview features may be subject to significant changes before they become GA.
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.
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.
Prerequisites
- Gateway WAF is enabled. See Enable Gateway WAF.
- Cluster-admin rights for the cluster-operator steps; each team needs write access to WAF resources in its own namespace.
Set it up
-
As cluster operator, create the baseline resources: the
defaultGlobalWAFPolicy, anorg-security-baselineGlobalWAFPlugin, and aproduction-requirementsGlobalWAFValidationPolicy(Enforce, scoped toenv: production). Follow the patterns in Set a cluster-wide baseline, Write custom rules, and Validate configuration with Rego. -
Grant application operators read access to the
Global*resources so they can see what they're inheriting:apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: waf-global-readerrules:- apiGroups: ["applicationlayer.projectcalico.org"]resources:- globalwafpolicies- globalwafplugins- globalwafvalidationpoliciesverbs: ["get", "list", "watch"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: waf-global-readernamespace: team-a # repeat for paymentssubjects:- kind: Groupname: team-a-operatorsapiGroup: rbac.authorization.k8s.ioroleRef:kind: ClusterRolename: waf-global-readerapiGroup: rbac.authorization.k8s.ionoteThis grants read on cluster-scoped resources via a namespace-scoped
RoleBinding— application operators can see but not editGlobal*resources. -
Each team labels its namespace so the validation policy applies (
kubectl label namespace team-a env=production, same forpayments), then creates aWAFPolicytargeting its own Gateway.team-araises paranoia to 2, inheritingcoreRuleSet.state: Enabled;paymentsruns paranoia 3 plus a namespaceWAFPluginblocking GraphQL introspection, using an ID from its own SecRule ID range.
What happens if a team tries to weaken the baseline
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:
kubectl get wafpolicy payments-waf -n payments \
-o jsonpath='{.status.validation.failures}' | jq
No EnvoyExtensionPolicy is generated until corrected — restore action: Block or request an exception from the cluster operator.
Result: isolation in practice
team-a— Core Rule Set at paranoia 2, own custom rules, own ID range.payments— Core Rule Set at paranoia 3, GraphQL introspection block, a different ID range.- Neither team can disable the org-wide rule from
GlobalWAFPluginor drop below Block mode. - The cluster operator sees all violations centrally on
GlobalWAFValidationPolicy/production-requirementsstatus — one place to see which namespaces are non-compliant.
Next steps
- Reference — CRDs, Rego validation, SecRule ID ranges, and status conditions.
- Troubleshooting — diagnose a policy blocked by a guardrail.