buildkit icon indicating copy to clipboard operation
buildkit copied to clipboard

Dockerfile: Allow mounting secrets directly into env vars

Open rittneje opened this issue 4 years ago • 12 comments

This is a followup to the discussion touched on back in #1703. Currently buildctl supports pulling secrets from env vars via --secret id=xyz,env=ENV_VAR_NAME. However, in order to populate the corresponding env var within RUN, we have to do RUN --mount=type=secret,id=xyz ENV_VAR_NAME="$(cat /run/secrets/xyz)" ....

Not only is having to run cat every time annoyingly repetitive, it doesn't really work well in conjunction with the required=false mount option, since cat will fail due to the file not existing, so even more complex boilerplate is needed.

In addition, if multiple commands need the environment variable, we must explicitly export it, which is a pain because it requires even more boilerplate to be properly safe:

RUN --mount=type=secret,id=pip-index-url \
    PIP_INDEX_URL="$(cat /run/secrets/pip-index-url)" && \
    export PIP_INDEX_URL && \
    pip3 install ...

I propose adding an env option to --mount, which is mutually exclusive with dst. If provided, it contains the name of the environment variable that will be set to the value of the secret in question (and no file will be mounted at /run/secrets). If the mount is not required and the secret does not exist, then the environment variable will not be set at all (rather than having an empty value).

For example:

RUN --mount=type=secret,id=pip-index-url,env=PIP_INDEX_URL pip3 install ...

rittneje avatar May 20 '21 17:05 rittneje

I thought this was easy and added to milestone but actually this can't be done in Dockerfile frontend as envs with the secret values would leak to build cache.

So only way to add this is to add a special option to llb.Exec op and it would be a non-backward compatible change.

tonistiigi avatar Jul 07 '21 01:07 tonistiigi

@tonistiigi Can you expand upon this? Why is this different with respect to the cache than the existing secret file mounting feature? Or do you mean to say the existing feature leverages a special llb.Exec option?

Also, what is the specific nature of the "non-backward compatible change"? Is it something between buildctl (the command line) and buildkitd (the daemon)? Or something else?

rittneje avatar Jul 07 '21 01:07 rittneje

Yes, secret mount is a special mount type in llb.Exec.

Also, what is the specific nature of the "non-backward compatible change"?

When we can make a change that only touches the frontend it means that older versions of buildkit will still be able to build Dockerfiles with new syntax. With LLB changes Dockerfile will not build unless BuildKit has been updated to support new LLB feature.

tonistiigi avatar Jul 07 '21 02:07 tonistiigi

Wouldn't this kind of compatibility issue inevitably arise when trying to use any new kind of mount, not just this change in particular? For example, if --mount=type=ssh didn't exist yet and was to be introduced in v0.9.0, anyone who tried to build such a Dockerfile with an earlier version of BuildKit would encounter a failure (since llb.AddSSHSocket would not exist).

By the way, if it would make things simpler in the implementation, I personally would be fine having this new feature be something like --mount=type=secretenv, if only to avoid confusion over which options (such as uid and gid) can be meaningfully specified. Not that that helps at all with the compatibility issue.

rittneje avatar Jul 07 '21 02:07 rittneje

Wouldn't this kind of compatibility issue inevitably arise when trying to use any new kind of mount,

Yes, I'm not saying that we can never add this. Just that it is more complicated than what I initially assumed.

tonistiigi avatar Jul 07 '21 03:07 tonistiigi

Another example is Go's GOPROXY variable, which when used via CI/CD from github/circleci/etc generally includes auth credentials, so needs to be a secret in the build.

cep21 avatar Dec 21 '21 17:12 cep21

LLB side of this is implemented in #2579

tonistiigi avatar Jan 25 '22 07:01 tonistiigi

@rittneje

... since cat will fail due to the file not existing, so even more complex boilerplate is needed.

Can you share this boilerplate please?

kevcube avatar Apr 29 '22 07:04 kevcube

@kevcube It's in the example I shared.

RUN --mount=type=secret,id=pip-index-url \
    PIP_INDEX_URL="$(cat /run/secrets/pip-index-url)" && \
    export PIP_INDEX_URL && \
    pip3 install ...

rittneje avatar Apr 29 '22 11:04 rittneje

@tonistiigi What still needs to be done for this feature to be fully realized?

rittneje avatar Jun 20 '22 01:06 rittneje

ping @tonistiigi

rittneje avatar Aug 22 '22 03:08 rittneje

What still needs to be done for this feature to be fully realized?

Dockerfile frontend would need to be able to parse it from the flag and do the correct LLB calls. For testing all this can be done with an external frontend image.

tonistiigi avatar Aug 24 '22 23:08 tonistiigi

@tonistiigi any update on this?

rittneje avatar Aug 07 '23 03:08 rittneje