Skip to content

security(vault-backup): stop the mirror inheriting a gid chosen for openbao - #3281

Closed
devantler wants to merge 1 commit into
mainfrom
claude/vault-backup-mirror-gid-3202
Closed

devantler wants to merge 1 commit into
mainfrom
claude/vault-backup-mirror-gid-3202

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Motivation

The nightly OpenBao backup pods were running one container under a group ID that was never meant for it. The mirror step uses a different image from the snapshot step, but it inherited the group chosen for OpenBao — so a security scan flagged it, and the existing written justification for that low ID did not actually cover it.

Changes

Give the backup pods the same high, unprivileged group the rest of the pod already uses, so only the container that genuinely needs OpenBao's built-in identity still runs with it.

The two steps still hand the snapshot file between them exactly as before — that sharing never depended on the group being changed here, and the same arrangement is already proven in the sibling job.

Measured effect on this repository's own security scan: 6 findings → 4. The four that remain are the OpenBao image's own built-in identity, which is a separate and already-documented decision.

Part of #3202

…penbao

The vault-backup pods set runAsGroup 1000 at POD level, so the only container
that does not override it -- the `minio/mc` mirror -- ran a low GID picked for
a different image. `mc` bakes no group of its own, so nothing about 1000 was
its identity; trivy reported it as KSV-0021 on both the Job and the CronJob.

Raise the pod default to the same high 65532 the uid already uses, exactly as
vault-config does. The snapshot container keeps restating the openbao image's
baked `openbao:x:100:1000` explicitly, so the CKV_K8S_40 annotation now covers
only the execution its rationale accounts for instead of one more it does not.

