userEnvProbe (and --secrets-file) put container env values, incl. secrets, on the host `docker exec` argv
維護者通常 1 天內回覆
@v-Kaniska244 已經在處理了。
開始於 2026年10月6日。
評估
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 新手友好度
- 68/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- docker, typescript
研究方向
Start in src/spec-shutdown/dockerUtils.ts at toDockerExecArgs, then trace remoteEnv from probeUserEnv and the secrets merge in src/spec-common/injectHeadless.ts. Run the supplied Docker reproduction for both userEnvProbe and --secrets-file. Done means lifecycle hooks and devcontainer exec still receive their environment while secret values no longer appear in host docker exec argv.
由索引模型根據 Issue 內容生成。
描述
Summary
When userEnvProbe is enabled (the default, loginInteractiveShell), the CLI reads the container user's whole environment (cat /proc/self/environ) and passes every variable back to docker exec as a -e NAME=VALUE argument. This happens on every lifecycle hook (onCreateCommand, postCreateCommand, postStartCommand, …) and on every devcontainer exec.
So any secret that is already in the container's environment ends up in plaintext on the host process command line, where any local user can read it with ps. That includes secrets injected with runArgs: ["--env-file", …], containerEnv or an image ENV. Those variables are already part of the container's Config.Env, which docker exec inherits, so re-sending them on the command line gains nothing.
The --secrets-file feature has the same problem, even with "userEnvProbe": "none". Lifecycle commands merge the secrets into the exec env (const env = { ...(await remoteEnv), ...(await secrets) }), so each secret appears as -e NAME=VALUE on the host argv while the hook runs.
Where
On main (3e363f63a712d30a741174f6d1a7e75f47fe3fc1):
src/spec-shutdown/dockerUtils.tstoDockerExecArgs(~L400-415):Object.keys(env).forEach(key => execArgs.push('-e',${key}=${env[key]}))src/spec-common/injectHeadless.tsprobeUserEnv(~L777-789,cat /proc/self/environ) feedsremoteEnv.runLifecycleCommand(~L518) mergesremoteEnvandsecretsinto thatenv.- In the published 0.89.0 bundle (
dist/spec-node/devContainersSpecCLI.js) the same logic is the minified functionKN:r&&Object.keys(r).forEach(a=>g.push("-e",${a}=${r[a]})).
Repro (dummy values only)
@devcontainers/cli 0.89.0, Docker Desktop on macOS, image alpine:3.
mkdir probe && cd probe
umask 077
printf 'ARGV_PROBE_VAR=DUMMY_NOT_SECRET\n' > dummy.env
printf '{"image":"alpine:3","overrideCommand":true,"runArgs":["--env-file","%s/dummy.env"]}\n' "$PWD" > .devcontainer.json
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . sleep 6 &
sleep 3; ps -axww -o command | grep '^docker exec'
Observed (other values elided):
docker exec -i -u root -e HOSTNAME=… -e SHLVL=… -e HOME=… -e PAGER=… -e LC_COLLATE=… -e PATH=… -e LANG=… -e CHARSET=… -e ARGV_PROBE_VAR=DUMMY_NOT_SECRET -w /workspaces/probe <id> sleep 6
| Arm | docker exec argv contains ARGV_PROBE_VAR=DUMMY_NOT_SECRET |
Var visible inside the container |
|---|---|---|
default userEnvProbe |
yes (1 process) | yes |
"userEnvProbe": "none" |
no (0) | yes (inherited from Config.Env) |
--secrets-file arm: config {"image":"alpine:3","overrideCommand":true,"userEnvProbe":"none","postStartCommand":"sleep 8"} with --secrets-file secrets.json containing {"SECRETSFILE_PROBE_VAR":"DUMMY_NOT_SECRET_2"}. While the hook ran, the host showed docker exec -i -u root -e SECRETSFILE_PROBE_VAR=DUMMY_NOT_SECRET_2 -w /workspaces/probe-secretsfile <id> /bin/sh -c sleep 8.
We found this in a real setup where Doppler secrets were injected with --env-file. Every secret showed up in the host process table during devcontainer up and devcontainer exec.
Suggested fix
Any of these, in order of preference:
- Pass variables by name, not by value. Use
docker exec -e NAME(no=), so docker reads the value from the CLI process's own environment, and spawn thedockerchild with{ ...process.env, ...env }. Values never appear on argv. This also covers--secrets-fileandremoteEnv. - Or use
docker exec --env-file <tmpfile>, written mode 0600 and removed after the exec starts. - At minimum, don't re-send probed variables whose value already equals the container's
Config.Envvalue.docker execinherits those, so dropping them changes nothing. Only the delta the login shell adds would remain (e.g.PATH), which is rarely sensitive.
Workaround for users: set "userEnvProbe": "none" and run commands that need the login environment through bash -lc. This does not help with --secrets-file.
- 主要語言
- TypeScript
- 星號
- 3k
- 分支
- 462
- 平均合併
- 13 小時 28 分鐘
- 30 天內合併 PR
- 2
環境準備
在瀏覽器裡用你自己的 GitHub 帳號啟動這個專案的開發容器。
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
devcontainers/cli 的其他 Issue
-
難度 2/5 1-3 小時 新手友好度 82/100
devcontainers/cli#1325 ·
維護者通常 1 天內回覆
-
難度 1/5 1 小時以內 新手友好度 92/100
devcontainers/cli#1203 ·
維護者通常 1 天內回覆
-
難度 1/5 1-3 小時 新手友好度 68/100
devcontainers/cli#1178 · 1 則留言 ·
維護者通常 1 天內回覆
-
難度 3/5 半天 新手友好度 45/100
devcontainers/cli#1324 ·
維護者通常 1 天內回覆
-
`devcontainer build` fails on Buildx 0.37.2 because the generated Dockerfile is outside the Compose bake context可能已有人在做 @v-Kaniska244 於 2 天前認領。 未關閉
devcontainers/cli#1320 · 已指派 1 人 ·
維護者通常 1 天內回覆
查看 devcontainers/cli 的全部 Issue
相似的 Issue
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:other issue-rating: 🦞 diamond lobster no-stale P2
難度 2/5 1-3 小時 新手友好度 72/100
openclaw/openclaw#168089 · 2 則留言 · 1 個 reaction ·
維護者通常 1 天內回覆
-
✨ enhancement needs-discussion
難度 1/5 1 小時以內 新手友好度 85/100
-
[Bug]: [MCP/CLI] Bare loopback IP addresses (127.0.0.1:port) and hosts with ports fail to navigate due to erroneous scheme inference可能已有人在做 @alok-108 今天認領。 未關閉
難度 2/5 1-3 小時 新手友好度 78/100
microsoft/playwright#43263 ·
維護者通常 1 天內回覆
-
area:studio type:security
難度 2/5 1-3 小時 新手友好度 78/100
維護者通常 1 天內回覆
-
enhancement good first issue Stellar Wave trivial
難度 2/5 1-3 小時 新手友好度 85/100
StellarCanary/ProtocolCanary-Action#331 ·
維護者通常 1 天內回覆