Repository navigation
fix(bake): grant permissions for configured build inputs - #14325
Conversation
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
| local = append(local, path) | ||
| } | ||
| } | ||
| if buildConfig.Dockerfile != "" && buildConfig.DockerfileInline == "" && |
There was a problem hiding this comment.
[low] Dockerfile directory grant may not deduplicate against a relative context path
When the Dockerfile lives inside a relative context (e.g. context: ./app, dockerfile: Dockerfile), dockerFilePath resolves and EvalSymlinks the path to an absolute directory like /abs/app, but buildConfig.Context was already appended as the raw relative string ./app. Because slices.Compact(slices.Sorted(...)) deduplicates by string equality, both --allow fs.read=./app and --allow fs.read=/abs/app are emitted. Duplicate/overlapping grants are not harmful — buildx accepts them — but the stated intent of the deduplication is not fully achieved for this common case. The existing test uses contextDir = filepath.Join(root, "app") (already absolute), so the relative-path variant is not exercised.
Consider normalising buildConfig.Context with filepath.Abs before appending it to paths, or normalising all collected paths at the end of localBuildPaths before returning them.
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
There was a problem hiding this comment.
Compose loader resolves local build contexts to absolute paths before this function runs, so context: ./app won't normally reach this code as ./app. Also, filepath.EvalSymlinks doesn't inherently turn relative paths into absolute ones. The deduplication here removes identical grants; symlink aliases can still produce overlapping grants, but those are harmless. I don't think we need another normalization step here.
| if buildConfig.Dockerfile != "" && buildConfig.DockerfileInline == "" && | ||
| (!isRemoteContext(buildConfig.Context) || filepath.IsAbs(buildConfig.Dockerfile)) { | ||
| // Include the directory so Dockerfile-specific ignore files are covered. | ||
| local = append(local, filepath.Dir(dockerFilePath(buildConfig.Context, buildConfig.Dockerfile))) |
There was a problem hiding this comment.
[low] Empty context may cause filepath.Dir(dockerFilePath("", dockerfile)) to produce ., granting --allow fs.read=.
When buildConfig.Context is an empty string:
isRemoteContext("")returnsfalse(no://, not detected as git/remote), so the guard on line 369 does not exclude the empty-context case.dockerFilePath("", "Dockerfile")computesfilepath.Join("", "Dockerfile")="Dockerfile"(a bare filename).filepath.Dir("Dockerfile")="."."."is appended tolocal, emitting--allow fs.read=.— granting read access to the entire current working directory, which is broader than intended.
In practice Compose likely populates Context from the project directory before this function is reached, but the condition has no explicit guard for the empty-context case. Adding buildConfig.Context != "" to the condition (or relying on the fact that a missing context: key is resolved to the project directory in the loader) would make the intent explicit and prevent the fallthrough.
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
There was a problem hiding this comment.
An omitted build context defaults to . and is resolved by the Compose loader before this function runs. Granting the Dockerfile directory is intentional because Bake also needs access to Dockerfile-specific ignore files. Even for an explicitly empty context, the existing context grant already resolves to the working directory in Bake, so this doesn't introduce broader access. I'd keep the current behavior without adding an empty-context guard.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
85f0e1f to
c95a3fe
Compare
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
| // isRemoteContext reports whether the Dockerfile is resolved within a remote source. | ||
| func isRemoteContext(ctxName string) bool { | ||
| contextType, _ := build.DetectContextType(ctxName) | ||
| return contextType == build.ContextTypeGit || contextType == build.ContextTypeRemote || strings.Contains(ctxName, "://") |
There was a problem hiding this comment.
[low] isRemoteContext classifies cwd:// as remote, potentially skipping the Dockerfile directory grant
isRemoteContext (introduced in this PR) returns true whenever the context string contains ://:
func isRemoteContext(ctxName string) bool {
contextType, _ := build.DetectContextType(ctxName)
return contextType == build.ContextTypeGit || contextType == build.ContextTypeRemote || strings.Contains(ctxName, "://")
}This matches cwd://-prefixed contexts, which are actually local filesystem paths. The Dockerfile directory grant in localBuildPaths is gated on this function:
if buildConfig.Dockerfile != "" && buildConfig.DockerfileInline == "" &&
(!isRemoteContext(buildConfig.Context) || filepath.IsAbs(buildConfig.Dockerfile)) {
local = append(local, filepath.Dir(dockerFilePath(buildConfig.Context, buildConfig.Dockerfile)))
}When the main context is cwd://some/path and the Dockerfile is a relative path, both conditions are false (isRemoteContext → true, filepath.IsAbs → false), so the Dockerfile directory is never added to the read-grant list — even though cwd:// resolves to a local path that Bake needs to access.
The context path itself is added to local correctly (the strings.CutPrefix("cwd://", ...) branch in the first loop), so only the Dockerfile's parent directory (for .dockerignore and related files) would be missing from the grant.
If cwd:// can appear as buildConfig.Context with a relative dockerfile: path, the build would fail with an entitlement error. Consider excluding cwd:// from the :// check:
return contextType == build.ContextTypeGit || contextType == build.ContextTypeRemote ||
(strings.Contains(ctxName, "://") && !strings.HasPrefix(ctxName, "cwd://"))| Confidence | Score |
|---|---|
| 🟡 moderate | 65/100 |
There was a problem hiding this comment.
Fixed by stripping cwd:// before resolving the Dockerfile path for the read grant. The Bake target keeps its original context and Dockerfile values so Bake handles their resolution.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
Lower-confidence findings (not posted inline)
- [medium] pkg/compose/build_bake.go:721 —
isRemoteContextreturns true forcwd://contexts, potentially excluding Dockerfile directory from fs.read grants (confidence: weak 52/100)
| } | ||
| } | ||
| // Bake implicitly forwards the default agent for SSH Git contexts. | ||
| if len(ssh) == 0 { |
There was a problem hiding this comment.
[medium] SSH agent entitlement not granted when path-based SSH keys coexist with an SSH git context
When a service has explicit path-based SSH socket keys (key.Path != ""), those paths are added to fs.read grants but the "ssh" entitlement is not appended. The guard at line 272 only adds the "ssh" entitlement for SSH-protocol git contexts when len(ssh) == 0 — meaning no SSH config at all. If a user configures explicit SSH socket files and uses an SSH git build context (e.g. context: git+ssh://...), neither branch appends "ssh" to bake.entitlements:
for _, key := range ssh {
if key.Path == "" {
bake.entitlements = append(bake.entitlements, "ssh") // default agent → ✓
} else {
bake.localPaths = append(bake.localPaths, strings.Split(key.Path, ",")...) // socket path → fs.read only, no "ssh" entitlement
}
}
// Only fires when ssh is completely empty:
if len(ssh) == 0 {
if u, err := buildkitgit.ParseURL(buildConfig.Context); err == nil && u.Scheme == buildkitgit.SSHProtocol {
bake.entitlements = append(bake.entitlements, "ssh") // ← missed when len(ssh) > 0
}
}Bake implicitly forwards the SSH agent for SSH git cloning regardless of whether explicit --ssh socket paths are also configured. The missing entitlement would cause a permission denied / missing entitlement error when Bake tries to clone the SSH git context. Consider changing the guard to check for an SSH git context independently of whether ssh is non-empty:
| if len(ssh) == 0 { | |
| if u, err := buildkitgit.ParseURL(buildConfig.Context); err == nil && u.Scheme == buildkitgit.SSHProtocol { | |
| bake.entitlements = append(bake.entitlements, "ssh") | |
| } |
| Confidence | Score |
|---|---|
| 🟡 moderate | 67/100 |
There was a problem hiding this comment.
Bake only injects the default SSH agent when no explicit SSH configuration is provided for the build context. Our len(ssh) == 0 guard matches that behavior. With explicit key or socket paths, Bake checks filesystem access to those paths, which we already grant. Removing the guard would grant agent access that this configuration doesn't require.
| if key.Path == "" { | ||
| bake.entitlements = append(bake.entitlements, "ssh") | ||
| } else { | ||
| bake.localPaths = append(bake.localPaths, strings.Split(key.Path, ",")...) |
There was a problem hiding this comment.
[low] SSH key.Path comma-split may produce empty paths in fs.read grants
strings.Split(key.Path, ",") is used to support comma-separated socket paths. If key.Path has a trailing comma or contains consecutive commas (e.g. a malformed or programmatically constructed config), an empty string is appended to bake.localPaths. The slices.Compact(slices.Sorted(...)) deduplication in bakeArgs sorts paths ascending — the empty string sorts to the front and, since it appears only once, survives Compact — and this would emit --allow fs.read= to Bake, which may be rejected or behave unexpectedly.
Consider filtering out empty segments:
| bake.localPaths = append(bake.localPaths, strings.Split(key.Path, ",")...) | |
| bake.localPaths = append(bake.localPaths, slices.DeleteFunc(strings.Split(key.Path, ","), func(s string) bool { return s == "" })...) |
| Confidence | Score |
|---|---|
| 🟡 moderate | 67/100 |
There was a problem hiding this comment.
Bake's SSH parser also uses strings.Split and preserves empty segments. Filtering them only from the grants would make the grants differ from the SSH configuration passed to Bake, without fixing the malformed input. I'd leave validation to Bake and BuildKit rather than silently filter those segments here.
ndeloof
left a comment
There was a problem hiding this comment.
Thanks, this fixes the cases from #14285. I ran the PR binary with COMPOSE_BAKE=true against buildx v0.37.2 on a Dockerfile outside the context, a file secret, a key-path ssh: and local cache_from/cache_to: all four fail on v5.5.1 with additional privileges requested and build with this PR.
Requesting changes on one point only: the Dockerfile grant should be the file, not its directory (see the inline comment, with the buildx/buildkit references). Everything else looks fine to me.
| if buildConfig.Dockerfile != "" && buildConfig.DockerfileInline == "" && | ||
| (!isRemoteContext(buildConfig.Context) || filepath.IsAbs(buildConfig.Dockerfile)) { | ||
| // Include the directory so Dockerfile-specific ignore files are covered. | ||
| local = append(local, filepath.Dir(dockerFilePath(buildConfig.Context, buildConfig.Dockerfile))) |
There was a problem hiding this comment.
The grant here is the Dockerfile's parent directory, but buildx only asks for the Dockerfile file, and a file-level grant is enough. This is broader than needed, and the comment above it ("so Dockerfile-specific ignore files are covered") isn't accurate.
- What bake checks is the file:
collectLocalPathsaddst.DockerfilePath, not its directory (bake/bake.go#L1618-L1627 @ buildx v0.37.2). That's why the error readspass "--allow=fs.read=../outside/Dockerfile". - buildx does mount the whole Dockerfile directory as the
dockerfilelocal (build/opt.go#L977), but the entitlement isn't computed from that mount, and the frontend only pulls two files from it:<filename>and<filename>.dockerignore(dockerui/config.go#L351, read at #L415). A plain.dockerignoreis read from the build context, not from the Dockerfile's directory (#L538-L575). - I checked it with a real
buildx bake(v0.37.2,defaultbuilder, context./app,dockerfile: ../docker/Dockerfile.prod, run from an unrelated working directory): granting only the file builds, and a siblingdocker/Dockerfile.prod.dockerignoreexcluding a file is honored identically with the file grant and with the directory grant. A plaindocker/.dockerignoreisn't read in either case.
The directory grant can be much wider than the file: dockerfile: /Dockerfile.prod yields fs.read=/, and a Dockerfile kept in $HOME opens the whole home to the build, where before this change only the context was granted.
Suggestion: append dockerFilePath(buildConfig.Context, buildConfig.Dockerfile) as is (drop filepath.Dir) and fix the comment. The tests need the matching change: TestLocalBuildPaths (resolvedDir, dockerfileDir in the "remote context with local Dockerfile" case) and TestPrepareBakeBuildGrants (filepath.Dir(dockerfile)) should expect the file path.
Only checked on buildx v0.37.2 with the docker driver.
There was a problem hiding this comment.
Ah yes you're right, I conflated the directory BuildKit mounts with the path Bake checks for entitlements. Changed the grant to the Dockerfile itself and updated the tests to assert that its parent directory isn't granted.
Buildx 0.37.2 enforces entitlements with rawjson progress, exposing missing permissions in Compose's Bake invocation. Grant read access to Dockerfiles and file secrets, including local Dockerfiles used with remote contexts, and forward requested entitlements and host network access. Deduplicate grants and preserve environment secrets and privileged builds. Add regression coverage for the generated Bake configuration and arguments. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
c95a3fe to
58ed333
Compare
Grant access to SSH keys and sockets from build configuration and CLI options, and authorize default agent forwarding, including implicit SSH Git contexts. Serialize pathless SSH entries without an empty source so Bake applies the agent entitlement check. Grant read access to local cache imports and OCI layout contexts, and write access to local cache exports. Match Bake's path and cache syntax while excluding service, target and image references from filesystem grants. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
58ed333 to
83efad5
Compare
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The entitlement grant logic is well-structured and correctly implements the required permissions for Buildx bake. Deduplication via slices.Compact(slices.Sorted(...)) is correct, the ociLayoutPath helper mirrors Buildx's own parsing, isRemoteContext correctly excludes remote schemes from path grants, and pathless SSH entries are serialized as bare IDs to trigger Bake's agent entitlement checks. No medium- or high-severity bugs were found in the changed code.
Lower-confidence findings (not posted inline)
- [low] pkg/compose/build_bake.go:455 —
localCachePathscomparesattrs["type"]against the lowercase literal"local"but stores values without lowercasing them, so a cache entry withtype=Local(capital L) would be silently skipped with no read/write grant issued. In practice BuildKit only emits lowercase values, so real-world impact is negligible. Consider lowercasing the value when storing:attrs[strings.ToLower(name)] = strings.ToLower(value). (unverified / low severity)
The current parsing matches Buildx. Attribute keys are case-insensitive, but values are preserved, including the cache type. Buildx and BuildKit both check for exactly |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The Compose plugin bundled in the Docker 29 image omitted the network.host grant when passing build.network: host to Buildx Bake. Docker-in-gVisor builds consequently failed with a request to allow host networking. Build Compose from the merged fix 1e1c577ea [1] and install it in the image's CLI plugin directory until the official image includes the fix. Use the upstream Makefile to produce a static plugin for each native architecture, with a verified source archive and pinned image bases. [1]: docker/compose#14325 Assisted-by: OpenAI Codex, acting on behalf of Tamir Duberstein.
Buildx 0.37.2 enforces the fs.read entitlement under --progress rawjson, which Compose always uses, and Compose grants no read for the merged Dockerfile the Dev Containers CLI writes under $TMPDIR, so every Compose-based dev container failed to build (docker/compose#14285, devcontainers/cli#1320). The fix, docker/compose#14325, is merged but not in any release through 5.6.1. bin/docker-devcontainer exports BUILDX_BAKE_ENTITLEMENTS_FS=0 and execs docker. The rendered VS Code settings point dev.containers.dockerPath at it through a new `dotfiles` template variable (the setting is application-scoped, so a workspace file can't set it), so only Dev Containers builds lose the check. Delete all of it once a Compose release carries the fix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
What this PR does
Fixes Compose builds that fail with missing entitlement grants since Buildx v0.37.2. Compose invokes Bake with
--progress rawjson, which now enforces permissions that this mode previously skipped.Related issue
Changes made
Grants read access to configured Dockerfiles, file secrets, SSH keys and sockets, local cache sources, and OCI layout contexts, plus write access to local cache destinations. Dockerfile grants cover absolute local Dockerfiles used with remote contexts and external relative Dockerfiles used with
cwd://contexts. Each Dockerfile grant targets the file itself.Grants SSH agent access for pathless
build.sshand--sshentries, including implicit agent forwarding for SSH Git contexts without explicit SSH configuration. Pathless entries are serialized as bare SSH IDs so Bake applies its agent entitlement checks correctly.Forwards explicitly requested build entitlements and adds
network.hostforbuild.network: host. Identical grants are deduplicated, while environment secrets and privileged builds retain their existing behavior.Additional context
The raw JSON entitlement enforcement change addresses GHSA-gwr2-q96m-6682. The related GHSA-p54p-jq4x-rc28 covers implicit secret sources and OCI layout paths; it is not the direct cause of the reported explicit file-secret failure.
This change preserves Bake's entitlement enforcement and supplies grants derived from the Compose build configuration and CLI options.