Skip to content

feat(ci): a chart must run as the uid its image owns files as - #427

Merged
beyondnetPeru merged 1 commit into
developfrom
feat/guard-chart-image-uid
Aug 4, 2026
Merged

feat(ci): a chart must run as the uid its image owns files as#427
beyondnetPeru merged 1 commit into
developfrom
feat/guard-chart-image-uid

Conversation

@beyondnetPeru

Copy link
Copy Markdown
Contributor

The MCP chart pinned runAsUser: 1000 against an image that creates evolith at 1001 and chowns the corpus to it. A securityContext overrides the image's USER, so the process landed on the base image's node user:

EACCES: permission denied, open '.../rulesets/opa/policy.wasm'

policy.wasm is mode 600 owned by 1001. Dispatch requires both engines to allow, OPA erroring fail-closes, and every tools/call returned FORBIDDEN — 51 tools advertised, none executable.

Why a guard and not just the fix

That value is correct today because it was corrected by hand two days ago, and nothing was watching it. A hand-corrected value with no guard is one edit from the same outage, and the next person to meet it meets it in a cluster.

Nothing here could have caught it before. The test harness runs the MCP server in-process — no container, no securityContext — so the defect was unreachable from every suite in the repository. Only a live deploy showed it.

What it checks

All four pinned fields, not just runAsUser: runAsGroup and fsGroup too, because getting three right and one wrong reproduces the same failure through a different door (fsGroup alone decides who owns mounted volumes). A chart pinning nothing is reported, not passed — it inherits the image's USER, which is right today and unanchored tomorrow.

Watched failing, not declared able to

Fixture Result
Reintroduce the exact 1000/1001 shape 2 divergences, exit 1
Move only fsGroup 1 divergence, exit 1
Unit tests over the predicate 10/10, including a green case so a reject-everything predicate cannot masquerade as thorough

The first version failed for its own reason and not the repository's: its adduser regex required every token before -u to be a flag, but the username is positional (adduser -S evolith -u 1001 -G evolith), so it read all three real Dockerfiles as unparseable. Caught by running it; the shape is now pinned by a test.

Guard Result
42-validate-guard-denominators ✅ 74 guards classified, none unprotected
43-validate-guard-negative-fixtures ✅ 50/50 turned red on the empty fixture
ci-runner.mjs governance ✅ 17 scripts
gitleaks dir . ✅ exit 0

🤖 Generated with Claude Code

The MCP chart pinned `runAsUser: 1000` against an image that creates
`evolith` at 1001 and chowns the corpus to it. A securityContext
overrides the image's USER, so the process landed on the base image's
`node` user: `policy.wasm` is mode 600 owned by 1001, OPA got EACCES,
dispatch requires both engines to allow, and every `tools/call` returned
FORBIDDEN — 51 tools advertised, none executable.

That value is correct today because it was corrected by hand two days
ago, and nothing was watching it. This is the part that replaces the
hand: a hand-corrected value with no guard is one edit from the same
outage, and the next person to meet it meets it in a cluster.

Nothing here could have caught it before. The test harness runs the MCP
server IN-PROCESS — no container, no securityContext — so the defect was
unreachable from every suite in the repository and only a live deploy
showed it.

All four pinned fields are compared, not just `runAsUser`: `runAsGroup`
and `fsGroup` too, because getting three right and one wrong reproduces
the same failure through a different door — `fsGroup` alone decides who
owns mounted volumes. A chart pinning NOTHING is reported rather than
passed: it inherits the image's USER, which is right today and unanchored
tomorrow.

Watched failing, not declared able to:

  - reintroducing the exact 1000/1001 shape → 2 divergences, exit 1;
  - moving ONLY fsGroup → 1 divergence, exit 1;
  - 10 unit tests over the predicate, including a green case so a
    reject-everything predicate cannot masquerade as thorough.

The first version failed for its own reason and not the repository's: its
`adduser` regex required every token before `-u` to be a flag, but the
username is POSITIONAL (`adduser -S evolith -u 1001 -G evolith`), so it
read all three real Dockerfiles as unparseable. Caught by running it; the
shape is pinned by a test.

Guards: 42 classifies it (74 total, none unprotected), 43 sees it turn
red on the empty fixture (50/50), governance suite 17/17, gitleaks clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner August 4, 2026 17:13
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 0
  • New EN files needing ES translation: 0

Repository Coverage

Metric Value
Total EN files 527
Total ES files 501
Paired files 0
Coverage 0%

Good: All EN changes have ES counterparts.


Generated by GitHub Actions

@beyondnetPeru
beyondnetPeru merged commit 24b64d9 into develop Aug 4, 2026
32 checks passed
@beyondnetPeru
beyondnetPeru deleted the feat/guard-chart-image-uid branch August 4, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant