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]>
120 lines
4.4 KiB
YAML
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
|