fsGroup stays 1000, which is what makes this safe rather than a trade: reading
the snapshot never depended on the mirror's PRIMARY gid. Kubernetes adds fsGroup
to every container's supplementary groups and sets the setgid bit on the volume,
so the 0640 snapshot #3279 established still lands group 1000 and a gid-65532
reader still opens it. vault-config already relies on that mechanism and pinned
it there against a paired control (#3258).

Measured on this tree with trivy 0.74.0, scan root the repository root and the
repo's own flags: 6 misconfigurations -> 4. Both retired findings are the
mirror's KSV-0021; the remaining 4 are all the snapshot container's baked
100:1000 pair.

Part of #3202
@github-actions

Copy link
Copy Markdown
Contributor

✅⚠️MegaLinter analysis: Success with warnings

⚠️ BASH / bash-exec - 6 errors
Results of bash-exec linter (version 5.3.9)
See documentation on https://megalinter.io/10.0.0/descriptors/bash_bash_exec/
-----------------------------------------------

✅ [SUCCESS] .github/scripts/setup-ksail.sh
✅ [SUCCESS] scripts/check-megalinter-version-drift.sh
✅ [SUCCESS] scripts/dr-rebuild-supersession-guard.sh
❌ [ERROR] scripts/ghcr-auth-lib.sh
    Error: File:[scripts/ghcr-auth-lib.sh] is not executable

✅ [SUCCESS] scripts/guard-checkov-skip-reasons.sh
✅ [SUCCESS] scripts/guard-cilium-homogeneous-device-rollout.sh
✅ [SUCCESS] scripts/guard-kubescape-gate-frameworks.sh
✅ [SUCCESS] scripts/guard-limitrange-premise.sh
✅ [SUCCESS] scripts/guard-shared-publish-workflow-pin.sh
✅ [SUCCESS] scripts/megalinter-scan-counts.sh
✅ [SUCCESS] scripts/normalize-sarif-paths.sh
❌ [ERROR] scripts/refresh-flux-ghcr-auth-safety.sh
    Error: File:[scripts/refresh-flux-ghcr-auth-safety.sh] is not executable

✅ [SUCCESS] scripts/refresh-flux-ghcr-auth.sh
✅ [SUCCESS] scripts/report-cilium-rollout-gate-suppression.sh
✅ [SUCCESS] scripts/run-ksail-prod-with-pull-auth.sh
✅ [SUCCESS] scripts/summarize-sarif-findings.sh
✅ [SUCCESS] scripts/tests/test-actual-budget-auth-route.sh
✅ [SUCCESS] scripts/tests/test-check-megalinter-version-drift.sh
✅ [SUCCESS] scripts/tests/test-cilium-bandwidth-manager-component.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-activation.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-autoscaler-gate.sh
✅ [SUCCESS] scripts/tests/test-cilium-homogeneous-devices-flux-wait.sh
✅ [SUCCESS] scripts/tests/test-cilium-mutual-auth-policy-regressions.sh
✅ [SUCCESS] scripts/tests/test-cilium-mutual-auth-policy.sh
✅ [SUCCESS] scripts/tests/test-cilium-rollout-gate-suppression-signal.sh
✅ [SUCCESS] scripts/tests/test-cnpg-degraded-alert.sh
✅ [SUCCESS] scripts/tests/test-coroot-postgres-scrape-policy.sh
✅ [SUCCESS] scripts/tests/test-crossplane-egress-policy.sh
✅ [SUCCESS] scripts/tests/test-crossplane-sync-exporter.sh
✅ [SUCCESS] scripts/tests/test-dr-rebuild-supersession-guard.sh
✅ [SUCCESS] scripts/tests/test-github-config-role-activation-parity.sh
✅ [SUCCESS] scripts/tests/test-guard-checkov-skip-reasons.sh
❌ [ERROR] scripts/tests/test-guard-limitrange-premise.sh
    Error: File:[scripts/tests/test-guard-limitrange-premise.sh] is not executable

❌ [ERROR] scripts/tests/test-headlamp-plugin-removal.sh
    Error: File:[scripts/tests/test-headlamp-plugin-removal.sh] is not executable

✅ [SUCCESS] scripts/tests/test-kubescape-gate-frameworks-guard.sh
✅ [SUCCESS] scripts/tests/test-kyverno-admission-vpa.sh
❌ [ERROR] scripts/tests/test-kyverno-umami-mutation-rbac.sh
    Error: File:[scripts/tests/test-kyverno-umami-mutation-rbac.sh] is not executable

✅ [SUCCESS] scripts/tests/test-megalinter-scan-counts-ignorefile.sh
✅ [SUCCESS] scripts/tests/test-minio-create-bucket-credentials.sh
✅ [SUCCESS] scripts/tests/test-normalize-sarif-paths.sh
✅ [SUCCESS] scripts/tests/test-openbao-oidc-role.sh
✅ [SUCCESS] scripts/tests/test-opencost-usage-scraper.sh
✅ [SUCCESS] scripts/tests/test-pvc-prune-safety.sh
✅ [SUCCESS] scripts/tests/test-refresh-flux-ghcr-auth-safety.sh
✅ [SUCCESS] scripts/tests/test-restrict-homepage-service-groups.sh
✅ [SUCCESS] scripts/tests/test-restrict-tenant-secret-stores.sh
✅ [SUCCESS] scripts/tests/test-setup-ksail.sh
✅ [SUCCESS] scripts/tests/test-shared-publish-workflow-pin-guard.sh
✅ [SUCCESS] scripts/tests/test-summarize-sarif-findings.sh
✅ [SUCCESS] scripts/tests/test-tenant-route-hostname-boundary.sh
✅ [SUCCESS] scripts/tests/test-trivyignore-configmap-content-boundary.sh
✅ [SUCCESS] scripts/tests/test-trivyignore-first-party-rbac-boundary.sh
✅ [SUCCESS] scripts/tests/test-trivyignore-vault-config-identity-boundary.sh
✅ [SUCCESS] scripts/tests/test-trivyignore-vendored-operator-boundary.sh
✅ [SUCCESS] scripts/tests/test-umami-provisioning-bootstrap.sh
✅ [SUCCESS] scripts/tests/test-use-prod-stable-api-endpoint.sh
✅ [SUCCESS] scripts/tests/test-validate-image-verifier-liveness.sh
❌ [ERROR] scripts/tests/test-vault-snapshot-group-readable.sh
    Error: File:[scripts/tests/test-vault-snapshot-group-readable.sh] is not executable

✅ [SUCCESS] scripts/tests/test-verify-published-evidence.sh
✅ [SUCCESS] scripts/update-vendored-operators.sh
✅ [SUCCESS] scripts/use-prod-stable-api-endpoint.sh
✅ [SUCCESS] scripts/validate-alert-coverage.sh
✅ [SUCCESS] scripts/validate-image-verifier-liveness.sh
✅ [SUCCESS] scripts/verify-published-evidence.sh
✅ [SUCCESS] scripts/wait-for-platform-flux-revision.sh
⚠️ SPELL / cspell - 4053 errors
"growpart",
        "grpcroutes",
        "gsub",
        "healthchecks",
        "helmrelease",
        "helmreleases",
        "helmv",
        "heredocs",
        "hetznerlike",
        "homelab",
        "hostnames",
        "httproute",
        "httproutes",
        "idempotently",
        "ifnotpresent",
        "ignorefile",
        "imagevalidatingpolicy",
        "imranismail",
        "initprovider",
        "injective",
        "inspectable",
        "iscsi",
        "italicise",
        "ivpol",
        "jobif",
        "journalled",
        "keylessly",
        "keypair",
        "kprobes",
        "kptr",
        "krew",
        "ksail",
        "ksail's",
        "ksailcd",
        "kubeconfig",
        "kubeconfig's",
        "kubeconform",
        "kubeconform's",
        "kubelet",
        "kubelet's",
        "kubelets",
        "kubescape",
        "kubescape's",
        "kubespan",
        "kubevirt",
        "kubevuln",
        "kustomization",
        "kustomization's",
        "kustomizations",
        "letsencrypt",
        "libc",
        "libgnutls",
        "limfvbk",
        "limitrange",
        "lintable",
        "livez",
        "loadtester",
        "locationless",
        "logfile",
        "lookarounds",
        "lrwxrwxrwx",
        "lserror",
        "lsfail",
        "luks",
        "lycheeignore",
        "machineconfig",
        "materialise",
        "materialised",
        "materialises",
        "maxage",
        "maxbackup",
        "maxsize",
        "maxx",
        "microtime",
        "mikefarah",
        "misconfig",
        "misconfigs",
        "misordered",
        "mistargeted",
        "mitrelike",
        "mktemp",
        "mlock",
        "multidoc",
        "multidoclike",
        "mutatingpolicies",
        "mutatingwebhookconfigurations",
        "myapp",
        "mycorp",
        "najsk",
        "namespaceless",
        "neighbour",
        "neighbouring",
        "nenv",
        "nestedlike",
        "netlink",
        "netpol",
        "netpols",
        "neutralises",
        "neutralising",
        "nextjs",
        "nftables",
        "nilnil",
        "nlimitrange",
        "nobuckets",
        "nodepod",
        "nodeport",
        "nolabel",
        "nolint",
        "nonexec",
        "nonroot",
        "nons",
        "nonslike",
        "noprov",
        "normalisation",
        "normalise",
        "normalised",
        "normalises",
        "normalising",
        "nosec",
        "nsalike",
        "nsxform",
        "nsxformlike",
        "nullglob",
        "ocirepository",
        "okcommented",
        "okdouble",
        "okempty",
        "okflowfirst",
        "okflowsecond",
        "okhash",
        "oklookalike",
        "okmention",
        "okmerge",
        "okmultidoc",
        "okprose",
        "oksingle",
        "oktooling",
        "oktrailing",
        "oktwoflow",
        "onlycri",
        "onlysystem",
        "openbao",
        "openbao's",
        "opencost",
        "openfeature",
        "oras",
        "otherns",
        "otherplugin",
        "overclaimed",
        "overprovisioning",
        "parallelised",
        "partialgroup",
        "pasteable",
        "permissioning",
        "persistentvolumeclaims",
        "persistentvolumes",
        "phaseless",
        "pipefail",
        "policyreports",
        "portforward",
        "pousr",
        "preemptible",
        "preservingly",
        "prioritisable",
        "prioritisation",
        "prioritised",
        "privesc",
        "probeerror",
        "providerconfigs",
        "pseudonymization",
        "pseudonymized",
        "pseudonymizes",
        "publishapp",
        "publishprovider",
        "pushsecret",
        "pushsecrets",
        "qrbvrml",
        "randomises",
        "rdqwpktr",
        "readyz",
        "reassertions",
        "recognisable",
        "recognisably",
        "recognise",
        "recognised",
        "recolour",
        "reconverges",
        "referencegrants",
        "refreshfluxghcrauth",
        "regenerable",
        "rego",
        "releaserc",
        "rematerialise",
        "rematerialised",
        "replicaset",
        "repoint",
        "repointed",
        "repoints",
        "repositoryrulesets",
        "resizer",
        "restrictor",
        "retabbed",
        "retarget",
        "rmem",
        "rolebindings",
        "rollouts",
        "rshared",
        "rwxr",
        "sanitised",
        "sanitiser",
        "sanitising",
        "sarif",
        "scheckov",
        "schedulability",
        "schedulable",
        "scopeable",
        "scopeless",
        "seccomp",
        "secretbox",
        "secretstore",
        "seedable",
        "selftest",
        "serialise",
        "serialised",
        "serverside",
        "serviceaccount",
        "serviceaccounts",
        "setgid",
        "sgdisk",
        "shellcheck",
        "shfmt",
        "shopt",
        "shortsha",
        "siderolabs",
        "siderolink",
        "sigstore",
        "skmde",
        "slurpfile",
        "sngle",
        "softwarecomposition",
        "specnull",
        "spiffe",
        "sprintf",
        "srole",
        "stakater",
        "startswith",
        "statefulset",
        "statefulsets",
        "statemanager",
        "stdlib",
        "stepif",
        "storageclass",
        "strenv",
        "subresource",
        "subresources",
        "subshell",
        "subtest",
        "summarised",
        "surfaceless",
        "syft",
        "syscall",
        "sysctls",
        "syste",
        "systembare",
        "tagliteral",
        "tagonly",
        "talosconfig",
        "talosctl",
        "tanzu",
        "tcproutes",
        "teammemberships",
        "teamrepositories",
        "templatesyncignore",
        "thresholded",
        "tlsroutes",
        "tlsv",
        "tmpl",
        "toplevel",
        "tostring",
        "tracepoints",
        "travelled",
        "treewide",
        "trixie",
        "trueish",
        "trustd",
        "ture",
        "udproutes",
        "uids",
        "umami",
        "umami's",
        "unablated",
        "unclickable",
        "uncompilable",
        "unconfigured",
        "uncordon",
        "uncordoned",
        "uncordoning",
        "uncordons",
        "undecoded",
        "undercounts",
        "undispositioned",
        "unenforcing",
        "unevidenced",
        "unexcepted",
        "unfiled",
        "ungated",
        "ungenerated",
        "unifi",
        "uninspected",
        "uninvoked",
        "unioned",
        "unmarshalling",
        "unmarshals",
        "unmodelled",
        "unparseable",
        "unpremised",
        "unprovisioned",
        "unrecognisable",
        "unrecognised",
        "unrelatedlike",
        "unrepresentable",
        "unreviewed",
        "unroutable",
        "unrun",
        "unshippable",
        "unskipped",
        "untrackable",
        "unvalidated",
        "unwaited",
        "unwired",
        "upbound",
        "updatekeys",
        "upjet",
        "upstreaming",
        "urlencode",
        "userinfo",
        "userns",
        "ushfn",
        "validatable",
        "validatealertcoverage",
        "validatingwebhookconfigurations",
        "vcunav",
        "velero",
        "virt",
        "volumesnapshot",
        "vpas",
        "vulnerabilitymanifests",
        "vulnerabilitymanifestsummary",
        "vulns",
        "vxlan",
        "webapps",
        "wffc",
        "wgpolicyk",
        "wildcarded",
        "wildcarding",
        "wlid",
        "wmem",
        "workloadconfigurationscans",
        "workloadconfigurationscansummary",
        "worktrees",
        "wrongns",
        "xcheckov",
        "xform",
        "xoxb",
        "xpkg",
        "yannh",
        "yubikey",
        "yzwvjjmcyfnl",
        "zizmor"
    ]
}


