Skip to content

build/bake: BuildKit secrets support#248

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

build/bake: BuildKit secrets support#248
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.

The new build-secrets secret accepts a YAML object that maps BuildKit secret IDs to secret values. The build workflow parses that YAML and forwards each entry to docker/build-push-action through secret-envs. The bake workflow uses the same contract and appends *.secrets+=id=...,env=... overrides for docker/bake-action, which replaces existing same-ID Bake secret sources when they are already declared.

A caller can pass a literal multiline secret with YAML block syntax.

secrets:
  build-secrets: |
    npmrc: |
      //registry.npmjs.org/:_authToken=token
      always-auth=true

A caller can also pass GitHub secrets through toJSON(...) so multiline values and YAML-sensitive characters are preserved.

secrets:
  build-secrets: |
    npmrc: ${{ toJSON(secrets.NPMRC) }}
    aws_credentials: ${{ toJSON(secrets.AWS_CREDENTIALS) }}

Reusable workflows cannot accept arbitrary dynamic secret names, so this keeps the public contract stable while still allowing callers to provide any BuildKit secret ID they need.

The workflow does not accept file paths as secret payloads. Bake targets can still declare file-based secret sources for local docker buildx bake usage, and matching build-secrets entries override those sources with env-backed workflow-provided values during the reusable workflow run.

@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
envs[envName] = secret;
secretEnvs.push(`${id}=${envName}`);
});
core.setOutput('envs', JSON.stringify(envs));

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.

Are these setOutput calls really safe?

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 changed this so secret-bearing env values are exported with core.exportVariable(...) instead of being serialized through core.setOutput('envs', ...). The later env: ${{ fromJson(steps.prepare.outputs.envs || '{}') }} plumbing is gone from both build steps.

secret-envs remains an output in build.yml, but that only contains secret_id=ENV_NAME mappings, not secret values.

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.

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 I think the 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.

Comment thread .github/workflows/build.yml Outdated
try {
parsed = yaml.load(normalized, {schema: yaml.FAILSAFE_SCHEMA});
} catch (err) {
throw new Error(`Failed to parse build-secrets YAML: ${err.message}`);

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.

Can't this leak secret value? Same in bake.yml.

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.

Hum you right. Made some changes so malformed build-secrets YAML no longer reports err.message, which can include source snippets from the secret-bearing YAML. It now reports only:

Failed to parse build-secrets YAML at line X, column Y

GIT_AUTH_TOKEN: inpGitHubToken
};
const secretEnvs = ['GIT_AUTH_TOKEN=GIT_AUTH_TOKEN'];
Object.entries(buildSecrets).forEach(([id, secret], index) => {

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.

Do we need to verify that GIT_AUTH_TOKEN is not overwritten?

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.

Yes good catch, I added a guard that rejects a user-provided BuildKit secret ID of GIT_AUTH_TOKEN.

Comment thread .github/workflows/bake.yml Outdated
if (!parsed) {
return {};
}
if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {

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.

second !parsed is dead code here

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
@crazy-max crazy-max requested a review from tonistiigi July 9, 2026 10:32
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