docs: specify automated vulnerability visibility for production (R-VULN-*, ADR-0027)

Adds R-VULN-01..09, docs/17 and ADR-0027: a scheduled digest scanner in the
cluster, a versioned report, an SSO page listing each CVE against its images,
containers and workloads, grouped email alerts, and a Renovate-shaped CI triage
bot. Renovate stays the only author of version changes, and "not scanned" is
always visible rather than reading as clean.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5.5 committed 2026-11-09 09:21:52 -06:00
1 parent c626349c7d
commit 0fb44bb0a8
12 files changed
+413 -1

No files matched your search

+1
View File
@@ -75,3 +75,4 @@ pattern choices.
| [0023](adr-0023-automated-updates-renovate.md) | Automated updates: Renovate for images and charts, every change PR-gated | Accepted | R-UPD-01, R-UPD-02, R-UPD-03, R-TEST-03 |
| [0024](adr-0024-delivery-authority-ci-gate.md) | Delivery authority: the CI gate is the approver; sensitive-path review; attributed admin override | Accepted | R-TEST-03, R-TEST-06, R-GITOPS-01, R-SEC-02 |
| [0025](adr-0025-in-cluster-ai-program-manager.md) | In-cluster AI Program Manager (pm-agent) | Accepted | R-GITOPS-01, R-TEST-03, R-TEST-06, R-SEC-02 |
| [0027](adr-0027-vulnerability-visibility.md) | Vulnerability visibility: scheduled digest scanning with a Renovate-shaped bot | Accepted | R-VULN-01..09 |
@@ -0,0 +1,67 @@
# ADR-0027 — Vulnerability visibility: scheduled digest scanning with a Renovate-shaped bot
- Status: Accepted
- Date: 2026-11-09
- Serves: R-VULN-01..09, R-OBS-02, R-OBS-03, R-C2-02, R-UPD-02, R-SEC-02, R-IDP-06
- Supersedes: —
- Accepted by: operator merge of this change; effective on merge.
## Context
The operator has no view of the vulnerabilities in what production runs. Renovate moves versions
forward on a schedule, but it knows nothing about CVEs, and nothing in the platform assesses a running
image. Measured on 2026-11-09:
- 259 containers run from 98 unique digests, across eight or more registries, with no scanner present.
- Prometheus holds 427k head series, using about 2.75 GiB of its 3 GiB limit.
- etcd and Longhorn share a partition on every node (accepted risk, [docs/02](../docs/02-environments-and-provisioning.md)),
and etcd's own metrics are not scraped, so the headroom for added etcd writes cannot be measured.
- CI holds no cluster credential by decision, and has two runner slots that also serve the merge gate.
- The implementation repository is readable without authentication.
- Trivy's release pipeline was compromised in March 2026 (CVE-2026-33634).
## Decision
**A small scheduled scanner runs in the cluster, built on the Trivy CLI, and a Renovate-shaped CI
bot turns its results into action.** The full contract is [docs/17](../docs/17-vulnerability-visibility.md).
- **Scanner.** Takes its inventory from the Kubernetes API, scans each unique digest with Trivy, and
stores one versioned report on a small dedicated volume, outside etcd and Prometheus.
- **Page.** Served from that report behind SSO and restricted to `platform-admins`.
- **Metrics.** Aggregates only.
- **Bot.** A scheduled Gitea Actions workflow using the existing CI-scoped Renovate credential. It
maintains a counts-only dashboard issue and requests Renovate PRs through Renovate's own
dashboard approval mechanism.
- **Renovate stays the sole author of version changes.** No second bot edits a tag.
- **Delivery tooling, not a catalog service.** Its cluster-wide read role is something a
ServiceDefinition cannot express, so it is hand-authored under `platform/` with its own non-gating
Flux tier, as in ADR-0025.
## Alternatives considered
- **Trivy Operator.** The obvious choice, and rejected on measurement. It stores one report per
workload as a CRD, adding write load to an etcd we cannot currently measure and which shares its
disk with Longhorn; the largest images produce the largest reports, so size-limit failures would
hide exactly the worst offenders. It needs broad cluster permissions and job-creation rights. It
would also still need a bespoke aggregator, because the per-CVE page cannot be fed from Prometheus.
- **Grafana table over per-CVE Prometheus metrics.** Needs no new page, but adds tens of thousands of
long-labelled series to a Prometheus at about 92% of its memory limit. Raising the limit treats the
symptom; a CVE inventory is not time-series data.
- **Scanning from Gitea Actions.** Closest to Renovate literally, but CI has no view of what is
running, and a long scan would occupy one of the two runner slots that gate merges.
- **A third-party Trivy dashboard.** Assumes the operator's CRDs, and adds a further small-maintainer
supply-chain dependency to a sensitive surface.
- **An in-cluster bot with a Gitea token.** Puts a git-writing credential in the cluster, which
ADR-0023 rejects.
- **A CVE dashboard issue listing the findings.** The most Renovate-like, and rejected because the
repository is publicly readable and the list would be an attack map.
- **A bot that authors bump PRs itself.** A package's fixed version does not identify the image tag
containing it, and two version authors would contend. Rejected in favour of driving Renovate.
## Consequences
- Scanning and its output are hands-off, and "not scanned" is visible rather than silent.
- The bot reuses the Renovate token, so both act as `renovate-bot`. This avoids a new credential;
splitting them later changes one workflow secret.
- The platform carries a small custom component that it must maintain and test. The report schema
isolates the scanner, so Trivy can be replaced without touching the page or the bot.
- Findings for private-registry images, VMs and nodes remain unassessed, and are shown as such
([known gaps](../docs/16-known-gaps.md)).
- The critical-fix alert will fire on first deployment. That is the backlog becoming visible.
- A tick can only open a Renovate PR. Remediation of majors and blast-radius components still waits
for human review.