# ============================================================================= # L2 BEHAVIORAL SCAFFOLD — Default-deny east-west (R-NET-01) # Catalog: docs/14-testing-and-conformance.md "Default-deny (R-NET-01)" # Spec: docs/03-networking-isolation-and-egress.md "Default-deny baseline" # # EXEMPLAR: this file is the reference for the tagging convention and step # style. Other scaffolds follow the same shape (see tests/README.md). # # GIVEN a fresh workload in a namespace with no allow rules, # WHEN it attempts any in-cluster connection except DNS, # THEN the connection FAILS (fail-closed); DNS still succeeds. # # This asserts observable BEHAVIOR (a probe cannot connect), not the mere # existence of a NetworkPolicy object (R-TEST-01). Safety-relevant: the # fail-closed direction is also exercised under fault-injection at L3 # (kill-switch / drift), but the baseline deny is a behavioral must-pass. # # STATUS: scaffold — cannot run until Phase 2 delivers the default-deny # baseline (T-2.2). Chainsaw auto-creates/cleans the namespace (hermetic). # TODOs mark fixtures that land as later phases deliver. # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: default-deny labels: # --- requirement tags (authoritative; one label per verified R-* ID) --- conformance.home.k3s/R-NET-01: "true" annotations: conformance.home.k3s/requirements: "R-NET-01" conformance.home.k3s/layer: "L2" conformance.home.k3s/safety-critical: "false" conformance.home.k3s/intent: >- Given a bare workload with no allow rules, only DNS egress succeeds; every other in-cluster connection is denied. spec: steps: # -- GIVEN: a fresh, unprivileged probe workload with zero allow rules. The # per-test namespace inherits the platform default-deny baseline (T-2.2). - name: given-bare-probe-workload try: - apply: # TODO(T-2.2): once the namespaced default-deny CiliumNetworkPolicy # baseline exists, assert it is present AND that the probe below is # actually blocked (existence != enforcement, R-TEST-01). resource: apiVersion: v1 kind: Pod metadata: name: probe labels: { app: conformance-probe } spec: restartPolicy: Never containers: - name: probe image: ghcr.io/kyverno/kyvernopre:latest # TODO: pin a curl/nc probe image command: ["sleep", "3600"] - assert: resource: apiVersion: v1 kind: Pod metadata: { name: probe } status: { phase: Running } # -- THEN(DNS allowed): DNS egress MUST succeed even under default-deny. - name: then-dns-succeeds try: - script: # TODO(T-2.2): resolve an in-cluster name via kube-dns; expect rc=0. content: | set -euo pipefail kubectl -n "$NAMESPACE" exec probe -- \ sh -c 'nslookup kubernetes.default.svc.cluster.local' && echo DNS_OK check: # bounded, deterministic: no sleeps; chainsaw retries within timeout. ($stdout =~ 'DNS_OK'): true # -- THEN(everything else denied): a non-DNS in-cluster connection MUST fail. - name: then-nondns-denied try: - script: # TODO(T-2.2 / T-2.6): target an in-cluster service (e.g. Plex/Gateway) # on a real port; expect the connection to FAIL closed (non-zero / timeout). content: | set -euo pipefail if kubectl -n "$NAMESPACE" exec probe -- \ sh -c 'nc -z -w 3 80'; then echo "LEAK: connection succeeded under default-deny" >&2 exit 1 fi echo DENIED_OK check: ($stdout =~ 'DENIED_OK'): true # -- finally: chainsaw deletes the namespace + probe automatically; this block # is the explicit hook for any out-of-namespace fixtures (none here). finally: - script: content: 'echo "cleanup handled by chainsaw namespace teardown"'