Skip to content

build/bake: secrets support - #248

Open
crazy-max wants to merge 1 commit into
docker:mainfrom
crazy-max:buildkit-secrets
Open

crazy-max wants to merge 1 commit into
docker:mainfrom
crazy-max:buildkit-secrets

Conversation

@crazy-max

@crazy-max crazy-max commented Jul 7, 2026

Copy link
Copy Markdown
Member

fixes #40
closes #239

This adds BuildKit secret support to the build and bake reusable workflows through a shared build-secrets workflow secret. It accepts a YAML object mapping BuildKit secret IDs to secret values, without requiring callers to reference internal environment variable names.

Callers can use YAML block syntax for literal multiline values or toJSON(...) to preserve GitHub secret values and YAML-sensitive characters.

secrets:
  build-secrets: |
    npm.token: ${{ toJSON(secrets.NPM_TOKEN) }}
    aws.credentials: ${{ toJSON(secrets.AWS_CREDENTIALS) }}
    inline_config: |
      first line
      second line

For literal values that must retain trailing blank lines, use |+ on both the outer build-secrets block and the inner value.

For Bake, flat entries apply to the workflow's target input. Nested mappings select a specific target within the resolved build graph. Dots remain literal characters in secret IDs.

with:
  target: default
secrets:
  build-secrets: |
    npm.token: ${{ toJSON(secrets.NPM_TOKEN) }}
    default:
      aws.credentials: ${{ toJSON(secrets.AWS_CREDENTIALS) }}

Bake secrets must already be declared in the target definition, for example:

target "default" {
  secret = [
    "id=npm.token,env=NPM_TOKEN",
    "id=aws.credentials,src=./aws-credentials",
  ]
}

The workflows mask each value and write it to a private temporary file on the runner. The build workflow passes these files through docker/build-push-action's secret-files input. The bake workflow uses Buildx's target.secret.<id>=src=... overrides to replace existing secret sources without introducing new secret declarations.

The input accepts secret values, not caller workspace paths. Bake definitions can retain local file or environment sources for direct docker buildx bake usage; matching workflow entries override those sources during the reusable workflow run.

Secret files are created after registry authentication and removed immediately after the build, including on failure. Secret values are not exported to the job-wide environment, and Git authentication remains scoped to the consuming step. Abrupt runner termination can prevent cleanup.

@crazy-max
crazy-max marked this pull request as ready for review July 7, 2026 11:02
@crazy-max
crazy-max requested a review from a team as a code owner July 7, 2026 11:02
@crazy-max
crazy-max requested a review from tonistiigi July 7, 2026 11:02
@crazy-max crazy-max mentioned this pull request Jul 7, 2026
Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/bake.yml Outdated
Object.entries(buildSecrets).forEach(([id, secret], index) => {
const envName = toBuildSecretEnvName(id, index);
envs[envName] = secret;
secretOverrides.push(`*.secrets+=id=${id},env=${envName}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this adding secrets to the definition? I would think secrets need to be defined already and just values are loaded here.

@crazy-max crazy-max Jul 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dug into this more and secrets+= override is intentional here.

For Bake, target.secret defines both the BuildKit secret ID and the source for that secret. If a caller has secret = ["id=foo,env=MY_FOO"], only exporting another env var from the workflow does not change what Bake uses. Using *.secrets+=id=foo,env=<internal env> replaces the existing same-ID secret source instead of duplicating it, which I verified with buildx bake --print.

That keeps the reusable workflow contract as build-secrets: { foo: value } and avoids making callers reference github-builder internal env names in their Bake files. It also lets existing Bake targets keep local sources for direct docker buildx bake usage, while the reusable workflow overrides those sources when a matching build-secrets entry is provided.

I updated the docs to avoid saying file-based secrets are supported as workflow payloads. The workflow still accepts secret values only and exposes them to BuildKit from env vars. A Bake target can still declare a file source for local use, for example type=file,id=aws,src=${HOME}/.aws/credentials, and the reusable workflow overrides that source when build-secrets contains aws.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't specifically have a problem with += but that this can be used to define new secrets (to specific or any target), even if they were not defined in the bake file. I think the secrets that are passed to build should be defined in bake definition and can't be just injected. I'm ok with having a way to change the source of the secret to something else, but I think that is confusing if there is a bake target that defines only secret foo and suddenly build runs where bar, baz etc are passed to the run and git/http steps. Especially if it is accidental leak from another target.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed extra commit to enforce this in the workflow.

The prepare job now records the secret IDs declared by the resolved Bake target graph. Before adding any target.secrets+=id=...,env=... override, the build job validates that the target is part of the resolved Bake build and that the secret ID is already declared by that target.

So build-secrets now only provides the value/source for a declared Bake secret. If a caller passes an undeclared secret ID, the workflow fails instead of injecting it into the target.

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/bake.yml Outdated
@crazy-max
crazy-max requested a review from tonistiigi July 9, 2026 10:32
@crazy-max crazy-max changed the title build/bake: BuildKit secrets support build/bake: secrets support Jul 21, 2026
@crazy-max
crazy-max force-pushed the buildkit-secrets branch 3 times, most recently from 0543877 to e69b8f7 Compare August 13, 2026 13:54
@crazy-max
crazy-max force-pushed the buildkit-secrets branch 4 times, most recently from c3af07e to 9cc390e Compare September 18, 2026 09:03
Accept a shared YAML build-secrets mapping and pass values through private temporary files. Scope Git credentials to the build action and remove secret files after the build, including on failure.

Override only declared Bake secrets in the resolved target graph using Buildx secret source overrides. Cover multiline values and replacement of environment and file sources in the workflow fixtures.

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Secrets support

2 participants