Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion services/core/internal/sandbox/docker/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ func copyRuntimeFiles(ctx context.Context, c *client.Client, id, path string, en
if e := writer.Close(); e != nil {
return e
}
_, e := c.CopyToContainer(ctx, id, client.CopyToContainerOptions{DestinationPath: path, Content: io.Reader(&content), CopyUIDGID: true})
// Never ask Docker to copy the container user's UID/GID onto these entries.
// Docker before 28 resolves the container's Config.User with a passwd lookup
// and passes a "uid:gid" pair through as one user name, so the request fails
// ("getent unable to find entry \"1000:1000\"") and the Runtime never receives
// its bootstrap file. The tar entries already carry uid/gid 1000, which the
// daemon preserves on extraction.
_, e := c.CopyToContainer(ctx, id, client.CopyToContainerOptions{DestinationPath: path, Content: io.Reader(&content)})
return e
}
23 changes: 23 additions & 0 deletions services/core/internal/sandbox/docker/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,26 @@ func TestBootstrapDeliversOnlyPublicConnectionInput(t *testing.T) {
t.Fatal("missing Runtime input")
}
}

// Docker before 28 fails a copyUIDGID request when the container's Config.User
// is a "uid:gid" pair, because it runs a passwd lookup for the whole string.
// Bootstrap must not depend on that lookup.
func TestBootstrapDoesNotRequireContainerUserLookup(t *testing.T) {
b := sandbox.Bootstrap{CoreURL: "https://core.example/api/v1", DeviceID: "da912024-1543-4242-a2c1-5f4f7ebbc6c7", Credential: "test-secret"}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("copyUIDGID") != "" {
http.Error(w, `Handler for PUT /containers/test/archive returned error: getent unable to find entry "1000:1000" in passwd database`, http.StatusInternalServerError)
return
}
w.WriteHeader(200)
}))
defer server.Close()
c, err := client.New(client.WithHost(server.URL), client.WithAPIVersion("1.52"))
if err != nil {
t.Fatal(err)
}
defer c.Close()
if err := (&Provider{client: c}).bootstrap(t.Context(), "test", b); err != nil {
t.Fatal(err)
}
}
Loading