You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security(k8s): three CKV_K8S_40 sites have no UID constraint β raise them or demonstrate oneΒ #2901
While dispositioning CKV_K8S_40 in #2898, three of the eleven sites turned out to have no
constraint on their UID at all. Each runs below UID 10000 by convention, and nothing in the
manifest requires it:
A /data emptyDir under fsGroup: 1000, which governs group ownership β so the UID itself is not what makes the volume writable.
The first draft of #2898 suppressed all three with reasons asserting shared file ownership. That was
wrong on inspection β none of them shares a volume with anything β so the suppressions were removed
rather than shipped with an invented justification.
Why this is worth doing rather than suppressing
Two of these three run on the local Docker provider, which CI brings up on every run, and the
third only calls an HTTP API. So unlike the rest of the CKV_K8S_40 backlog, this is the subset
where raising the UID is both plausible and verifiable in CI rather than requiring a production
change nobody can exercise.
Smallest useful change
Raise the mc Job first β it has no fsGroup, no shared volume, and the shortest feedback loop.
Then the umami CronJob (no volumes at all), then the MinIO server, which is the only one where the
image's own expectations about /data need checking.
Success signal
CKV_K8S_40 drops from 5 to 2 with the local Docker provider still coming up green in CI, and the
MinIO bucket-creation Job still completing. If a workload turns out to genuinely need its UID, that
is a real answer too β record it as a scoped skip under #2898's convention with the evidence.
Acceptance criteria
Each of the three either runs above UID 10000, or carries a scoped skip whose reason is
demonstrated rather than assumed.
The docker-provider CI run is green, and minio-create-bucket still completes.
.mega-linter.yml's recorded checkov count is refreshed.
#2344 - fix(security-context): pair pod-level runAsUser with injected runAsGroup (#2292 regression) [merged] #2437 - fix(cilium): run the cilium-operator as non-root [merged] #2455 - fix(security): harden containers with read-only root filesystems (Kubescape C-0017) [merged] #2857 - test(headlamp): measure the user-namespace mapping in prod [merged] #2860 - security(k8s): record an explicit service-account-token verdict per workload [merged]
π Issue Planner
Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.
Create Plan
π§ͺ Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your .coderabbit.yaml:
issue_enrichment:
auto_enrich:
enabled: false
π¬ Have feedback or questions? Drop into our discord!
Two more sites join this bucket β both from review, both measured
#2900's review round found two further CKV_K8S_40 sites whose stated constraint did not hold.
Both are now here rather than suppressed, bringing this issue to five sites.
It does not mount the vault-snapshots PVC. Its volume list is entirely emptyDir and secret; the snapshots volume is an emptyDir that the fetch-snapshot init container fills
from the R2/MinIO mirror β and two comments already in that file say exactly this ("read it WITHOUT mounting the RWO vault-snapshots PVC").
Nor is 100 an image requirement: the pinned quay.io/openbao/openbao:2.5.3 config reads User: "<unset>". What is true, read live from the cluster, is that the OpenBao StatefulSet
runs runAsUser: 100, runAsGroup: 1000, fsGroup: 1000 β which is why the two vault-backup jobs,
which do mount the claim, keep their disposition and this one does not.
Open question for whoever picks it up: vault-init and store-keys write to emptyDirs under fsGroup: 1000, so a higher UID is plausible β but OpenBao's docker-entrypoint.sh may expect
paths owned by 100. That needs exercising, not reasoning.
That is the distroless nonroot identity β 65532, already above the 10000 threshold. The manifest
simply sets no runAsUser, so checkov cannot see it. An explicit runAsUser: 65532 would match
what the container already does and clear the check outright.
β οΈThe catch, and why this one is not "just do it". This is the local Docker provider's cluster
DNS, and this repo runs no full-cluster system test on PRs β only static validation and a
Kubescape scan. If the assumption is wrong, the symptom is a cluster with no DNS, not a red check.
The container already grants NET_BIND_SERVICE and the distroless binary carries the capability on
the file, so it should hold β but "should" is the word that makes this need a cluster.
#2900 also corrected the false comment in that file, which had asserted the opposite for as long
as it existed.
Revised scope
Five sites, in ascending order of how much evidence is still needed:
Site
UID
What is still unknown
minio-create-bucket
1000
nothing obvious β no fsGroup, no shared volume, HTTP only
umami-provision-tenants
1001
nothing obvious β no volumes at all, HTTP only
minio (server)
1000
whether the image expects /data at its own UID
coredns
unset (image: 65532)
whether :53 still binds; needs a cluster
vault-config
100
whether OpenBao's entrypoint expects paths owned by 100
One premise of this issue is false β acceptance criterion 2 cannot be met as written
This issue argues the MinIO pair is the verifiable subset:
Two of these three run on the local Docker provider, which CI brings up on every run [β¦] this
is the subset where raising the UID is both plausible and verifiable in CI.
MinIO is not brought up by CI. It is an opt-in controller, commented out in the docker
provider's own kustomizations:
k8s/providers/docker/infrastructure/controllers/kustomization.yaml lists # - minio/ under "Opt-in controllers (uncomment what you want to try locally)", alongside velero/.
So acceptance criterion 2 β "the docker-provider CI run is green, and minio-create-bucket still
completes" β cannot be satisfied: that Job never runs in CI. The criterion is unmet rather than
skipped, and #2911 says so instead of implying a live exercise.
What replaced it
Static evidence read from the images themselves rather than reasoned about, which is what the
withdrawn suppressions got wrong:
Site
Image User
/etc/passwd entry for the UID it ran under
Disposition
minio/job.yaml (mc)
none (would run as root)
none
raised to 65532
minio/deployment.yaml
none (would run as root)
none
raised to 65532
umami/cron-job.yaml
nextjs
nextjs:x:1001:65533 β exact match
scoped skip
Both MinIO images are therefore already running under a UID their own image does not know about,
which is the same class of change as raising it. MinIO's entrypoint only switches user when MINIO_USERNAMEandMINIO_GROUPNAME are set; neither is set here, so it takes the plain exec branch and never looks the UID up.
The umami CronJob went the other way: 1001 turned out to be its image's baked user, so raising it
would break the match the image asks for. That is the one demonstrated constraint of the three, and
it is recorded as a scoped skip rather than a change.
Measured, with the repo's own scripts/megalinter-scan-counts.sh: checkov 37 β 34, CKV_K8S_40
11 β 8. trivy also moved 624 β 620, and that delta is attributable rather than drift β exactly two
targets changed (both MinIO files, 4 β 2 failures each), the other 457 kubernetes targets are
unchanged, and both runs ran the same 118 tests per target, so the rule set did not move.
Residual risk, stated plainly: because CI does not deploy MinIO, a UID mistake here would surface
the first time someone opts the stack in locally, not in this pull request. The blast radius is
bounded to that opt-in local/CI stand-in for R2 β it is not a production workload.
Activity
coderabbitai commentedon Aug 2, 2026
π Related PRs
#2344 - fix(security-context): pair pod-level runAsUser with injected runAsGroup (
#2292regression) [merged]#2437 - fix(cilium): run the cilium-operator as non-root [merged]
#2455 - fix(security): harden containers with read-only root filesystems (Kubescape C-0017) [merged]
#2857 - test(headlamp): measure the user-namespace mapping in prod [merged]
#2860 - security(k8s): record an explicit service-account-token verdict per workload [merged]
π Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.π§ͺ Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:π¬ Have feedback or questions? Drop into our discord!
security(k8s): drop three CKV_K8S_40 skips whose reason was not demonβ¦
security(k8s): drop two more CKV_K8S_40 skips and correct a false Corβ¦
devantler commentedon Aug 2, 2026
Two more sites join this bucket β both from review, both measured
#2900's review round found two further
CKV_K8S_40sites whose stated constraint did not hold.Both are now here rather than suppressed, bringing this issue to five sites.
k8s/bases/infrastructure/vault-config/job.yaml(Jobvault-config, UID 100)It does not mount the
vault-snapshotsPVC. Its volume list is entirelyemptyDirandsecret; thesnapshotsvolume is anemptyDirthat thefetch-snapshotinit container fillsfrom the R2/MinIO mirror β and two comments already in that file say exactly this ("read it
WITHOUT mounting the RWO vault-snapshots PVC").
Nor is 100 an image requirement: the pinned
quay.io/openbao/openbao:2.5.3config readsUser: "<unset>". What is true, read live from the cluster, is that the OpenBao StatefulSetruns
runAsUser: 100, runAsGroup: 1000, fsGroup: 1000β which is why the twovault-backupjobs,which do mount the claim, keep their disposition and this one does not.
Open question for whoever picks it up:
vault-initandstore-keyswrite toemptyDirs underfsGroup: 1000, so a higher UID is plausible β but OpenBao'sdocker-entrypoint.shmay expectpaths owned by 100. That needs exercising, not reasoning.
k8s/providers/docker/.../coredns/deployment.yaml(Deploymentcoredns)CoreDNS does not run as root. The pinned image's own config,
registry.k8s.io/coredns/coredns:v1.14.3@sha256:884b72ddβ¦, reads:{"User": "nonroot:nonroot", "Entrypoint": ["/coredns"]}That is the distroless nonroot identity β 65532, already above the 10000 threshold. The manifest
simply sets no
runAsUser, so checkov cannot see it. An explicitrunAsUser: 65532would matchwhat the container already does and clear the check outright.
DNS, and this repo runs no full-cluster system test on PRs β only static validation and a
Kubescape scan. If the assumption is wrong, the symptom is a cluster with no DNS, not a red check.
The container already grants
NET_BIND_SERVICEand the distroless binary carries the capability onthe file, so it should hold β but "should" is the word that makes this need a cluster.
#2900 also corrected the false comment in that file, which had asserted the opposite for as long
as it existed.
Revised scope
Five sites, in ascending order of how much evidence is still needed:
minio-create-bucketfsGroup, no shared volume, HTTP onlyumami-provision-tenantsminio(server)/dataat its own UIDcoredns:53still binds; needs a clustervault-configdocs(k8s): correct a probe comment that restated the refuted UID claim
docs(ci): drop an over-strong RWO claim and fix the CoreDNS issue attβ¦
devantler commentedon Aug 2, 2026
One premise of this issue is false β acceptance criterion 2 cannot be met as written
This issue argues the MinIO pair is the verifiable subset:
MinIO is not brought up by CI. It is an opt-in controller, commented out in the docker
provider's own kustomizations:
k8s/providers/docker/infrastructure/controllers/kustomization.yamllists# - minio/under"Opt-in controllers (uncomment what you want to try locally)", alongside
velero/.k8s/providers/docker/infrastructure/kustomization.yamllikewise keepsvault-backup/commentedout, noting it "needs controllers/velero + minio".
So acceptance criterion 2 β "the docker-provider CI run is green, and
minio-create-bucketstillcompletes" β cannot be satisfied: that Job never runs in CI. The criterion is unmet rather than
skipped, and #2911 says so instead of implying a live exercise.
What replaced it
Static evidence read from the images themselves rather than reasoned about, which is what the
withdrawn suppressions got wrong:
User/etc/passwdentry for the UID it ran underminio/job.yaml(mc)minio/deployment.yamlumami/cron-job.yamlnextjsnextjs:x:1001:65533β exact matchBoth MinIO images are therefore already running under a UID their own image does not know about,
which is the same class of change as raising it. MinIO's entrypoint only switches user when
MINIO_USERNAMEandMINIO_GROUPNAMEare set; neither is set here, so it takes the plainexecbranch and never looks the UID up.The umami CronJob went the other way: 1001 turned out to be its image's baked user, so raising it
would break the match the image asks for. That is the one demonstrated constraint of the three, and
it is recorded as a scoped skip rather than a change.
Measured, with the repo's own
scripts/megalinter-scan-counts.sh: checkov 37 β 34,CKV_K8S_4011 β 8. trivy also moved 624 β 620, and that delta is attributable rather than drift β exactly two
targets changed (both MinIO files, 4 β 2 failures each), the other 457 kubernetes targets are
unchanged, and both runs ran the same 118 tests per target, so the rule set did not move.
Residual risk, stated plainly: because CI does not deploy MinIO, a UID mistake here would surface
the first time someone opts the stack in locally, not in this pull request. The blast radius is
bounded to that opt-in local/CI stand-in for R2 β it is not a production workload.
docs(lint): make the CKV_K8S_40 breakdown add up
security(k8s): disposition the vault-config CKV_K8S_40 as image-baked
docs(lint): cite the demonstrated image-baked precedent, not the refuβ¦