5.3 KiB
5.3 KiB
date, branch, title
| date | branch | title |
|---|---|---|
| 2026-11-09 | feat/vuln-visibility | Automated CVE visibility for production (R-VULN-01..09, ADR-0027) |
What changed
Implements docs/17-vulnerability-visibility.md from spec PR Home-Network/home-network-k3s#4
(commit 0fb44bb, open, not merged). ci/SPEC_PIN is deliberately not bumped: the merge SHA does
not exist yet, and open PR #160 owns the pin backlog.
platform/vuln-scanner/: stdlib Python scanner in a ConfigMap on a digest-pinnedpython:3.14.9-alpine, Trivy 0.76.1 by digest copied in by an initContainer, a per-app oauth2-proxy sidecar forcve.${BASE_DOMAIN}, read-only ClusterRole, RWO Longhorn PVC for the report and state, size-limited emptyDir for the DB and scan cache, pinned Service ClusterIP10.43.210.10as the bot's DNS fallback, five alerts with promtool tests and a runbook.clusters/base/vuln-scanner.yaml: its own Flux tier,wait: false,dependsOn: platform-config, nothing depends on it..gitea/workflows/cve-triage.yamlandscripts/cve_triage.py: the bot, cron15 7 * * *, counts-onlyCVE Dashboardissue with its owncve-dashboardlabel, approve-row ticks only.renovate.json5: Trivy on the blast-radius list, 7-day minimum release age, and everything underplatform/vuln-scanner/**non-automerging.conformance/: R-VULN-01..09 in the registry; L0 checks and L1 fixture tests (scanner, page, bot) wired by scope, L2/L3 authored as scaffolds.- PiHole LAN names and the Homepage link gained the CVE page.
Decisions a future reader will want
- Page is server-rendered with no JavaScript, not inline JS: the CSP has no
script-src, escaping is in one place, and the L1 test exercises the real rendered page, which a JS page could not do without a browser engine. Filters, sort and pagination are URL parameters. - Three pod ports, not two. Prometheus gets 9090 only, so it cannot read
report.json. - A killed image (OOM, eviction) is recorded as failed from an in-flight marker and sits out full runs for 20h, so it cannot wedge the scan loop.
- Private registries are named in
VULN_PRIVATE_REGISTRIESand short-circuit tocredential-requiredbefore Trivy runs (see escalation 1). - Bot's job container is the repo's toolbox image by digest:
actions/checkoutneeds node and the pull is already proven by the merge gate.
Spec escalations
- Coverage state
credential-requiredis defined as "the registry refused anonymous access", but the egress contract excludes LAN ranges, so the scanner cannot reach the private registry to be refused. Implemented by naming private registries in configuration. The spec should define the state by configuration or admit the registry to egress. - The egress contract lists only the Kubernetes API, DNS and public TCP/443, but the SSO sidecar must reach Keycloak. Egress additionally allows the Gateway on 443 and Keycloak. The spec should name this path.
What I verified
- Toolbox gate on the integrated tree: rc 3 (UNDETERMINED, not green). Stages that did not run:
spec-immutabilityandgitea-settings;conformance(L2/L3) logged SKIPPED, no cluster.approval-policy,decisions-log,lint,l0,render-golden,l1reported green.approval-policyis N/A offline and proves nothing locally. - All
vuln-*L0 and L1 checks pass in the toolbox, each with a mutated-fixture non-vacuity case. renovate-config-validatorfromghcr.io/renovatebot/renovate:44.167.0, the tagrenovate.yamlruns: validated successfully. This is a read-only container run of a public image.- Trivy 0.76.1 digest: registry
created2026-10-21, 19 days old at authoring; cosign keyless verification passed (cosign 3.1.3);cpand the CA bundle confirmed from a layer listing. - ClusterIP
10.43.210.10confirmed unused against the live Service list, read-only. - An independent reviewer checked the diff against docs/17 and found no anonymous leak path, no way
for an unscanned image to read as clean, no way for the bot to tick a
rebase-branchrow, and no platform stall path. One BLOCKER and five other code findings were fixed with tests.
What I did NOT verify
Nothing here has been deployed. No runtime behaviour has been observed.
- R-VULN-04 is NOT delivered until the operator adds the Keycloak client. It needs a new key in
platform/keycloak/keycloak-realm-clients.sops.yaml, which needs the age private key, and a one-timekcadmagainst the live realm. Exact steps:platform/vuln-scanner/README.md, "Operator action required". Until then login fails closed withinvalid_client. Those steps are UNVERIFIED. - A real Trivy run: every scanner test uses a recorded fixture and a fake runner.
- The real Kubernetes API, real browser rendering, OIDC login and the
platform-adminsrestriction. - Whether in-cluster DNS resolves from a DinD job container, and whether the pinned ClusterIP fallback is reachable from one. The workflow has never run; Gitea behaviour is tested against a fake only.
- The
## Pending Approvalrow format is from Renovate's source as recalled, because issue #91 has no live approve row today. Theapprove-branchmarker itself is documented. - L2 and L3 are scaffolds and do not run in CI.
- Renovate
minimumReleaseAgebehaviour on a real update, as opposed to the config validating. - Cgroup v2 memory behaviour under the scanner's limits is inferred, not measured.