Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion .mega-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ DISABLE_ERRORS_LINTERS:
# (#3205). Its dispositions are documented below because they are what holds that zero β€” each one
# is a scoped, resource-level skip naming its reason, not a disabled check.
#
# trivy (93 in the local v0.74.0 reproduction): Kubernetes misconfiguration in k8s/, which the
# trivy (66 in the local v0.74.0 reproduction): Kubernetes misconfiguration in k8s/, which the
# section above explains is NOT covered elsewhere. Tracked by #2787.
#
# trivy was 614 until KSV-0039 ("limit range usage") was skipped in .trivyignore.yaml β€” 450 of
Expand Down Expand Up @@ -137,6 +137,22 @@ DISABLE_ERRORS_LINTERS:
# ⚠️ That 109 is NOT 108 plus a regression. 108 was measured on trivy v0.73.0 and 109 on v0.74.0
# with no manifest change in between, so that 1 is a scanner rule-set difference rather than new
# backlog β€” the same drift the KSV-0037 note above records. Only the 16 are this slice's work.
# It then went 93 -> 66 across 29 -> 16 targets when KSV-0125's trusted-registry list was DECLARED
# rather than left at its default β€” 27 findings, the single largest family on this tree. The check
# ships trusting only Azure ACR, AWS ECR and GCR, so on a repository that uses quay.io, docker.io,
# registry.k8s.io and ghcr.io it flagged literally every container and measured nothing. The list
# lives in .trivy/data/trusted-registries.yaml and is wired in through REPOSITORY_TRIVY_ARGUMENTS
# below; see that file for why suffix matching means this catches an unexpected registry rather
# than a deliberately look-alike one.
#
# This is NOT a suppression: the check is live and now discriminating. It proved that by surviving
# on one container β€” vault-config's store-keys ran `alpine/k8s`, whose first path segment is a
# Docker Hub NAMESPACE, not a registry host, so the check read the registry as "alpine". That one
# was fixed at the manifest by writing the image fully-qualified (`docker.io/alpine/k8s`, same
# pinned digest), which is what the other 11 docker.io images here already do.
#
# The list is load-bearing, verified by ablation rather than asserted: deleting the single
# `quay.io` entry returns exactly 11 findings, and all 11 are quay.io-hosted containers.
#
# checkov is at 0 failed checks on all three frameworks CI runs. Its last two findings were both
# CKV_K8S_40, on the two vault-backup workloads, and #2904 dispositioned them as a scoped
Expand Down Expand Up @@ -308,6 +324,14 @@ REPOSITORY_TRIVY_ARGUMENTS:
- .trivyignore.yaml
- --skip-dirs
- tests
# KSV-0125's BUILT-IN trusted-registry list is Azure ACR, AWS ECR and GCR only β€” read from the
# check itself, not inferred. This repository uses none of those three, so without this argument
# every container it declares reads as "untrusted": 27 findings that measured nothing about supply
# chain and only said "this is not a Google, Azure or AWS shop". The data file declares the four
# registries actually in use, which is what turns the check into a gate that can catch an
# unexpected registry. Omitting this argument silently restores those 27.
- --config-data
- .trivy/data

