Was a scaffold (fixture TODO, fault-injection a no-op echo). Implemented against the live datapath: fixtures/allowed-pair.yaml = default-deny baseline + source + dest(:8080) + the sole explicit allow. The test verifies source->dest works (ALLOWED_OK), then REVOKES both halves of the allow (real fault: kubectl delete networkpolicy), and asserts connectivity fails CLOSED (FAIL_CLOSED_OK) while DNS still resolves (DNS_OK). Proves default-deny is datapath-enforced and fails closed on revocation. DNS check asserts the resolved answer, since busybox nslookup exits non-zero on a successful forward lookup. PASSES (74.8s). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
109 lines
3.0 KiB
YAML
109 lines
3.0 KiB
YAML
# =============================================================================
|
|
# Fixtures for the default-deny FAIL-CLOSED fault-injection test (R-NET-01/02).
|
|
# Applied into the per-test namespace chainsaw creates (hermetic, self-cleaned).
|
|
#
|
|
# Models: a namespace default-deny baseline + ONE explicit allow that is the SOLE
|
|
# reason source can reach dest:8080. The test later DELETES the allow (fault) and
|
|
# proves connectivity drops to fail-closed while DNS still resolves.
|
|
#
|
|
# Standard Kubernetes NetworkPolicy (Cilium enforces it). No readinessProbe on the
|
|
# pods: under default-deny the kubelet TCP probe (node->pod) is dropped, so a
|
|
# probe-bearing pod would never go Ready — without a probe the container is Ready
|
|
# once running, which is all the test needs (K2 finding).
|
|
# =============================================================================
|
|
---
|
|
# --- Default-deny baseline: deny all ingress+egress, allow only DNS egress. -----------
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: default-deny-baseline
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes: [Ingress, Egress]
|
|
ingress: []
|
|
egress:
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
podSelector:
|
|
matchLabels:
|
|
k8s-app: kube-dns
|
|
ports:
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 53
|
|
---
|
|
# --- The ONE explicit allow (source -> dest tcp/8080): the two matching halves a
|
|
# real rendered dependency produces (egress permit on source, ingress on dest).
|
|
# Deleting BOTH is the fault injection.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-source-to-dest-egress
|
|
spec:
|
|
podSelector:
|
|
matchLabels: { app: source }
|
|
policyTypes: [Egress]
|
|
egress:
|
|
- to:
|
|
- podSelector:
|
|
matchLabels: { app: dest }
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-source-to-dest-ingress
|
|
spec:
|
|
podSelector:
|
|
matchLabels: { app: dest }
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels: { app: source }
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
---
|
|
# --- source: an idle probe pod (nc is issued via kubectl exec). ------------------------
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: source
|
|
labels: { app: source }
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
image: busybox:1.36.1
|
|
command: ["sh", "-c", "sleep 3600"]
|
|
---
|
|
# --- dest: LISTENS on 8080 so a block is caused by POLICY, not a missing listener. -----
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: dest
|
|
labels: { app: dest }
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
image: busybox:1.36.1
|
|
command: ["httpd", "-f", "-p", "8080", "-h", "/tmp"]
|
|
ports:
|
|
- containerPort: 8080
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: dest
|
|
spec:
|
|
selector: { app: dest }
|
|
ports:
|
|
- name: p8080
|
|
port: 8080
|
|
targetPort: 8080
|