Skip to main content
Calico Cloud documentation

Multi-tenant setup

note

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.

Scenario

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​

  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, Write custom rules, and Validate configuration with Rego.

  2. Grant application operators read access to the Global* resources so they can see what they're inheriting:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: waf-global-reader
    rules:
    - apiGroups: ["applicationlayer.projectcalico.org"]
    resources:
    - globalwafpolicies
    - globalwafplugins
    - globalwafvalidationpolicies
    verbs: ["get", "list", "watch"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: waf-global-reader
    namespace: team-a # repeat for payments
    subjects:
    - kind: Group
    name: team-a-operators
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: waf-global-reader
    apiGroup: rbac.authorization.k8s.io
    note

    This grants read on cluster-scoped resources via a namespace-scoped RoleBinding — application operators can see but not edit Global* resources.

  3. 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.

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 GlobalWAFPlugin or drop below Block mode.
  • The cluster operator sees all violations centrally on GlobalWAFValidationPolicy/production-requirements status — 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.