# Default-deny plus the narrowest set of exceptions (AUDIT-WP-0005-T03). # # The receiver holds the audit trail, so reachability is part of its threat # model: only the declared sender may write, and only the declared operator # path may read. --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-default-deny namespace: audit-core spec: podSelector: {} policyTypes: [Ingress, Egress] # No rules: everything not permitted below is denied. --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-sender-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # user-engine is the only sender. A second sender is a deliberate change # here and a matching entry in AUDIT_CORE_SENDERS — the network rule and # the credential binding must move together. - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: user-engine ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-tenant-engine-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # AUDIT-WP-0010-T03 / AUDIT-IN-0002. Attributive mutation evidence. # Both selectors belong to one peer and are therefore ANDed. Attributive # rather than load-bearing changes what may be claimed of the stream, not # how narrow its reachability should be — a weaker evidence class is not a # reason for a wider network rule. - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: tenant-engine podSelector: matchLabels: app.kubernetes.io/name: tenant-engine ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-whitehat-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # Governed E2 evidence plane. Both selectors belong to one peer and are # therefore ANDed: only the registered audit-core probe in the dedicated # whitehat namespace reaches this port. Application sender authentication # and tenant scope remain the inner boundary. - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: whitehat podSelector: matchLabels: whitehat.security/plane: "true" whitehat.security/target: audit-core ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-approval-engine-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # AUDIT-WP-0009-T09 / AUDIT-IN-0001. Load-bearing approval evidence # (§9.4). Both selectors belong to one peer and are therefore ANDed: # only the approval-engine workload in its own namespace reaches this # port. Splitting them into two list items would turn AND into OR and # admit every pod in either set. # # Narrower than user-engine's namespace-only rule on purpose: this is a # new sender, and a new rule should not inherit an older rule's breadth. # user-engine's policy is deliberately left unchanged. - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: approval-engine podSelector: matchLabels: app.kubernetes.io/name: approval-engine ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-informed-decision-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # AUDIT-WP-0009-T11 / AUDIT-IN-0003. Load-bearing presentation evidence # under GH-DEC-2026-012 limit 3 and GH-DEC-2026-014. Both selectors belong # to one peer and are therefore ANDed, following the approval-engine rule # rather than user-engine's older namespace-only breadth. # # Note what this rule does NOT create: no egress from audit-core to # informed-decision. The commitment-only record's custody declaration is # carried, never dereferenced from here — audit-core makes no retrieval # call, and the egress policy below is the proof. - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: informed-decision podSelector: matchLabels: app.kubernetes.io/name: informed-decision ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-operator-ingress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core policyTypes: [Ingress] ingress: # Operator read path: lookup, dead letters, secret findings, stats. # Namespace-scoped rather than open, and still gated on a credential # carrying may_read — the network rule is the outer of two checks, not the # only one. - from: - namespaceSelector: matchLabels: railiance.io/audit-core-reader: "true" ports: - {protocol: TCP, port: 8080} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-attest-egress namespace: audit-core spec: # Scoped to the attestation job by component label, so the receiver itself # gains nothing from this rule. The receiver must not be able to reach the # API server: a compromised receiver that could rewrite the chain-head # ConfigMap could forge its own attestation, which is the one thing the # separation of these two workloads exists to prevent. podSelector: matchLabels: app.kubernetes.io/name: audit-core app.kubernetes.io/component: attest policyTypes: [Egress] egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: databases ports: - {protocol: TCP, port: 5432} - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - {protocol: UDP, port: 53} - {protocol: TCP, port: 53} # kube-apiserver. On this single-node k3s cluster the API server is the # host itself, so this is a host-network destination rather than a pod # selector; narrow it to the API port. - ports: - {protocol: TCP, port: 6443} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: audit-core-egress namespace: audit-core spec: podSelector: matchLabels: app.kubernetes.io/name: audit-core app.kubernetes.io/component: receiver policyTypes: [Egress] egress: # PostgreSQL custody store. This is the only destination the receiver needs; # it calls no other service. - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: databases ports: - {protocol: TCP, port: 5432} - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - {protocol: UDP, port: 53} - {protocol: TCP, port: 53}