Skip to content

security(umami): the provisioning CronJob runs as uid/gid 1001, below KSV-0020/0021's 10000 floorΒ #3221

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence

Measured on main @4d2a3dc4 with scripts/megalinter-scan-counts.sh --trivy-only (trivy 0.74.0,
scan rooted at the repository root):

trivy β€” 24 misconfigurations across 10 targets, 0 vulnerabilities
  9 KSV-0021 Β· 7 KSV-0020 Β· 3 KSV-01010 Β· 3 KSV-0011 Β· 2 KSV-0018

All 16 KSV-0020/0021 findings are first-party. 14 of them belong to #3202 β€” the three OpenBao
writers that share one RWO PVC and must be raised together. The remaining 2 are here:

file findings
k8s/bases/apps/umami/cron-job.yaml KSV-0020 Γ—1, KSV-0021 Γ—1

The pod already sets runAsUser: 1001, runAsGroup: 1001, fsGroup: 1001. KSV-0020/0021 are
"runs with UID/GID <= 10000", so the finding is that 1001 is low β€” not that it is unset. Raising it
is the whole change.

An earlier revision of this issue stated the cause as a missing runAsUser/runAsGroup across four
files. That was wrong on all four; the correction and the per-file measurement are in
this comment.

Why this is not a one-line edit

The umami provisioning CronJob runs ghcr.io/umami-software/umami, whose image bakes uid 1001. Raising
the pod to an arbitrary high UID only works if nothing in the image's filesystem is owned by 1001 in a
way the process needs to write, and if fsGroup covers whatever it does write. A wrong value fails the
provisioning run rather than erroring at apply time β€” the manifest validates either way.

Unlike the vault trio, umami shares no volume with another workload, so it can move on its own.

Acceptance criteria

  • Determine the UID/GID the umami image actually runs as, and what it writes, from the image
    config rather than assumption.
  • Raise runAsUser/runAsGroup above 10000, with fsGroup matching whatever it writes.
  • Demonstrate the provisioning CronJob still completes successfully after the change β€” a green
    apply is not evidence. This is the work; the diff is trivial.
  • trivy KSV-0020/0021 drop by exactly 2, measured with scripts/megalinter-scan-counts.sh --trivy-only.

Size

Small diff, medium verification cost. The verification needs the job actually run, and the agent has
read-only cluster access β€” which is why this stayed split out of #2787 rather than being folded into a
disposition PR.

Part of #2787

