# ============================================================================= # L2 BEHAVIORAL — Explicit allow / deny-undeclared (R-NET-02) [RUNNABLE] # Catalog: docs/14 "Explicit allow / deny-undeclared (R-NET-02)" # Spec: docs/03-networking-isolation-and-egress.md "Explicit allow rules" # # GIVEN a namespace with default-deny + ONE explicit allow (source -> declared-dest:8080), # WHEN the source reaches the DECLARED dest port -> THEN it succeeds; # WHEN the source reaches an UNDECLARED port (9090) -> THEN it fails; # WHEN the source reaches an UNDECLARED dest (other) -> THEN it fails. # # The destinations LISTEN on every probed port, so a block is caused by POLICY, # not by a missing listener — this asserts BEHAVIOR, not object existence (R-TEST-01). # # RUNNABLE ON THE BASE CLUSTER: the fixture supplies the default-deny baseline and # the single allow (fixtures/source-and-dests.yaml); Cilium enforces the standard # NetworkPolicies. Probes are ephemeral Jobs labeled app=source so the allow/deny # rules apply to them; chainsaw namespace teardown cleans everything. # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: deny-undeclared labels: conformance.home.k3s/R-NET-02: "true" annotations: conformance.home.k3s/requirements: "R-NET-02" conformance.home.k3s/layer: "L2" conformance.home.k3s/safety-critical: "false" conformance.home.k3s/executable: "true" conformance.home.k3s/intent: >- A declared dependency (source->declared-dest:8080) connects; an undeclared port and an undeclared destination are both blocked by policy. spec: steps: # -- GIVEN: baseline + one explicit allow + two live destinations. Wait until # both destinations are Ready so a later block is unambiguously policy-caused. - name: given-source-with-declared-dependency try: - apply: file: fixtures/source-and-dests.yaml - assert: resource: apiVersion: v1 kind: Pod metadata: { name: declared-dest } status: # scalar match — chainsaw does strict (length-equal) list matching, so asserting a # single condition against the pod's full conditions[] list fails. Running is enough: # the listener is up once the container runs (no readinessProbe under default-deny). phase: Running - assert: resource: apiVersion: v1 kind: Pod metadata: { name: other-dest } status: # scalar match — chainsaw does strict (length-equal) list matching, so asserting a # single condition against the pod's full conditions[] list fails. Running is enough: # the listener is up once the container runs (no readinessProbe under default-deny). phase: Running # -- THEN(declared succeeds): source -> declared-dest:8080 connects. Green Job = allowed. - name: then-declared-succeeds try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: probe-allowed spec: backoffLimit: 4 activeDeadlineSeconds: 120 template: metadata: labels: { app: source } spec: restartPolicy: Never containers: - name: probe image: busybox:1.36.1 command: - sh - -c - | set -eu nc -w 5 declared-dest 8080 declared-dest:9090 is blocked. # The Job EXITS 0 only when the connection is BLOCKED (green Job = deny). - name: then-undeclared-port-denied try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: probe-undeclared-port spec: backoffLimit: 4 activeDeadlineSeconds: 120 template: metadata: labels: { app: source } spec: restartPolicy: Never containers: - name: probe image: busybox:1.36.1 command: - sh - -c - | set -u if nc -w 5 declared-dest 9090 /dev/null; then echo "LEAK: undeclared port 9090 reachable" 1>&2 exit 1 fi echo DENY_PORT_OK - assert: resource: apiVersion: batch/v1 kind: Job metadata: { name: probe-undeclared-port } status: { succeeded: 1 } # -- THEN(undeclared DEST denied): source -> other-dest:8080 is blocked. - name: then-undeclared-dest-denied try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: probe-undeclared-dest spec: backoffLimit: 4 activeDeadlineSeconds: 120 template: metadata: labels: { app: source } spec: restartPolicy: Never containers: - name: probe image: busybox:1.36.1 command: - sh - -c - | set -u if nc -w 5 other-dest 8080 /dev/null; then echo "LEAK: undeclared destination reachable" 1>&2 exit 1 fi echo DENY_DEST_OK - assert: resource: apiVersion: batch/v1 kind: Job metadata: { name: probe-undeclared-dest } status: { succeeded: 1 } finally: - script: content: 'echo "cleanup handled by chainsaw namespace teardown"'