SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data is
a build-check warning; the build succeeds. Docker saw a build argument or environment
variable whose name looks like a secret, here API_PASSWORD. Pass the value as
a build secret with RUN --mount=type=secret instead, so it is available to one
step and is not written into the Dockerfile.
Build with --secret id=api_password,src=... and read the value from
/run/secrets/api_password inside the RUN step that needs it.
The error
1 warning found (use docker --debug to expand):
- SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "API_PASSWORD") (line 2)
The build exited with code 0. The terminal colour codes in the captured lines are left out.
The value in this test was the fake string fake-not-a-secret.
Why it happens
The Dockerfile declared the value as a build argument with a default:
FROM alpine
ARG API_PASSWORD=fake-not-a-secret
RUN echo "length ${#API_PASSWORD}"
The check reads the names of ARG and ENV instructions and warns
when one looks like a credential; the warning names the instruction it matched,
ARG "API_PASSWORD", and its line. Two more builds showed it goes by the name:
ARG API_PASSWORD=hello still raised the warning, and
ARG GREETING=fake-not-a-secret raised none.
Moving the value out of the Dockerfile does not make it safe. A build with a bare
ARG API_PASSWORD and --build-arg API_PASSWORD=fake-not-a-secret on
the command line raised the same warning, and docker history --no-trunc on the
resulting image showed the value in two layer entries:
RUN |1 API_PASSWORD=fake-not-a-secret /bin/sh -c echo "length ${#API_PASSWORD}" # buildkit
ARG API_PASSWORD=fake-not-a-secret
Anyone who can pull the image can read that. ARG and ENV are not
safe for secrets even when the value comes from --build-arg; use the secret
mount.
The fix
FROM alpine
RUN --mount=type=secret,id=api_password echo "length $(wc -c < /run/secrets/api_password)"
docker build --progress=plain --secret id=api_password,src=fix8-bld-q5fix/api_password.txt -t fix8-bld-q5fix fix8-bld-q5fix
The file api_password.txt held the same fake value. The build exited with code
0, printed no warning section, and the step printed length 17: the value was
readable at /run/secrets/api_password during that one step. The Dockerfile no
longer contains it, there is no ARG or ENV for the check to flag,
and docker history --no-trunc on this image showed the RUN step
without the value.
The secret is read from a file on the machine that runs the build, so keep that file out of
the repository and out of the build context. In this test it sat in the context folder only
because the value was fake; with a real value, point src= at a path outside the
project, such as a folder in your user profile that no repository tracks.
How it was reproduced
A three-line Dockerfile on alpine, built with
docker build --progress=plain -t fix8-bld-q5 fix8-bld-q5 on Docker Desktop with
engine 29.9.2 and BuildKit, then the two-line secret-mount version built with the command
above. The value, tags and folders are this test's own and are not real credentials.
Frequently asked
- Does SecretsUsedInArgOrEnv stop the Docker build?
- No. It is a warning and the build exits with code 0. It means an ARG or ENV name looks like a secret.
- How do I pass a password to docker build without ARG?
- Use a build secret: docker build --secret id=name,src=file, and in the Dockerfile RUN --mount=type=secret,id=name, reading the value from /run/secrets/name.
- Does the SecretsUsedInArgOrEnv check look at the value?
- No, it looks at the name. In the test ARG API_PASSWORD=hello raised the warning, while ARG GREETING with the fake password as its value raised none.
More decoded errors in the Fixes category, including the UndefinedVar warning about build arguments.