Skip to content

feat: auto-discover webhook-injected SA token mounts in proxy - #8

Merged
vercel-eddie merged 1 commit into
mainfrom
eddie/fuse-mount-discovery
May 19, 2026
Merged

vercel-eddie merged 1 commit into
mainfrom
eddie/fuse-mount-discovery

Conversation

@vercel-eddie

Copy link
Copy Markdown
Collaborator

Why

The proxy's `--mount-roots` flag is derived from the source Deployment spec (`containerMountPaths` in `pkg/k8s/resources/resources.go`), so it misses any volume the kubelet or an admission webhook adds at pod-creation time — most notably:

  • `/var/run/secrets/kubernetes.io/serviceaccount` (kubelet default SA auto-mount)
  • `/var/run/secrets/eks.amazonaws.com/serviceaccount` (EKS Pod Identity / IRSA)
  • `/var/run/secrets/azure/tokens` when injected by Azure Workload Identity (vs. specified in the spec, as api-devbox does today)

The devcontainer-side mount list (`podVolumeMountPaths` in `pkg/admin/local.go`) already reads the live pod, so the interceptor obediently FUSE-mounts every webhook-injected path locally. But the proxy's `authorize()` only allows paths under the smaller spec-derived root list, so every `Stat`/`Read` against a webhook path returns `PermissionDenied` and surfaces in the devcontainer as a hard-to-diagnose `EACCES` (e.g. AWS SDK reading the IRSA token).

This PR closes the asymmetry on the proxy side without coupling to any specific webhook: parse `/proc/self/mountinfo` at startup and append every tmpfs mount under `/run/secrets/` or `/var/run/secrets/` to the allowed roots. Both `/run/...` and `/var/run/...` aliases are registered so prefix-matching authorizes whichever form the client sends.

Validation

  • `go test ./...` passes (incl. new `TestParseSecretMountLine` covering eks/k8s/azure lines, non-tmpfs rejection, the `/var/run` alias form, and lines with optional mountinfo fields)
  • Manually verified the failure mode against a live api-devbox bridge: interceptor `BRIDGE_MOUNT_PATHS` had 8 paths (incl. EKS + kube-api SA tokens), proxy registered only 5, and `AssumeRoleWithWebIdentity` on DynamoDB failed with `EACCES open '/var/run/secrets/eks.amazonaws.com/serviceaccount/token'`. With this change, `DiscoverSecretMounts` returns the missing tmpfs mounts from the proxy pod's `/proc/self/mountinfo` and they're appended to `mountRoots` at startup.

🤖 Generated with Claude Code

The proxy's --mount-roots argument is derived from the source Deployment
spec (containerMountPaths in pkg/k8s/resources/resources.go). That misses
mounts added at pod-admission time by webhooks — kubelet auto-mount of
the default SA token at /var/run/secrets/kubernetes.io/serviceaccount,
EKS Pod Identity at /var/run/secrets/eks.amazonaws.com/serviceaccount,
and similar webhook-injected projected volumes.

The devcontainer-side mount list (podVolumeMountPaths in pkg/admin/local.go)
already comes from the live pod and so includes the webhook mounts. The
result is asymmetric: the interceptor FUSE-mounts every webhook path
locally, but every Stat/Read against those paths is rejected by
authorize() with "not within any allowed mount root", which surfaces in
the devcontainer as "Permission denied" (and a hard-to-diagnose EACCES
for any SDK that reads the IRSA token from disk).

Close the gap by having the proxy parse /proc/self/mountinfo at startup
and append every tmpfs mount under /run/secrets/ or /var/run/secrets/
to its allowed roots. Both /run/... and /var/run/... aliases are
registered so authorize() prefix-matches whichever form the client
sends.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel-eddie
vercel-eddie marked this pull request as ready for review May 19, 2026 23:19
@vercel-eddie
vercel-eddie merged commit 0511497 into main May 19, 2026
4 of 5 checks passed
@vercel-eddie
vercel-eddie deleted the eddie/fuse-mount-discovery branch May 19, 2026 23:19
@vercel-eddie
vercel-eddie restored the eddie/fuse-mount-discovery branch May 20, 2026 17:03
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