144 lines
7.4 KiB
Markdown
144 lines
7.4 KiB
Markdown
# PR#166: The values of a build's variables no longer stand on the command line of werkdock and docker
|
|
|
|
Version: patch
|
|
|
|
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
|
|
|
|
## Problem
|
|
|
|
Werkator passes each variable of a sandboxed build as `-e KEY=VALUE` to `werkdock run`,
|
|
and as `--env KEY=VALUE` to `docker run`.
|
|
So every secret of a build stands in the argv of that process, e.g. the tokens of the ai-review build of werkrevisor.
|
|
Each process that can read `/proc/<pid>/cmdline` sees them, and so does each `ps`, crash report or log of an argv.
|
|
On mih09 on 2026-10-20 they leaked from `pgrep -af` into a transcript.
|
|
|
|
## Non-Goals
|
|
|
|
- No change of what a build sees: the same variables with the same values, the same precedence of `werkdock.env`/`docker.env`.
|
|
- No change of `bwrap`'s command line;
|
|
werkdock passes the values on as `bwrap --setenv` up to 0.13.1, and werkdock PR#17 ends that.
|
|
- No rotation of the leaked secrets; that is the human's.
|
|
|
|
## Specification
|
|
|
|
### Feature: a build's variables reach the sandbox off the command line
|
|
|
|
#### Background
|
|
|
|
- werkdock takes a bare `-e KEY` from its own environment
|
|
and omits the variable when that has none (since 0.2.0).
|
|
- docker reads `--env-file`: one `KEY=VALUE` per line, before the `--env` operands, a later entry winning.
|
|
|
|
#### Scenario#166.01: the werkdock command line names a build's variables, werkdock's environment holds their values
|
|
|
|
… so that no `ps` and no log of an argv shows a secret of a build.
|
|
|
|
- **Given** a build with a token and a whole configuration file in `werkdock.env`
|
|
- **and** a server with a secret of its own in its environment
|
|
- **When** Werkator starts the build
|
|
- **Then** the argv of `werkdock run` names each variable as a bare `-e KEY`, and holds none of the values
|
|
- **and** werkdock's environment holds the values,
|
|
and of the server's environment only what werkdock reads itself
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerUnitTest.kt)
|
|
- [RuntimeEnvironmentUnitTest](../../src/test/kotlin/de/javagil/werkator/build/RuntimeEnvironmentUnitTest.kt)
|
|
|
|
#### Scenario#166.02: a variable that steers werkdock itself stays on the command line
|
|
|
|
… so that a build cannot change how werkdock runs outside the sandbox.
|
|
|
|
- **Given** a build that sets `PATH`, `WERKDOCK_HOME` and `LD_PRELOAD` in `werkdock.env`
|
|
- **When** Werkator starts the build
|
|
- **Then** the command line holds each as `-e KEY=VALUE`, as before
|
|
- **and** werkdock runs with the server's values of these three
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerUnitTest.kt)
|
|
- [RuntimeEnvironmentUnitTest](../../src/test/kotlin/de/javagil/werkator/build/RuntimeEnvironmentUnitTest.kt)
|
|
|
|
#### Scenario#166.03: the build still sees the values
|
|
|
|
… so that a build that needs its secrets keeps working.
|
|
|
|
- **Given** a build with a key and an empty value in `werkdock.env`
|
|
- **When** Werkator starts the build
|
|
- **Then** the command in the sandbox sees both values
|
|
- **and** werkdock sees none of the server's own secrets
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerIntegrationTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerIntegrationTest.kt)
|
|
|
|
#### Scenario#166.04: a Docker build's values reach the container through a file only the server's account reads
|
|
|
|
… so that neither `ps` nor a program the docker client starts on the host sees them.
|
|
|
|
- **Given** a build with a token in `docker.env`
|
|
- **When** Werkator starts the build
|
|
- **Then** the command line of `docker run` holds no value of the build
|
|
- **and** only the server's account reads the file that holds them, and only until the build ends
|
|
- **and** a value that docker's file format cannot hold stays on the command line, as before
|
|
|
|
##### Verified by
|
|
|
|
- [DockerBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt)
|
|
- [DockerEnvFileUnitTest](../../src/test/kotlin/de/javagil/werkator/build/DockerEnvFileUnitTest.kt)
|
|
|
|
## Solution
|
|
|
|
- werkdock: `RuntimeEnvironment` splits a build's variables into the `-e` operands and the environment of `werkdock run`,
|
|
which `WerkdockBuildRunner` starts with exactly that environment, cleared first.
|
|
It holds the server's values of werkdock's own names, then the build's values;
|
|
before, werkdock inherited all of the server's environment.
|
|
- werkdock's own names: what werkdock, the Go runtime or glibc reads, e.g. `PATH`, `HOME`, `WERKDOCK_HOME`,
|
|
`LD_PRELOAD`, `GCONV_PATH`; the full list is in `docs/configuration.md`.
|
|
A build variable of such a name keeps `KEY=VALUE` on the command line, as before.
|
|
In werkdock's environment it would steer werkdock: a branch could choose the `bwrap` it runs, its image store,
|
|
or a library loaded into a process outside the sandbox.
|
|
A name with `=` or an empty name stays on the command line, since no environment can hold it.
|
|
- The environment route is safe for werkdock alone: `werkdock run` starts no host program but itself and `bwrap`,
|
|
and werkdock PR#18 starts `bwrap` with an empty environment.
|
|
- Docker: `DockerEnvFile` writes the variables, after the container defaults `HOME` and `GRADLE_USER_HOME`,
|
|
to a `0600` file in `.git/werkator/docker-env/` (`0700`), which the build container sees masked.
|
|
The file goes when the client ends;
|
|
a file older than an hour, left by a killed Werkator, goes with the next Docker build.
|
|
The docker client keeps Werkator's environment, as before, and gets no build value in it.
|
|
- Why not the environment for Docker too: the Codex review found that the docker client starts host programs with it,
|
|
`ssh` for an `ssh://` daemon (`SSH_ASKPASS`) and credential helpers such as `pass` (`BASH_ENV`).
|
|
A list of forbidden names never covers every helper; the human chose the file (2026-10-20).
|
|
- docker's file format holds one line per variable, shorter than 64 KiB:
|
|
a value with a line break, a longer line, or a name docker would misread
|
|
stays `--env KEY=VALUE` on the command line, after the file, as before.
|
|
- `SecretFiles.createDirectoriesOwnerOnly` takes a directory that another thread created in the meantime,
|
|
so two Docker builds that start together both get their file (review of the Codex agent).
|
|
- Red/green: with `KEY=VALUE` operands back, the tests of Scenario#166.01 fail.
|
|
- Werkator and werkdock can be rolled out in either order; the leak ends with both.
|
|
- The native runner already puts the variables into the build's environment,
|
|
and the follow-up builds run through the same runners.
|
|
- Version 1.43.6 with a release-notes entry.
|
|
|
|
## Open Questions
|
|
|
|
- [x] Does anything else of Werkator put a build's value on a command line?
|
|
No: the image builds, `werkdock images`/`import` and `curl` carry no build variable.
|
|
|
|
## Additional Changes
|
|
|
|
- `docs/configuration.md`, the architecture skill and `docs/werkator.werkbaum` describe the change.
|
|
|
|
## Prerequisite PRs
|
|
|
|
- None; werkdock has taken a bare `-e KEY` since 0.2.0.
|
|
|
|
## Follow-up PRs
|
|
|
|
- [werkdock PR#17](https://git.javagil.de/mi/werkdock/pulls/17):
|
|
bwrap reads the sandbox's environment from a pipe, not from its command line;
|
|
an instance's files are readable by the owner only.
|
|
- [werkdock PR#18](https://git.javagil.de/mi/werkdock/pulls/18): bwrap starts with an empty environment.
|