Skip to content

vault-config's non-openbao containers get a high UID but inherit the openbao GID #3255

Closed

Description

@devantler

🤖 Generated by the Agentic Engineer

Evidence

k8s/bases/infrastructure/vault-config/job.yaml sets pod-level runAsUser: 65532,
runAsGroup: 1000, fsGroup: 1000, and then overrides runAsUser: 100 on only the two openbao
containers (vault-init, vault-config).

That per-container split is deliberate and well-reasoned. The manifest's own comment explains it:
a checkov.io/skipN directive is resource-scoped, so setting UID 100 at pod level would have
silenced CKV_K8S_40 for every container while minio/mc and alpine/k8s bake no uid-100
entry of their own — "an unjustified low-UID execution that this annotation then hid".

I verified the premise behind that split against the pinned digest rather than assuming it:

$ docker run --rm --entrypoint sh quay.io/openbao/openbao:2.5.3@sha256:fdc6da21... \
    -c "grep -E ^openbao /etc/passwd; grep -E :1000: /etc/group"
openbao:x:100:1000::/home/openbao:/sbin/nologin
openbao:x:1000:openbao

So 100:1000 is genuinely the image's baked identity, and the reasoning holds — for the UID.

It was not carried through to the GID. runAsGroup: 1000 is only ever set at pod level, so
fetch-snapshot (minio/mc) and store-keys (alpine/k8s) run with primary GID 1000 — the
openbao group — which their images no more define than they define uid 100. That is the same
class of unjustified low identity the UID split exists to prevent, one axis over.

trivy reports it as KSV-0021 on all four containers (lines 147, 202, 433, 473). Two of those four
are the image-baked openbao pair and are the accepted class; the other two are this gap.

Who this affects

Low severity and not a live compromise: both containers already run a high, non-root UID, and the
group grants no access beyond the shared emptyDir volumes. What it costs is that the file cannot
reach KSV-0021: 0, so #2787 cannot restore trivy as a hard gate while it stands — and a
path-scoped disposition cannot separate the two real findings from the two accepted ones, because
trivy scopes by file, not by line.

Expected behaviour

fetch-snapshot and store-keys carry an explicit high runAsGroup matching their high
runAsUser, leaving the openbao pair on their image-baked 100:1000.

Why this needs a proving run rather than a direct PR

runAsGroup sets the primary GID only — fsGroup is added to every container as a
supplementary group, so volume access survives the change. (The existing KSV-0021 disposition
in .trivyignore.yaml states this same distinction.)

The risk is file ownership, not access: fetch-snapshot downloads the snapshot that the
openbao vault-init container then reads. Files it creates would become group-owned 65532 instead
of 1000, so any mode without world-read would break vault-init. That is exactly the failure
#3202 found on the neighbouring vault-backup writers, where a 0440 file owned root:65534 read
fine as 65534 but gave EACCES as 1001.

So the change needs the created files' modes checked and a real Job run observed, which is also
why this is filed rather than shipped speculatively.

Acceptance criteria

  • fetch-snapshot and store-keys set an explicit runAsGroup above 10000.
  • The openbao containers still run 100:1000.
  • A real vault-config Job run completes: the snapshot fetch-snapshot writes is readable by
    vault-init, and store-keys still writes the openbao-unseal Secret.
  • trivy KSV-0021 on this file drops from 4 to 2, and the residual two are the openbao pair.

Rough size: small, gated on one observed Job run.

Part of #2787

Activity

  1. moved this to 📥 Backlog in 🌊 Project Boardon Aug 19, 2026
  2. coderabbitai commented on Aug 19, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #3249 - security(umami): raise the provisioning CronJob above the UID/GID floor [merged]
    #3203 - security(vault-backup): disposition the last two CKV_K8S_40 findings as one deferred exception [merged]
    #3246 - security(vault-backup): make the raft snapshot group-readable [merged]
    #3231 - chore(security): disposition KSV-0020/0021 on the vendored VM operators [merged]
    #3236 - security(k8s): disposition the 8 RBAC-breadth trivy findings [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!

  3. self-assigned this
    on Sep 1, 2026
  4. added a commit that references this issue on Sep 1, 2026
  5. devantler commented on Sep 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Closing as already delivered — every acceptance criterion is met at main

    This issue duplicates #3258 (CodeRabbit flagged the pair on 2026-08-19), which #3264
    "fix(vault-config): scope the low GID to the two openbao containers" closed on 2026-08-20. The
    fix landed while this issue stayed open, so I verified all four criteria against live state rather
    than inheriting that closure.

    AC1 — fetch-snapshot and store-keys run a primary GID above 10000 — met, by a better shape

    Stated as "set an explicit runAsGroup above 10000" per container; delivered as an explicit
    pod-level runAsGroup: 65532 that those two containers take, with the override moved onto the
    openbao pair instead. Same outcome with two fewer per-container blocks, and it makes the high
    identity the default rather than something each new container must remember to restate. Recording
    the deviation rather than quietly reading it as satisfied.

    AC2 — the openbao containers still run 100:1000 — met

    vault-init and vault-config each carry runAsUser: 100 / runAsGroup: 1000; fetch-snapshot
    and store-keys carry neither and inherit the pod default.

    AC3 — a real Job run completes — met, observed post-merge on the live cluster

    Recorded on #3258: the merge_group deploy for #3264 succeeded, 🩹 Heal Prod was skipped, and
    Job.openbao.vault-config reported succeeded=1 (04:05:28Z → 04:06:33Z, 65s) with the containers
    on differing primary GIDs — store-keys read /shared written by vault-init at gid 1000, and
    vault-init read /snapshots written by fetch-snapshot at gid 65532. That is the property this
    issue's "why this needs a proving run" section identified as the risk, and it held.

    Re-confirmed live today: all three openbao-* pods 1/1 Running (readiness requires unsealed), and
    the infrastructure Kustomization READY=True at the current revision. The Job object itself is
    gone, as ttlSecondsAfterFinished: 600 intends.

    AC4 — trivy KSV-0021 drops from 4 to 2, residual being the openbao pair — met, measured

    Scanning the raw manifest (trivy 0.74.0), with a paired negative control against the pre-fix blob
    49d88512^:

    revision KSV-0021 lines
    pre-fix (49d88512^) 4 147, 202, 433, 473 — exactly the four this issue cites
    main (731537ec) 2 212 vault-init, 486 vault-config — the openbao pair

    The control fires, so the measurement discriminates rather than merely agreeing. The repository then
    goes one step further than this criterion asked: #3264 added a path-scoped KSV-0021
    disposition on the image-baked-identity premise, asserted by
    scripts/tests/test-trivyignore-vault-config-identity-boundary.sh, so the dispositioned count is
    0 and #2787 is no longer held open by this file.

    No code change is needed. Closing as completed by #3264; the durable record lives on #3258.

  6. removed their assignment
    on Sep 1, 2026
  7. moved this from 📥 Backlog to ✅ Done in 🌊 Project Boardon Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for Security.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions