Files
home-network-k3s-impl/platform/vuln-scanner/networkpolicy.yaml
T
jakman85andClaude Opus 5.5 b85d1e3ed7 feat(vuln): vuln-scanner platform manifests, Flux tier, alerts, L0/L2/L3 tests
Hand-authored platform/vuln-scanner (non-gating Flux tier, digest-pinned Trivy 0.76.1 and
python 3.14.9-alpine, read-only RBAC, per-pod oauth2-proxy, internal-only cve.<domain>),
five alerts with promtool tests, R-VULN-01..09 registry entries, and vuln-* L0 checks with
mutation self-tests. src/ and the bot workflow are integrated separately.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 09:42:52 -06:00

120 lines
4.4 KiB
YAML

---
# Default-deny baseline (R-NET-01): deny all ingress and egress, allow only DNS. A platform namespace,
# so Kyverno does not generate this and Flux is its only writer (namespace.yaml).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: vuln-scanner
labels:
homelab.keithjackson.dev/baseline: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
# INGRESS — exactly three paths (docs/17 "Security of the scanner itself"), one client each:
# :4180 from the shared Gateway only (the oauth2-proxy sidecar). The page itself (:8080, loopback)
# has no Service port and no rule.
# :8081 from the CI runner only. Job containers run inside the DinD sidecar of the act-runner pod in
# namespace gitea-actions, so what the network sees is THAT pod's identity (verified live
# 2026-11-09: pods carry app.kubernetes.io/name=act-runner). No wider than the runner pods.
# :9090 from Prometheus only (verified live: app.kubernetes.io/name=prometheus in monitoring).
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: vuln-scanner-allow-ingress
namespace: vuln-scanner
labels:
app.kubernetes.io/name: vuln-scanner
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: vuln-scanner
ingress:
- fromEntities:
- ingress
toPorts:
- ports:
- { port: "4180", protocol: TCP }
- fromEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: gitea-actions
app.kubernetes.io/name: act-runner
toPorts:
- ports:
- { port: "8081", protocol: TCP }
- fromEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: monitoring
app.kubernetes.io/name: prometheus
toPorts:
- ports:
- { port: "9090", protocol: TCP }
---
# EGRESS (DNS is in the baseline above).
# * kube-apiserver: the inventory.
# * tcp/443 to the public internet for the vulnerability database and registries, OUT THE DIRECT
# PATH (egress-class=internet, deployment.yaml; never the VPN gateway). Written as a CIDR rule
# rather than `toEntities: world` because the spec excludes cluster and LAN ranges and `world`
# includes every LAN address: RFC1918 + CGNAT + loopback + link-local + multicast are subtracted.
# Consequence, recorded rather than worked around: the Gitea registry resolves to the LAN VIP, so
# images hosted there are unreachable from here and report `failed` with that reason, until a
# registry credential and a narrower LAN rule are decided (docs/17, "Private registry coverage").
# * The two Keycloak rules below are the ONE addition beyond the spec's list, and they are required:
# the oauth2-proxy sidecar shares this pod's network identity with the scanner, and it must
# reach Keycloak for OIDC discovery and the token exchange. keycloak.<domain> resolves in-cluster
# to the Gateway VIP (split-horizon CoreDNS), which Cilium hands to the Gateway's Envoy
# (reserved:ingress, tcp/443), and Envoy then re-evaluates THIS pod's egress against Keycloak's
# real backend identity (enforce_policy_on_l7lb), the same pair catalog/render.py gives every SSO
# app (buildkit/networkpolicy.yaml has the measurement). Without them login 403s.
# * NOT listed: anything else in-cluster, any node entity, the LAN, the VPN.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: vuln-scanner-allow-egress
namespace: vuln-scanner
labels:
app.kubernetes.io/name: vuln-scanner
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: vuln-scanner
egress:
- toEntities:
- kube-apiserver
- toCIDRSet:
- cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 100.64.0.0/10
- 127.0.0.0/8
- 169.254.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- 224.0.0.0/4
toPorts:
- ports:
- { port: "443", protocol: TCP }
- toEntities:
- ingress
toPorts:
- ports:
- { port: "443", protocol: TCP }
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: keycloak
app: keycloak