devcontainers / devcontainers/cli

Dockerfile `ENV` mutates system-wide `/etc/environment`

Open
#1,231 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
3k
Forks
457
Avg merge
13h 17m
Merged PRs (30d)
6

Description

Problem: Extremely surprising that DevContainers mutates system-wide `/etc/environment` at all.

This is a pretty insane architectural choice for a few reasons:

1. fails rule of lease surprises
2. is a breaking-change compared to other container runners, doesn't match deployment of a target container
3. major security concern for per-user secrets, or build-time only secrets, getting injected into the system environment where every user (e.g. daemon web applications, databases, etc.) now potentially has these secrets in their environment

Reproduce this issue, create a Dockerfile:

```Dockerfile
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

# Layer 1: Locale + Timezone
RUN apt-get update && apt-get install -y --no-install-recommends \
locales tzdata \
&& sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
&& locale-gen \
&& ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime \
&& dpkg-reconfigure -f noninteractive tzdata \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 TZ=America/Los_Angeles

# Switch to picard user for all per-user tool installs
USER picard
WORKDIR /home/picard

# Layer: Rust (stable via rustup)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \
-y --default-toolchain stable --profile default
ENV PATH="/home/picard/.cargo/bin:$PATH"
```

Just opening a container using VsCode will edit the `/etc/environment`:

https://github.com/devcontainers/cli/blob/65f98a518a1f62355a08e6f38e9d6bfb9a0d8ac9/src/spec-common/injectHeadless.ts#L750-L761

```sh
# Open a shell inside the container
docker compose exec claude-dev zsh
```

From the container:

```console
cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"

ANTHROPIC_API_KEY="sk-ant-your-key-here"
OPENAI_API_KEY="sk-your-key-here"
GEMINI_API_KEY="your-key-here"
PATH="/home/picard/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
DEBIAN_FRONTEND="noninteractive"
LANG="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
TZ="America/Los_Angeles"
```

In this repro, it means that every user's Rust bin will be served from the `picard` user's home directory. That's just not how this should work.

I don't believe this is the intention for the `ENV` directive of a Dockerfile!
https://docs.docker.com/reference/dockerfile/#env

When you run `docker exec`, the container runtime reads the same image config and passes the `ENV` vars via `execve` to the new process, regardless of which user it runs as (`--user`). So every docker exec session gets them. But this is very different than injecting it into `/etc/environment` where the change is persisted for any user logging into the container (e.g. `ssh`) and not limited to `docker exec`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.