# zizmor aborts with "fatal: no audit was performed" because MegaLinter strips GITHUB_TOKEN and its
# artipacked audit then 401s against the GitHub API. Handing it the token makes the audit run
Expand Down
34 changes: 34 additions & 0 deletions .trivy/data/trusted-registries.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Registries this repository is allowed to pull container images from.
#
# WHY THIS FILE EXISTS
# trivy's KSV-0125 ("Restrict container images to trusted registries") ships a default trusted list
# of Azure ACR, AWS ECR and GCR only β€” read from the check itself
# (policies/kubernetes/policies/uses_untrusted_registry.rego), not inferred. This repository uses
# none of those three, so every single container it declares was reported "untrusted": 27 findings,
# which said nothing about supply-chain risk and only said "this is not a Google, Azure or AWS
# shop". That noise is why REPOSITORY_TRIVY sits in DISABLE_ERRORS_LINTERS (#2787).
#
# The check reads `data.ksv0125.trusted_registries` and uses it INSTEAD of its defaults whenever the
# list is non-empty, so declaring the four registries actually in use turns the control from
# permanently-failing noise into a real gate: a workload pulling from anywhere else now stands out.
# The list below is derived from the tree, not chosen β€” every `image:` in k8s/ that names a registry
# host resolves to exactly these four.
#
# ⚠️ MATCHING IS SUFFIX-BASED, NOT EXACT. The check tests `endswith(registry, trusted)`, so an entry
# `ghcr.io` also trusts a registry named `evil-ghcr.io`. That is upstream behaviour this file cannot
# tighten; it is recorded so the guarantee is not overstated. The control catches an unexpected
# registry, it does not defeat a deliberately look-alike one β€” image digest pinning and the
# signature verification in #2856 are what cover that.
#
# Adding a registry here is a supply-chain decision: it must be a registry this project genuinely
# publishes to or consumes from, added in its own reviewed pull request.
ksv0125:
trusted_registries:
# Upstream project images (cert-manager, kubevirt, cdi, coroot, minio, openbao, …).
- quay.io
# Docker Hub β€” alpine/k8s, minio/mc, traefik and other upstream images.
- docker.io
# Kubernetes' own registry β€” coredns and other in-tree components.
- registry.k8s.io
# GitHub Container Registry β€” devantler-tech's own published images and upstream charts.
- ghcr.io
2 changes: 1 addition & 1 deletion k8s/bases/infrastructure/vault-config/job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ spec:
echo "Unseal phase complete."
# --- 2. Persist credentials in K8s Secret (only on fresh init) ---
- name: store-keys
image: alpine/k8s:1.36.2@sha256:44ef4942e171939b9c665a4a84beb80e2dcdb9a24330d4651cfdfd2e9deecc47
image: docker.io/alpine/k8s:1.36.2@sha256:44ef4942e171939b9c665a4a84beb80e2dcdb9a24330d4651cfdfd2e9deecc47
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
Expand Down
23 changes: 21 additions & 2 deletions scripts/megalinter-scan-counts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,29 @@
# - Command: [checkov --skip-path tests/ --config-file /action/lib/.automation/.checkov.yml
# --skip-path .agents --skip-path megalinter-reports --directory . --skip-framework secrets]
# - Command: [trivy fs --scanners vuln,misconfig --exit-code 1 --ignorefile
# .trivyignore.yaml --skip-dirs tests .]
# .trivyignore.yaml --skip-dirs tests --config-data .trivy/data .]
# Read them from a "🧹 Lint - mega-linter" job log if they ever need re-deriving:
# gh api repos/devantler-tech/platform/actions/jobs/<job-id>/logs | grep -aE '^\S+ - Command: '
#
# ⚠️ SCAN THE REPOSITORY ROOT. Narrowing trivy's target to k8s/ INFLATES the count, silently.
# .trivyignore.yaml's dispositions are path-scoped, and every one of them is written relative to the
# repository root (k8s/bases/infrastructure/**, k8s/clusters/prod/bootstrap/config-map.yaml, ...).
# Trivy matches those globs against paths relative to the SCAN ROOT, so a scan rooted at k8s/ sees
# bases/infrastructure/... instead, and the path-scoped entries stop matching. They deactivate with
# no warning, and the run re-reports findings this repository has already dispositioned.
#
# Measured on this tree with trivy v0.74.0, same subcommand and same flags, scan root the ONLY
# variable: `... .` reports 66 findings across 16 targets, `... k8s/` reports 133 across 62 β€” the
# count doubles. Every check ID that appears only in the k8s/-rooted arm (KSV-0022, KSV-0037,
# KSV-0053, KSV-0109, KSV-0114, KSV-0117) is already dispositioned there; KSV-0037 alone
# accounts for 47 of the 67 extra. A few IDs rise without being new to that arm (KSV-0041, KSV-0046,
# KSV-0056) because their disposition covers only some paths, so the scoped subset deactivates while
# their other instances legitimately remain.
#
# The failure direction is what makes this dangerous: it looks like more backlog to work off, not
# like a broken measurement, so it reads as plausible and gets believed. Run the command above from
# the repository root, or the number is not comparable to CI's.
#
# TWO DELIBERATE DIFFERENCES FROM THE CI COMMAND, both verified not to change the counts.
# (--skip-framework secrets is NOT one of them: CI passes it too since MegaLinter 10.0.0, so
# mirroring it keeps the invocations aligned rather than diverging them. The two --skip-path
Expand Down Expand Up @@ -260,7 +279,7 @@ scan_trivy() {
local out="$OUT_DIR/trivy.txt" rc=0
printf '\nRunning trivy (this takes ~1 minute)...\n' >&2
(cd "$REPO_ROOT" && trivy fs --scanners vuln,misconfig --exit-code 1 \
--ignorefile .trivyignore.yaml --skip-dirs tests .) \
--ignorefile .trivyignore.yaml --skip-dirs tests --config-data .trivy/data .) \
>"$out" 2>"$OUT_DIR/trivy.err" || rc=$?
if [ "$rc" -gt 1 ]; then
printf 'trivy exited %d β€” refusing to report a count from an incomplete run\n' "$rc" >&2
Expand Down
Loading