You can also copy-paste megalinter-reports/.cspell.json at the root of your repository

(Truncated to last 8000 characters out of 735346)
⚠️ COPYPASTE / jscpd - 153 errors
(19 lines, 65 tokens)
   scripts/tests/test-opencost-usage-scraper.sh [15:1 - 33:2]
Clone found (bash)
 - scripts/tests/test-cilium-mutual-auth-policy.sh [28:29 - 41:10] (14 lines, 78 tokens)
   scripts/tests/test-cilium-mutual-auth-policy.sh [79:33 - 92:10]
Clone found (bash)
 - scripts/tests/test-cilium-mutual-auth-policy.sh [98:33 - 107:54] (10 lines, 57 tokens)
   scripts/tests/test-cilium-mutual-auth-policy.sh [118:32 - 127:54]
Clone found (bash)
 - scripts/tests/test-cnpg-degraded-alert.sh [270:33 - 280:2] (11 lines, 56 tokens)
   scripts/tests/test-cnpg-degraded-alert.sh [404:51 - 414:2]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [123:9 - 128:13] (6 lines, 55 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [150:9 - 155:13]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [127:54 - 145:9] (19 lines, 171 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [155:39 - 173:9]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [184:36 - 192:28] (9 lines, 87 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [198:48 - 206:28]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [260:25 - 268:19] (9 lines, 52 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [280:24 - 288:19]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [260:25 - 268:18] (9 lines, 51 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [305:31 - 313:18]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [291:13 - 299:8] (9 lines, 52 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [370:18 - 378:14]
Clone found (bash)
 - scripts/tests/test-crossplane-egress-policy.sh [369:22 - 378:15] (10 lines, 63 tokens)
   scripts/tests/test-crossplane-egress-policy.sh [379:33 - 388:15]
Clone found (bash)
 - scripts/tests/test-crossplane-sync-exporter.sh [186:1 - 235:2] (50 lines, 299 tokens)
   scripts/tests/test-kyverno-umami-mutation-rbac.sh [16:1 - 63:2]
Clone found (bash)
 - scripts/tests/test-crossplane-sync-exporter.sh [186:1 - 237:11] (52 lines, 303 tokens)
   scripts/tests/test-opencost-usage-scraper.sh [33:1 - 84:11]
Clone found (bash)
 - scripts/tests/test-crossplane-sync-exporter.sh [221:27 - 238:53] (18 lines, 53 tokens)
   scripts/tests/test-opencost-usage-scraper.sh [68:27 - 85:51]
Clone found (bash)
 - scripts/tests/test-guard-checkov-skip-reasons.sh [69:3 - 81:2] (13 lines, 78 tokens)
   scripts/tests/test-guard-limitrange-premise.sh [98:3 - 110:2]
Clone found (bash)
 - scripts/tests/test-headlamp-plugin-removal.sh [121:89 - 126:22] (6 lines, 65 tokens)
   scripts/tests/test-headlamp-plugin-removal.sh [165:84 - 170:22]
Clone found (bash)
 - scripts/tests/test-refresh-flux-ghcr-auth-safety.sh [348:5 - 356:51] (9 lines, 76 tokens)
   scripts/tests/test-refresh-flux-ghcr-auth-safety.sh [357:5 - 365:51]
Clone found (python)
 - scripts/tests/test_validate_homepage_bookmarks.py [46:57 - 54:54] (9 lines, 58 tokens)
   scripts/tests/test_validate_homepage_bookmarks.py [100:53 - 109:54]
Clone found (go)
 - scripts/validate-dr-signing/main_test.go [1007:13 - 1015:2] (9 lines, 51 tokens)
   scripts/validate-dr-signing/main_test.go [1075:12 - 1083:2]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [564:50 - 569:24] (6 lines, 103 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1076:31 - 1081:24]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [596:14 - 602:4] (7 lines, 115 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [923:43 - 929:4]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [622:13 - 631:7] (10 lines, 158 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1205:18 - 1214:7]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [631:1 - 636:8] (6 lines, 93 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1215:1 - 1220:8]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [708:5 - 713:2] (6 lines, 82 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [894:8 - 899:9]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [709:1 - 719:23] (11 lines, 220 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [986:60 - 997:3]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [713:15 - 724:2] (12 lines, 185 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [814:17 - 825:2]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [760:15 - 771:12] (12 lines, 264 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [969:1 - 982:3]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [823:2 - 829:19] (7 lines, 89 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1008:7 - 1014:19]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [823:2 - 829:33] (7 lines, 103 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1390:58 - 1396:33]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [823:2 - 829:4] (7 lines, 74 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1503:5 - 1509:4]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [916:30 - 921:8] (6 lines, 50 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [943:44 - 948:8]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [1030:22 - 1036:2] (7 lines, 135 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1589:22 - 1595:2]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [1328:33 - 1336:11] (9 lines, 118 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1340:130 - 1348:11]
Clone found (go)
 - scripts/validate-eks-ci-role-policy/main_test.go [1535:47 - 1540:2] (6 lines, 166 tokens)
   scripts/validate-eks-ci-role-policy/main_test.go [1615:46 - 1620:2]
Clone found (go)
 - scripts/validate-flux-verify/instance_test.go [109:62 - 126:31] (18 lines, 57 tokens)
   scripts/validate-flux-verify/instance_test.go [161:57 - 178:31]
Clone found (go)
 - scripts/validate-flux-verify/instance_test.go [109:62 - 128:35] (20 lines, 62 tokens)
   scripts/validate-flux-verify/instance_test.go [198:55 - 217:26]
Clone found (python)
 - scripts/validate-naming.py [126:52 - 132:25] (7 lines, 53 tokens)
   scripts/validate-naming.py [171:82 - 177:29]
┌────────┬────────────────┬─────────────┬──────────────┬──────────────┬──────────────────┬───────────────────┐
│ Format │ Files analyzed │ Total lines │ Total tokens │ Clones found │ Duplicated lines │ Duplicated tokens │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ bash   │ 65             │ 21429       │ 83321        │ 37           │ 502 (2.34%)      │ 3455 (4.15%)      │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ go     │ 38             │ 33781       │ 200009       │ 114          │ 929 (2.75%)      │ 8609 (4.30%)      │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ python │ 4              │ 807         │ 5606         │ 2            │ 14 (1.73%)       │ 111 (1.98%)       │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ txt    │ 1              │ 229         │ 1289         │ 0            │ 0 (0.00%)        │ 0 (0.00%)         │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ Total: │ 108            │ 56246       │ 290225       │ 153          │ 1445 (2.57%)     │ 12175 (4.20%)     │
└────────┴────────────────┴─────────────┴──────────────┴──────────────┴──────────────────┴───────────────────┘
Found 153 clones.
HTML report saved to megalinter-reports/copy-paste/jscpd-report.html
ERROR: jscpd found too many duplicates (2.6%) over threshold (0.0%)
time: 1.03s

(Truncated to last 8000 characters out of 29133)
⚠️ MARKDOWN / markdownlint - 65 errors
.claude/skills/maintain/SKILL.md:6 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "Perform maintenance per the **..."]
AGENTS.md:15:401 error MD013/line-length Line length [Expected: 400; Actual: 838]
AGENTS.md:24 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
AGENTS.md:101:401 error MD013/line-length Line length [Expected: 400; Actual: 1784]
AGENTS.md:103:401 error MD013/line-length Line length [Expected: 400; Actual: 439]
AGENTS.md:105:401 error MD013/line-length Line length [Expected: 400; Actual: 1126]
AGENTS.md:106:401 error MD013/line-length Line length [Expected: 400; Actual: 628]
AGENTS.md:107:401 error MD013/line-length Line length [Expected: 400; Actual: 1774]
AGENTS.md:160:401 error MD013/line-length Line length [Expected: 400; Actual: 649]
AGENTS.md:162:401 error MD013/line-length Line length [Expected: 400; Actual: 971]
AGENTS.md:189:401 error MD013/line-length Line length [Expected: 400; Actual: 970]
AGENTS.md:193:401 error MD013/line-length Line length [Expected: 400; Actual: 660]
AGENTS.md:211:401 error MD013/line-length Line length [Expected: 400; Actual: 1510]
AGENTS.md:279:401 error MD013/line-length Line length [Expected: 400; Actual: 1016]
AGENTS.md:280:401 error MD013/line-length Line length [Expected: 400; Actual: 491]
AGENTS.md:281:401 error MD013/line-length Line length [Expected: 400; Actual: 468]
AGENTS.md:287:401 error MD013/line-length Line length [Expected: 400; Actual: 532]
AGENTS.md:289:401 error MD013/line-length Line length [Expected: 400; Actual: 523]
AGENTS.md:292:401 error MD013/line-length Line length [Expected: 400; Actual: 613]
AGENTS.md:293:401 error MD013/line-length Line length [Expected: 400; Actual: 714]
AGENTS.md:297:401 error MD013/line-length Line length [Expected: 400; Actual: 502]
AGENTS.md:301:401 error MD013/line-length Line length [Expected: 400; Actual: 441]
AGENTS.md:306:401 error MD013/line-length Line length [Expected: 400; Actual: 427]
AGENTS.md:409:401 error MD013/line-length Line length [Expected: 400; Actual: 1139]
AGENTS.md:411:401 error MD013/line-length Line length [Expected: 400; Actual: 1240]
AGENTS.md:427:401 error MD013/line-length Line length [Expected: 400; Actual: 1137]
AGENTS.md:824:401 error MD013/line-length Line length [Expected: 400; Actual: 790]
AGENTS.md:829:401 error MD013/line-length Line length [Expected: 400; Actual: 515]
CLAUDE.md:1 error MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "@AGENTS.md"]
docs/dr/alerting.md:226:28 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:22:389 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:23:264 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:35 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:161 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:27:239 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/crypto-custody.md:114 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "Custody recommendations"]
docs/dr/crypto-custody.md:245 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "Custody recommendations"]
docs/dr/crypto-custody.md:251 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "What to do if it leaks"]
docs/dr/crypto-custody.md:258 error MD024/no-duplicate-heading Multiple headings with the same content [Context: "What to do if it is *lost* (no..."]
docs/dr/restore-drill.md:42 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:23:102 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/runbook.md:23:487 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/runbook.md:34 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:41 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:50 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/dr/runbook.md:589:92 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/spire-server-ha.md:93 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/dr/velero-cnpg.md:11 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/dr/velero-cnpg.md:56:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:56:166 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:57:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:57:227 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:58:78 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/dr/velero-cnpg.md:58:166 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
docs/github-management.md:38:401 error MD013/line-length Line length [Expected: 400; Actual: 419]
docs/github-management.md:40:401 error MD013/line-length Line length [Expected: 400; Actual: 522]
docs/node-autoscaling.md:14 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/oidc-kubectl.md:95 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/runtime-security.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/rwx-storage.md:9 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/unifi-management.md:14 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/unifi-management.md:62 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
README.md:116:401 error MD013/line-length Line length [Expected: 400; Actual: 540]
README.md:237:32 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
README.md:237:36 error MD060/table-column-style Table column style [Table pipe does not align with header for style "aligned"]
⚠️ REPOSITORY / trivy - 1 error
astructure/vertical-pod-autoscalers/metrics-server.ya- │ kubernetes │        -        │         0         │
│ ml                                                                               │            │                 │                   │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/snapshot-controll- │ kubernetes │        -        │         0         │
│ er.yaml                                                                          │            │                 │                   │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/spire-agent.yaml   │ kubernetes │        -        │         0         │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/spire-server.yaml  │ kubernetes │        -        │         0         │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon-operator- │ kubernetes │        -        │         0         │
│ .yaml                                                                            │            │                 │                   │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/vertical-pod-autoscalers/tetragon.yaml      │ kubernetes │        -        │         0         │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ k8s/providers/hetzner/infrastructure/volume-snapshot-classes/longhorn.yaml       │ kubernetes │        -        │         0         │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ ksail.prod.yaml                                                                  │ kubernetes │        -        │         0         │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ ksail.yaml                                                                       │ kubernetes │        -        │         0         │
└──────────────────────────────────────────────────────────────────────────────────┴────────────┴─────────────────┴───────────────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)


k8s/bases/infrastructure/vault-backup/cron-job.yaml (kubernetes)
================================================================
Tests: 116 (SUCCESSES: 114, FAILURES: 2)
Failures: 2 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

KSV-0020 (LOW): Container 'snapshot' of CronJob 'vault-snapshot' should set 'securityContext.runAsUser' > 10000
════════════════════════════════════════
Force the container to run with user ID > 10000 to avoid conflicts with the host’s user table.

See https://avd.aquasec.com/misconfig/ksv-0020
────────────────────────────────────────
 k8s/bases/infrastructure/vault-backup/cron-job.yaml:116-154
────────────────────────────────────────
 116 ┌             - name: snapshot
 117 │               image: quay.io/openbao/openbao:2.5.3@sha256:fdc6da21ca6963560c32336fd7feb9cf2d5e52668f1a1647205a4b41171f0806
 118 │               securityContext:
 119 │                 # UID 100 is the openbao image's own baked identity (`openbao:x:100:1000`
 120 │                 # in its /etc/passwd) — see the checkov.io/skip2 rationale. Only this
 121 │                 # container overrides the pod's high default; the mirror takes 65532
 122 │                 # and reads the snapshot on the shared primary group 1000.
 123 │                 runAsUser: 100
 124 └                 runAsGroup: 1000
 ...   
────────────────────────────────────────


KSV-0021 (LOW): Container 'snapshot' of CronJob 'vault-snapshot' should set 'securityContext.runAsGroup' > 10000
════════════════════════════════════════
Force the container to run with group ID > 10000 to avoid conflicts with the host’s user table.

See https://avd.aquasec.com/misconfig/ksv-0021
────────────────────────────────────────
 k8s/bases/infrastructure/vault-backup/cron-job.yaml:116-154
────────────────────────────────────────
 116 ┌             - name: snapshot
 117 │               image: quay.io/openbao/openbao:2.5.3@sha256:fdc6da21ca6963560c32336fd7feb9cf2d5e52668f1a1647205a4b41171f0806
 118 │               securityContext:
 119 │                 # UID 100 is the openbao image's own baked identity (`openbao:x:100:1000`
 120 │                 # in its /etc/passwd) — see the checkov.io/skip2 rationale. Only this
 121 │                 # container overrides the pod's high default; the mirror takes 65532
 122 │                 # and reads the snapshot on the shared primary group 1000.
 123 │                 runAsUser: 100
 124 └                 runAsGroup: 1000
 ...   
────────────────────────────────────────



k8s/bases/infrastructure/vault-backup/job.yaml (kubernetes)
===========================================================
Tests: 116 (SUCCESSES: 114, FAILURES: 2)
Failures: 2 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

KSV-0020 (LOW): Container 'snapshot' of Job 'vault-snapshot-init' should set 'securityContext.runAsUser' > 10000
════════════════════════════════════════
Force the container to run with user ID > 10000 to avoid conflicts with the host’s user table.

See https://avd.aquasec.com/misconfig/ksv-0020
────────────────────────────────────────
 k8s/bases/infrastructure/vault-backup/job.yaml:114-150
────────────────────────────────────────
 114 ┌         - name: snapshot
 115 │           image: quay.io/openbao/openbao:2.5.3@sha256:fdc6da21ca6963560c32336fd7feb9cf2d5e52668f1a1647205a4b41171f0806
 116 │           securityContext:
 117 │             # UID 100 is the openbao image's own baked identity (`openbao:x:100:1000`
 118 │             # in its /etc/passwd) — see the checkov.io/skip2 rationale. Only this
 119 │             # container overrides the pod's high default; the mirror takes 65532
 120 │             # and reads the snapshot on the shared primary group 1000.
 121 │             runAsUser: 100
 122 └             runAsGroup: 1000
 ...   
────────────────────────────────────────


KSV-0021 (LOW): Container 'snapshot' of Job 'vault-snapshot-init' should set 'securityContext.runAsGroup' > 10000
════════════════════════════════════════
Force the container to run with group ID > 10000 to avoid conflicts with the host’s user table.

See https://avd.aquasec.com/misconfig/ksv-0021
────────────────────────────────────────
 k8s/bases/infrastructure/vault-backup/job.yaml:114-150
────────────────────────────────────────
 114 ┌         - name: snapshot
 115 │           image: quay.io/openbao/openbao:2.5.3@sha256:fdc6da21ca6963560c32336fd7feb9cf2d5e52668f1a1647205a4b41171f0806
 116 │           securityContext:
 117 │             # UID 100 is the openbao image's own baked identity (`openbao:x:100:1000`
 118 │             # in its /etc/passwd) — see the checkov.io/skip2 rationale. Only this
 119 │             # container overrides the pod's high default; the mirror takes 65532
 120 │             # and reads the snapshot on the shared primary group 1000.
 121 │             runAsUser: 100
 122 └             runAsGroup: 1000
 ...   
────────────────────────────────────────



📣 Notices:
  - Version 0.74.0 of Trivy is now available, current version is 0.73.0

To suppress version checks, run Trivy scans with the --skip-version-check flag

(Truncated to last 8000 characters out of 197717)

✅ Linters with no issues

actionlint, betterleaks, checkov, git_diff, grype, jsonlint, lychee, osv-scanner, prettier, prettier, revive, secretlint, shellcheck, shfmt, syft, trivy-sbom, trufflehog, v8r, v8r, yamllint, zizmor

Notices

⚠️ Your configuration references items that have been removed from MegaLinter and are ignored: REPOSITORY_GITLEAKS. See Removed linters to find their replacements.

See detailed reports in MegaLinter artifacts

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Closing this — the change is not safe as written, and CI was right to stop it.

scripts/tests/test-vault-snapshot-group-readable.sh pins pod runAsGroup: 1000 on purpose, and its rationale says exactly why: the mirror reads the snapshot on its primary GROUP entry alone — "a pod-level property, which is why this does not depend on the volume's setgid bit."

I justified this change from the vault-config precedent, which does run pod runAsGroup: 65532 alongside fsGroup: 1000. That precedent differs in the one respect that carries the mechanism: its shared paths are emptyDir volumes, where the comment (and #3258's paired control) rely on the setgid bit and supplementary groups. /snapshots here is a PersistentVolumeClaim, and the guard's author deliberately chose primary-GID sharing so this path would not depend on that mechanism.

So I asserted the two cases were equivalent on the point where they differ. I could not run the experiment that would settle it — reading a 0640 snapshot off that PVC as a gid-65532 process — and the failure mode if I am wrong is the nightly OpenBao backup silently ceasing to mirror. That is not something to ship on reasoning.

Withdrawing rather than adjusting the guard. Editing the test to accept 65532 would make the check pass without establishing the property it exists to protect.

The finding is real and worth keeping, so it is recorded on #3202 with a concrete next step: supplementalGroups: [1000] grants the group explicitly, independent of both primary GID and setgid, which would satisfy KSV-0021 while making the guard's invariant stronger rather than weaker — and it turns the guard from asserting an implementation (runAsGroup == 1000) into asserting the property (the mirror holds group 1000). That still needs the read verified against the real PVC before it ships.

@devantler devantler closed this Aug 21, 2026
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Aug 21, 2026
@devantler
devantler deleted the claude/vault-backup-mirror-gid-3202 branch August 21, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant