diff --git a/test/e2e/trafficprotectionpolicy-enforce/chainsaw-test.yaml b/test/e2e/trafficprotectionpolicy-enforce/chainsaw-test.yaml new file mode 100644 index 00000000..5d684ff7 --- /dev/null +++ b/test/e2e/trafficprotectionpolicy-enforce/chainsaw-test.yaml @@ -0,0 +1,164 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: trafficprotectionpolicy-enforce-serves-traffic +# Regression for issue #243: a TrafficProtectionPolicy in Enforce mode must not +# take the gateway offline. The branded error page NSO injects into Envoy's +# local_reply_config is a SubstitutionFormatString, so an unescaped '%' in the +# page (e.g. CSS "height: 100%") makes Envoy reject the whole listener and, with +# failOpen:false, drops the xDS update fleet-wide — every request then fails +# with an empty reply instead of the backend's 200. +# +# Precondition: the downstream (nso-infra) must have the WAF data plane wired up +# (extension-server + the extensionManager Envoy Gateway registered on the +# `datum-downstream-gateway` GatewayClass, with the Coraza filter). The stock +# `make prepare-infra-cluster` does NOT install this; this test is skipped by +# CI environments without it (the Gateway never reaches Programmed=True). +spec: + cluster: nso-infra + # EG only reconciles namespaces carrying this label. + namespaceTemplate: + metadata: + labels: + meta.datumapis.com/upstream-cluster-name: e2e + steps: + - name: Deploy a backend + try: + - apply: + resource: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: echo + spec: + replicas: 1 + selector: + matchLabels: + app: echo + template: + metadata: + labels: + app: echo + spec: + containers: + - name: echo + image: hashicorp/http-echo:1.0 + args: ["-text=hello from backend", "-listen=:8080"] + ports: + - containerPort: 8080 + - apply: + resource: + apiVersion: v1 + kind: Service + metadata: + name: echo + spec: + selector: + app: echo + ports: + - port: 80 + targetPort: 8080 + - assert: + resource: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: echo + status: + availableReplicas: 1 + + - name: Route through the WAF gateway with an Enforce policy + bindings: + - name: hostname + value: (join('.', [$namespace, 'e2e.test'])) + try: + - apply: + resource: + apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: waf-gw + spec: + gatewayClassName: datum-downstream-gateway + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: ($hostname) + allowedRoutes: + namespaces: + from: Same + - apply: + resource: + apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: echo + spec: + parentRefs: + - name: waf-gw + hostnames: + - ($hostname) + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: echo + port: 80 + - apply: + resource: + apiVersion: networking.datumapis.com/v1alpha + kind: TrafficProtectionPolicy + metadata: + name: enforce-waf + spec: + mode: Enforce + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: waf-gw + ruleSets: + - type: OWASPCoreRuleSet + owaspCoreRuleSet: {} + - assert: + resource: + apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: waf-gw + status: + (conditions[?type == 'Programmed']): + - status: "True" + + - name: Benign traffic must reach the backend (not an empty/5xx reply) + description: > + Probe the downstream Envoy via the kind hostPort (30080). Before the fix + the listener is rejected and curl returns 000 / empty reply; after the + fix a benign GET returns the backend's 200. Gateway.status.Programmed is + not sufficient on its own — EG reports Programmed from its own + translation, independent of Envoy's xDS ACK — so this asserts on the + wire. + bindings: + - name: hostname + value: (join('.', [$namespace, 'e2e.test'])) + try: + - script: + env: + - name: HOSTNAME + value: ($hostname) + content: | + set -u + for i in $(seq 1 30); do + code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 \ + -H "Host: ${HOSTNAME}" http://localhost:30080/) || code=000 + echo "attempt ${i}: HTTP ${code}" + if [ "${code}" = "200" ]; then + exit 0 + fi + sleep 3 + done + echo "benign request never returned 200 (issue #243 regression)" + exit 1