Activity

  1. moved this to πŸ“₯ Backlog in 🌊 Project Boardon Aug 18, 2026
  2. devantler commented on Aug 19, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Currency + one premise correction

    Independently re-measured on main @2199c715 with scripts/megalinter-scan-counts.sh --trivy-only. I reached this issue's exact per-file breakdown from a separate starting point, so the 16 stand confirmed:

    file findings KSV-0020 KSV-0021
    vault-config/job.yaml 6 2 4
    vault-backup/cron-job.yaml 4 2 2
    vault-backup/job.yaml 4 2 2
    umami/cron-job.yaml 2 1 1

    Tree total has moved: 45 β†’ 36, and β†’ 32 once #3231 lands (it dispositions the 4 vendored-bundle findings this issue correctly excludes). 0 CRITICAL, 0 HIGH. The 16 here are now half of everything trivy still reports on k8s/, so this issue is the critical path to removing REPOSITORY_TRIVY from DISABLE_ERRORS_LINTERS.

    One premise needs correcting

    they fire because the container sets runAsNonRoot: true but no explicit runAsUser/runAsGroup

    That is true for the three vault jobs, but not for umami. k8s/bases/apps/umami/cron-job.yaml pins a UID explicitly:

    securityContext:
      runAsNonRoot: true
      runAsUser: 1001
      runAsGroup: 1001
      fsGroup: 1001

    So umami is a deliberately low UID, not an absent one β€” a different problem needing a different fix. The vault jobs need a value chosen; umami needs an existing value changed, which is the riskier of the two because fsGroup: 1001 already governs volume ownership.

    Two additions worth folding into the acceptance criteria

    • vault-config/job.yaml is already known to be image-baked. It carries a scoped checkov.io/skip2 recording exactly that, and sets runAsUser per container instead. That is strong prior evidence for how at least those 6 resolve β€” worth confirming rather than re-deriving.
    • Any disposition added here should be asserted by a boundary test, the way scripts/tests/test-trivyignore-vendored-operator-boundary.sh asserts the vendored ones β€” otherwise it silently stops being path-scoped. That is the defect Three vendored-bundle trivy dispositions are outside the boundary test that is meant to keep them scopedΒ #3230 is about, and this issue would otherwise reintroduce it.
  3. added a commit that references this issue on Aug 19, 2026
  4. devantler commented on Aug 19, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Correction: the stated cause does not hold β€” every file named here already sets an explicit UID and GID

    Measured on main @4d2a3dc4 with this repository's own scripts/megalinter-scan-counts.sh --trivy-only
    (trivy 0.74.0, scan rooted at the repository root), plus a per-target JSON breakdown of the same scan.

    This issue says the findings fire because a container "sets runAsNonRoot: true but no explicit
    runAsUser/runAsGroup". That is not what the tree contains. All four files set both fields:

    file pod-level securityContext findings
    k8s/bases/infrastructure/vault-config/job.yaml runAsUser: 65532, runAsGroup: 1000, fsGroup: 1000 6
    k8s/bases/infrastructure/vault-backup/job.yaml runAsUser: 100, runAsGroup: 1000, fsGroup: 1000 4
    k8s/bases/infrastructure/vault-backup/cron-job.yaml runAsUser: 100, runAsGroup: 1000, fsGroup: 1000 4
    k8s/bases/apps/umami/cron-job.yaml runAsUser: 1001, runAsGroup: 1001, fsGroup: 1001 2

    KSV-0020/0021 are "runs with UID/GID <= 10000", not "UID/GID unset". The findings fire because the
    values are low, not because they are missing. So acceptance criterion 2 as written ("Set explicit
    runAsUser/runAsGroup") is already satisfied everywhere, and criterion 1 ("determine the UID
    each image actually runs as") is already answered in-tree: vault-config's own
    checkov.io/skip2 records that UID 100 is the openbao image's baked openbao:x:100:1000 user.

    In vault-config/job.yaml the findings are per-container overrides, not the pod default β€” the pod
    runs 65532, and four containers (fetch-snapshot, store-keys, vault-init, vault-config) restate
    the lower openbao identity. An implementer reading the current text would go looking for an absent
    field and find it present in all four files.

    Current counts, and why the vendored remainder is gone

    trivy β€” 24 misconfigurations across 10 targets, 0 vulnerabilities
      9 KSV-0021 Β· 7 KSV-0020 Β· 3 KSV-01010 Β· 3 KSV-0011 Β· 2 KSV-0018
    

    All 16 KSV-0020/0021 are now first-party and land in exactly the four files above (9 + 7 = 16,
    verified per target). The "remaining 4 in the vendored CDI/KubeVirt bundles" this issue mentions were
    dispositioned by #3220, so the split it describes no longer exists.

    Overlap with #3202 β€” 14 of the 16 are already owned

    #3202 (filed earlier, 01:12Z the same day) covers making the snapshot group-readable and then
    raising the writers' UIDs, and it correctly captures the constraint this issue does not: the three
    writers share one RWO PVC and must move together, because the snapshot lands -rw------- owned by
    100:1000 and minio/mc can only read it as its owner. That is vault-config/job.yaml,
    vault-backup/job.yaml and vault-backup/cron-job.yaml β€” 14 of the 16 findings.

    k8s/bases/apps/umami/cron-job.yaml (2 findings) is the only part not covered there. It shares no
    volume with the vault writers, so it can move independently.

    What this issue is being rescoped to

    Retitled and rewritten to cover umami only, with the vault trio left to #3202. The original
    premise is preserved here rather than silently overwritten.

    The verification bar is unchanged and is still the hard part: raising umami's UID has to be shown not
    to break the provisioning run, and this agent has read-only cluster access.

  5. changed the title [-]vault-backup, vault-config and umami jobs run without an explicit UID/GID[/-] [+]security(umami): the provisioning CronJob runs as uid/gid 1001, below KSV-0020/0021's 10000 floor[/+] on Aug 19, 2026
  6. self-assigned this
    on Aug 19, 2026
  7. added a commit that references this issue on Aug 19, 2026
  8. moved this from πŸ“₯ Backlog to πŸ‘€ In Review in 🌊 Project Boardon Aug 19, 2026
  9. moved this from πŸ‘€ In Review to πŸƒπŸ»β€β™‚οΈ In Progress in 🌊 Project Boardon Aug 19, 2026
  10. moved this from πŸƒπŸ»β€β™‚οΈ In Progress to πŸ‘€ In Review in 🌊 Project Boardon Aug 19, 2026
  11. moved this from πŸ‘€ In Review to βœ… Done in 🌊 Project Boardon Aug 19, 2026
  12. devantler commented on Aug 19, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Criterion 3 is met in production β€” observed, not inferred

    #3249 merged at 19:05:43Z. The acceptance criterion this issue called "the work" β€” demonstrate the
    provisioning CronJob still completes
    , explicitly noting a green apply is not evidence β€” is now
    satisfied by the real workload, read from the live cluster:

    job securityContext result ran
    umami-provision-tenants-29786100 (scheduled) uid/gid/fsGroup 1001 succeeded=1 19:00:00 β†’ 19:00:12Z
    umami-provision-tenants-bootstrap (post-deploy) uid/gid/fsGroup 65534 succeeded=1 19:02:31 β†’ 19:02:38Z

    That is a natural before/after pair two minutes apart on the same cluster against the same Umami API:
    the last run under the old ids succeeded, and the first run under the new ids succeeded too. The live
    CronJob spec now reads runAsUser/runAsGroup/fsGroup: 65534, so the change reconciled rather than
    merely merging.

    The container evidence in #3249 predicted this; it did not establish it. This does.

    One correction to the record

    A comment on #3249 said the merge_group deploy's inline tenant provisioning exercises this workload,
    citing the repository's own AGENTS.md. The πŸš€ Deploy to Prod job did run and succeed for
    pr-3249 β€” that part is confirmed from the jobs API β€” but I could not read its log (the log endpoint
    returned zero lines), so I never verified that it specifically provisioned umami. The claim was a
    reasonable inference from documentation, not a measurement, and it should not be read as one. The
    cluster evidence above stands on its own and does not depend on it.

    Closing the loop: this can move straight to Done rather than sitting in Verifying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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