Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

userEnvProbe (and --secrets-file) put container env values, incl. secrets, on the host `docker exec` argv

Aberta
#1,317 0 comentários 0 reações 1 responsável Ver no GitHub

Mantenedores costumam responder em até 1 dia

@v-Kaniska244 já está trabalhando nisso.

Desde 6/10/2026.

Avaliação

Dificuldade
4/5
Tempo estimado
3-5 dias
Facilidade para iniciantes
68/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Ativa
Stack de tecnologia
docker, typescript
Domínio
cli, devops, security

Direção de pesquisa

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.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

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.ts toDockerExecArgs (~L400-415): Object.keys(env).forEach(key => execArgs.push('-e', ${key}=${env[key]}))
  • src/spec-common/injectHeadless.ts probeUserEnv (~L777-789, cat /proc/self/environ) feeds remoteEnv. runLifecycleCommand (~L518) merges remoteEnv and secrets into that env.
  • In the published 0.89.0 bundle (dist/spec-node/devContainersSpecCLI.js) the same logic is the minified function KN: 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:

  1. 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 the docker child with { ...process.env, ...env }. Values never appear on argv. This also covers --secrets-file and remoteEnv.
  2. Or use docker exec --env-file <tmpfile>, written mode 0600 and removed after the exec starts.
  3. At minimum, don't re-send probed variables whose value already equals the container's Config.Env value. docker exec inherits 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.

Linguagem predominante
TypeScript
Estrelas
3k
Forks
463
Merge médio
13h 28min
PRs com merge (30d)
2

Preparar o ambiente

Abrir no Codespaces

Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de devcontainers/cli

Todas as issues de devcontainers/cli

Issues semelhantes

Mais issues de TypeScript

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.