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]>
This commit is contained in:
1 parent
4c3a284cad
commit
b85d1e3ed7
47 files changed
+2572
-6
No files matched your search
@@ -0,0 +1,119 @@
|
||||
---
|
||||
# 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
|
||||
Reference in new issue
Block a user