# Serves: R-SEC-02 (least network reach / least privilege), supports R-NET-01. # Spec: docs/07 "Identity/least privilege ... least network reach (R-SEC-02)"; # docs/03 default-deny. # # WHY: a pod on hostNetwork bypasses the CNI (Cilium) datapath and therefore # ESCAPES the default-deny NetworkPolicy fabric -- it is the most direct way to # defeat R-NET-01. hostPID/hostIPC and privileged containers likewise break # workload isolation. Application workloads never legitimately need these # (platform components that do -- e.g. Cilium, node-exporter -- live in platform # namespaces that do NOT carry the app-namespace label, so they are out of scope). # # Matches Pods; Kyverno pod-controller autogen extends the same checks to # Deployment/StatefulSet/DaemonSet/Job/CronJob pod templates automatically. apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: restrict-host-namespaces annotations: policies.kyverno.io/title: Restrict host namespaces and privilege in app namespaces policies.kyverno.io/category: Least Privilege policies.kyverno.io/severity: high homelab.keithjackson.dev/requirements: R-SEC-02,R-NET-01 policies.kyverno.io/description: >- Application-namespace pods must not use hostNetwork/hostPID/hostIPC (which bypass default-deny) or run privileged containers. spec: validationFailureAction: Enforce background: true rules: - name: disallow-host-namespaces match: any: - resources: kinds: - Pod namespaceSelector: matchLabels: homelab.keithjackson.dev/app-namespace: "true" validate: message: >- Application workloads must not set hostNetwork/hostPID/hostIPC -- host networking bypasses the default-deny NetworkPolicy fabric (R-SEC-02, R-NET-01). deny: conditions: any: - key: "{{ request.object.spec.hostNetwork || `false` }}" operator: Equals value: true - key: "{{ request.object.spec.hostPID || `false` }}" operator: Equals value: true - key: "{{ request.object.spec.hostIPC || `false` }}" operator: Equals value: true - name: disallow-privileged-containers match: any: - resources: kinds: - Pod namespaceSelector: matchLabels: homelab.keithjackson.dev/app-namespace: "true" validate: message: >- Application containers must not run privileged (R-SEC-02). foreach: - list: "request.object.spec.containers" deny: conditions: any: - key: "{{ element.securityContext.privileged || `false` }}" operator: Equals value: true