Tutorial
This feature is tech preview. Tech preview features may be subject to significant changes before they become GA.
This tutorial takes you from a running Ingress Gateway to a blocked attack in about five minutes.
Prerequisites
- Gateway WAF is enabled. See Enable Gateway WAF.
- Calico Ingress Gateway is set up. See About Calico Ingress Gateway.
- A namespace where you can deploy a test app — this tutorial creates one called
httpbin.
Block an attack in five minutes
-
Deploy a test application. Create an
httpbinnamespace with a deployment and service to act as the backend:apiVersion: v1kind: Namespacemetadata:name: httpbin---apiVersion: apps/v1kind: Deploymentmetadata:name: httpbinnamespace: httpbinspec:replicas: 1selector:matchLabels:app: httpbintemplate:metadata:labels:app: httpbinspec:containers:- name: httpbinimage: kong/httpbin:0.1.0ports:- containerPort: 80---apiVersion: v1kind: Servicemetadata:name: httpbinnamespace: httpbinspec:selector:app: httpbinports:- protocol: TCPport: 80targetPort: 80Apply it, then wait for the pod to be ready:
kubectl wait --for=condition=Ready pod -l app=httpbin -n httpbin --timeout=60s -
Create a Gateway and HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:name: httpbin-gatewaynamespace: httpbinspec:gatewayClassName: tigera-gateway-classlisteners:- name: httpprotocol: HTTPport: 9080---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata:name: httpbin-routenamespace: httpbinspec:parentRefs:- name: httpbin-gatewayrules:- matches:- path:type: PathPrefixvalue: /backendRefs:- name: httpbinport: 80noteUse port 9080, not 8080. Port 8080 is reserved by the Calico L7 log collector inside the Envoy proxy pod.
Apply it, then wait for the Gateway to be accepted:
kubectl wait --for=condition=Accepted gateway/httpbin-gateway -n httpbin --timeout=60sAt this point
Acceptedonly means Gateway passed validation. Let's now wait for the Envoy proxy Deployment that Ingress Gateway creates:kubectl wait --for=condition=Available deployment \-l gateway.envoyproxy.io/owning-gateway-name=httpbin-gateway \-n httpbin --timeout=180s -
Confirm traffic flows. Find the Envoy service the Ingress Gateway created for this Gateway (the service lands in the Gateway's own namespace,
httpbin):export GATEWAY_SVC=$(kubectl get svc -n httpbin \-o jsonpath='{.items[?(@.metadata.labels.gateway\.envoyproxy\.io/owning-gateway-name=="httpbin-gateway")].metadata.name}')Send a test request from inside the cluster:
kubectl run curl-test -n httpbin --rm -it --restart=Never --image=curlimages/curl -- \curl -s -o /dev/null -w "%{http_code}" \http://$GATEWAY_SVC.httpbin.svc.cluster.local:9080/get200Traffic is flowing through the Gateway with no WAF protection yet.
-
Apply a
GlobalWAFPolicyto set the cluster-wide baseline. Create the singletondefaultpolicy with the OWASP Core Rule Set enabled inBlockmode:tipThis tutorial uses
Blockmode immediately so you can see blocking in action. Most production rollouts start inDetectmode to baseline traffic before switching. See Merge order and enforcement for the tradeoffs.apiVersion: applicationlayer.projectcalico.org/v3kind: GlobalWAFPolicymetadata:name: defaultspec:defaultAction: BlockcoreRuleSet:state: EnabledparanoiaLevel: 1This defines the global posture. It does not attach WAF to any traffic yet — that requires a namespace-scoped
WAFPolicywithtargetRefs. -
Attach WAF to the Gateway. Create a
WAFPolicyin thehttpbinnamespace targeting the Gateway:apiVersion: applicationlayer.projectcalico.org/v3kind: WAFPolicymetadata:name: httpbin-wafnamespace: httpbinspec:targetRefs:- group: gateway.networking.k8s.iokind: Gatewayname: httpbin-gatewayThe reconciler merges the
GlobalWAFPolicybaseline with thisWAFPolicy, generates an ordered ModSecurity ruleset, and programs it into Envoy Gateway as anEnvoyExtensionPolicy. Check that the policy was accepted and programmed:kubectl get wafpolicy httpbin-waf -n httpbin \-o jsonpath='{range .status.conditions[*]}{.type}={.status} ({.reason}){"\n"}{end}'Licensed=True (LicenseValid)Accepted=True (Accepted)Validated=Unknown (NotEvaluated)Programmed=True (ConfigurationApplied)Ready=True (Ready)All four executable conditions (
Licensed,Accepted,Programmed,Ready) must beTruebefore WAF is active.Validated=Unknown (NotEvaluated)is expected when noWAFValidationPolicyis in scope. IfProgrammed=False, check the condition message — a common cause is the WASM image not being reachable (see Troubleshooting). Allow a few seconds for Envoy to load the WASM filter before running the tests below. -
Test WAF blocking. A normal request still passes, but a SQL injection probe and an XSS probe are both blocked:
# Normal request — expect 200kubectl run curl-ok -n httpbin --rm -it --restart=Never --image=curlimages/curl -- \curl -s -o /dev/null -w "%{http_code}" \http://$GATEWAY_SVC.httpbin.svc.cluster.local:9080/get# SQL injection probe — expect 403kubectl run curl-sqli -n httpbin --rm -it --restart=Never --image=curlimages/curl -- \curl -s -o /dev/null -w "%{http_code}" \"http://$GATEWAY_SVC.httpbin.svc.cluster.local:9080/get?id=1%27%20OR%20%271%27%3D%271"# XSS probe — expect 403kubectl run curl-xss -n httpbin --rm -it --restart=Never --image=curlimages/curl -- \curl -s -o /dev/null -w "%{http_code}" \"http://$GATEWAY_SVC.httpbin.svc.cluster.local:9080/get?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E"The Coraza WASM filter, running inside Envoy, evaluated each request against the OWASP Core Rule Set. Core Rule Set rule group 942xxx detects SQL injection; group 941xxx detects cross-site scripting. Both attacks are blocked before the request reaches the application.
Clean up
kubectl delete namespace httpbin
kubectl delete globalwafpolicies default
Troubleshoot
If a WAFPolicy you attach to a route later shows Programmed=False with reason WASMUnavailable, the Coraza WASM image could not be pulled. Private registries require a pull secret to be configured on the GatewayAPI CR. See Troubleshooting for diagnosis steps and the pull-secret configuration.
Next steps
- Set a cluster-wide baseline for Gateway WAF — start production configuration in
Detectmode. - Reference — CRDs, Rego validation, SecRule ID ranges, and status conditions.