Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v0.11] Backport CVE-2024-23653 #4638

Closed
wants to merge 2 commits into from
Closed
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
58 changes: 51 additions & 7 deletions client/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func TestClientGatewayIntegration(t *testing.T) {
), integration.WithMirroredImages(integration.OfficialImages("busybox:latest")))

integration.Run(t, integration.TestFuncs(
testClientGatewayContainerSecurityMode,
testClientGatewayContainerSecurityModeCaps,
testClientGatewayContainerSecurityModeValidation,
), integration.WithMirroredImages(integration.OfficialImages("busybox:latest")),
integration.WithMatrix("secmode", map[string]interface{}{
"sandbox": securitySandbox,
Expand All @@ -69,7 +70,8 @@ func TestClientGatewayIntegration(t *testing.T) {
)

integration.Run(t, integration.TestFuncs(
testClientGatewayContainerHostNetworking,
testClientGatewayContainerHostNetworkingAccess,
testClientGatewayContainerHostNetworkingValidation,
),
integration.WithMirroredImages(integration.OfficialImages("busybox:latest")),
integration.WithMatrix("netmode", map[string]interface{}{
Expand Down Expand Up @@ -1684,9 +1686,17 @@ func testClientGatewayExecFileActionError(t *testing.T, sb integration.Sandbox)
checkAllReleasable(t, c, sb, true)
}

// testClientGatewayContainerSecurityMode ensures that the correct security mode
// testClientGatewayContainerSecurityModeCaps ensures that the correct security mode
// is propagated to the gateway container
func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox) {
func testClientGatewayContainerSecurityModeCaps(t *testing.T, sb integration.Sandbox) {
testClientGatewayContainerSecurityMode(t, sb, false)
}

func testClientGatewayContainerSecurityModeValidation(t *testing.T, sb integration.Sandbox) {
testClientGatewayContainerSecurityMode(t, sb, true)
}

func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox, expectFail bool) {
integration.CheckFeatureCompat(t, sb, integration.FeatureSecurityMode)
requiresLinux(t)

Expand All @@ -1713,6 +1723,9 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
require.EqualValues(t, 0xa80425fb, caps)
}
allowedEntitlements = []entitlements.Entitlement{}
if expectFail {
return
}
} else {
assertCaps = func(caps uint64) {
/*
Expand All @@ -1729,6 +1742,9 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
}
mode = llb.SecurityModeInsecure
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
if expectFail {
allowedEntitlements = []entitlements.Entitlement{}
}
}

b := func(ctx context.Context, c client.Client) (*client.Result, error) {
Expand Down Expand Up @@ -1778,6 +1794,12 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
t.Logf("Stdout: %q", stdout.String())
t.Logf("Stderr: %q", stderr.String())

if expectFail {
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
return nil, err
}

require.NoError(t, err)

capsValue, err := strconv.ParseUint(strings.TrimSpace(stdout.String()), 16, 64)
Expand All @@ -1792,7 +1814,13 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
AllowedEntitlements: allowedEntitlements,
}
_, err = c.Build(ctx, solveOpts, product, b, nil)
require.NoError(t, err)

if expectFail {
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
} else {
require.NoError(t, err)
}

checkAllReleasable(t, c, sb, true)
}
Expand Down Expand Up @@ -1868,7 +1896,15 @@ func testClientGatewayContainerExtraHosts(t *testing.T, sb integration.Sandbox)
checkAllReleasable(t, c, sb, true)
}

func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandbox) {
func testClientGatewayContainerHostNetworkingAccess(t *testing.T, sb integration.Sandbox) {
testClientGatewayContainerHostNetworking(t, sb, false)
}

func testClientGatewayContainerHostNetworkingValidation(t *testing.T, sb integration.Sandbox) {
testClientGatewayContainerHostNetworking(t, sb, true)
}

func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandbox, expectFail bool) {
if os.Getenv("BUILDKIT_RUN_NETWORK_INTEGRATION_TESTS") == "" {
t.SkipNow()
}
Expand All @@ -1889,6 +1925,9 @@ func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandb
if sb.Value("netmode") == hostNetwork {
netMode = pb.NetMode_HOST
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementNetworkHost}
if expectFail {
allowedEntitlements = []entitlements.Entitlement{}
}
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
Expand Down Expand Up @@ -1947,7 +1986,12 @@ func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandb
t.Logf("Stderr: %q", stderr.String())

if netMode == pb.NetMode_HOST {
require.NoError(t, err)
if expectFail {
require.Error(t, err)
require.Contains(t, err.Error(), "network.host is not allowed")
} else {
require.NoError(t, err)
}
} else {
require.Error(t, err)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,8 @@ func newController(c *cli.Context, cfg *config.Config) (*control.Controller, err
return nil, err
}
frontends := map[string]frontend.Frontend{}
frontends["dockerfile.v0"] = forwarder.NewGatewayForwarder(wc, dockerfile.Build)
frontends["gateway.v0"] = gateway.NewGatewayFrontend(wc)
frontends["dockerfile.v0"] = forwarder.NewGatewayForwarder(wc.Infos(), dockerfile.Build)
frontends["gateway.v0"] = gateway.NewGatewayFrontend(wc.Infos())

cacheStorage, err := bboltcachestorage.NewStore(filepath.Join(cfg.Root, "cache.db"))
if err != nil {
Expand Down
10 changes: 8 additions & 2 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
"net"
"syscall"

"github.com/moby/buildkit/snapshot"
"github.com/containerd/containerd/mount"
"github.com/docker/docker/pkg/idtools"
"github.com/moby/buildkit/solver/pb"
)

Expand All @@ -27,8 +28,13 @@ type Meta struct {
RemoveMountStubsRecursive bool
}

type MountableRef interface {
Mount() ([]mount.Mount, func() error, error)
IdentityMapping() *idtools.IdentityMapping
}

type Mountable interface {
Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error)
Mount(ctx context.Context, readonly bool) (MountableRef, error)
}

type Mount struct {
Expand Down
3 changes: 2 additions & 1 deletion frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/executor"
gw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
Expand All @@ -17,7 +18,7 @@ type Result = result.Result[solver.ResultProxy]
type Attestation = result.Attestation[solver.ResultProxy]

type Frontend interface {
Solve(ctx context.Context, llb FrontendLLBBridge, opt map[string]string, inputs map[string]*pb.Definition, sid string, sm *session.Manager) (*Result, error)
Solve(ctx context.Context, llb FrontendLLBBridge, exec executor.Executor, opt map[string]string, inputs map[string]*pb.Definition, sid string, sm *session.Manager) (*Result, error)
}

type FrontendLLBBridge interface {
Expand Down
9 changes: 4 additions & 5 deletions frontend/gateway/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type Mount struct {
WorkerRef *worker.WorkerRef
}

func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g session.Group, req NewContainerRequest) (client.Container, error) {
func NewContainer(ctx context.Context, cm cache.Manager, exec executor.Executor, sm *session.Manager, g session.Group, req NewContainerRequest) (client.Container, error) {
ctx, cancel := context.WithCancel(ctx)
eg, ctx := errgroup.WithContext(ctx)
platform := opspb.Platform{
Expand All @@ -58,7 +58,7 @@ func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g s
netMode: req.NetMode,
extraHosts: req.ExtraHosts,
platform: platform,
executor: w.Executor(),
executor: exec,
errGroup: eg,
ctx: ctx,
cancel: cancel,
Expand All @@ -79,9 +79,8 @@ func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g s
}

name := fmt.Sprintf("container %s", req.ContainerID)
mm := mounts.NewMountManager(name, w.CacheManager(), sm)
p, err := PrepareMounts(ctx, mm, w.CacheManager(), g, "", mnts, refs, func(m *opspb.Mount, ref cache.ImmutableRef) (cache.MutableRef, error) {
cm := w.CacheManager()
mm := mounts.NewMountManager(name, cm, sm)
p, err := PrepareMounts(ctx, mm, cm, g, "", mnts, refs, func(m *opspb.Mount, ref cache.ImmutableRef) (cache.MutableRef, error) {
if m.Input != opspb.Empty {
cm = refs[m.Input].Worker.CacheManager()
}
Expand Down
9 changes: 6 additions & 3 deletions frontend/gateway/forwarder/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

cacheutil "github.com/moby/buildkit/cache/util"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/frontend"
"github.com/moby/buildkit/frontend/gateway"
"github.com/moby/buildkit/frontend/gateway/client"
Expand All @@ -26,7 +27,7 @@ import (
"golang.org/x/sync/errgroup"
)

func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*opspb.Definition, w worker.Infos, sid string, sm *session.Manager) (*bridgeClient, error) {
func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, opts map[string]string, inputs map[string]*opspb.Definition, w worker.Infos, sid string, sm *session.Manager) (*bridgeClient, error) {
bc := &bridgeClient{
opts: opts,
inputs: inputs,
Expand All @@ -35,6 +36,7 @@ func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLL
sm: sm,
workers: w,
workerRefByID: make(map[string]*worker.WorkerRef),
executor: exec,
}
bc.buildOpts = bc.loadBuildOpts()
return bc, nil
Expand All @@ -52,6 +54,7 @@ type bridgeClient struct {
workerRefByID map[string]*worker.WorkerRef
buildOpts client.BuildOpts
ctrs []client.Container
executor executor.Executor
}

func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) {
Expand Down Expand Up @@ -292,13 +295,13 @@ func (c *bridgeClient) NewContainer(ctx context.Context, req client.NewContainer
return nil, err
}

w, err := c.workers.GetDefault()
cm, err := c.workers.DefaultCacheManager()
if err != nil {
return nil, err
}

group := session.NewGroup(c.sid)
ctr, err := gateway.NewContainer(ctx, w, c.sm, group, ctrReq)
ctr, err := gateway.NewContainer(ctx, cm, c.executor, c.sm, group, ctrReq)
if err != nil {
return nil, err
}
Expand Down
5 changes: 3 additions & 2 deletions frontend/gateway/forwarder/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package forwarder
import (
"context"

"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/frontend"
"github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
Expand All @@ -22,8 +23,8 @@ type GatewayForwarder struct {
f client.BuildFunc
}

func (gf *GatewayForwarder) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*pb.Definition, sid string, sm *session.Manager) (retRes *frontend.Result, retErr error) {
c, err := llbBridgeToGatewayClient(ctx, llbBridge, opts, inputs, gf.workers, sid, sm)
func (gf *GatewayForwarder) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, opts map[string]string, inputs map[string]*pb.Definition, sid string, sm *session.Manager) (retRes *frontend.Result, retErr error) {
c, err := llbBridgeToGatewayClient(ctx, llbBridge, exec, opts, inputs, gf.workers, sid, sm)
if err != nil {
return nil, err
}
Expand Down
28 changes: 12 additions & 16 deletions frontend/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func filterPrefix(opts map[string]string, pfx string) map[string]string {
return m
}

func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*frontend.Result, error) {
func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, opts map[string]string, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*frontend.Result, error) {
source, ok := opts[keySource]
if !ok {
return nil, errors.Errorf("no source specified for gateway")
Expand Down Expand Up @@ -251,18 +251,13 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
}
}

lbf, ctx, err := serveLLBBridgeForwarder(ctx, llbBridge, gf.workers, inputs, sid, sm)
lbf, ctx, err := serveLLBBridgeForwarder(ctx, llbBridge, exec, gf.workers, inputs, sid, sm)
defer lbf.conn.Close() //nolint
if err != nil {
return nil, err
}
defer lbf.Discard()

w, err := gf.workers.GetDefault()
if err != nil {
return nil, err
}

mdmnt, release, err := metadataMount(frontendDef)
if err != nil {
return nil, err
Expand All @@ -275,8 +270,7 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
mnts = append(mnts, *mdmnt)
}

err = w.Executor().Run(ctx, "", mountWithSession(rootFS, session.NewGroup(sid)), mnts, executor.ProcessInfo{Meta: meta, Stdin: lbf.Stdin, Stdout: lbf.Stdout, Stderr: os.Stderr}, nil)

err = exec.Run(ctx, "", mountWithSession(rootFS, session.NewGroup(sid)), mnts, executor.ProcessInfo{Meta: meta, Stdin: lbf.Stdin, Stdout: lbf.Stdout, Stderr: os.Stderr}, nil)
if err != nil {
if errdefs.IsCanceled(ctx, err) && lbf.isErrServerClosed {
err = errors.Errorf("frontend grpc server closed unexpectedly")
Expand Down Expand Up @@ -405,11 +399,11 @@ func (lbf *llbBridgeForwarder) Result() (*frontend.Result, error) {
return lbf.result, nil
}

func NewBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) LLBBridgeForwarder {
return newBridgeForwarder(ctx, llbBridge, workers, inputs, sid, sm)
func NewBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) LLBBridgeForwarder {
return newBridgeForwarder(ctx, llbBridge, exec, workers, inputs, sid, sm)
}

func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) *llbBridgeForwarder {
func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) *llbBridgeForwarder {
lbf := &llbBridgeForwarder{
callCtx: ctx,
llbBridge: llbBridge,
Expand All @@ -422,13 +416,14 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
sid: sid,
sm: sm,
ctrs: map[string]gwclient.Container{},
executor: exec,
}
return lbf
}

func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*llbBridgeForwarder, context.Context, error) {
func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*llbBridgeForwarder, context.Context, error) {
ctx, cancel := context.WithCancel(ctx)
lbf := newBridgeForwarder(ctx, llbBridge, workers, inputs, sid, sm)
lbf := newBridgeForwarder(ctx, llbBridge, exec, workers, inputs, sid, sm)
server := grpc.NewServer(grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor), grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor))
grpc_health_v1.RegisterHealthServer(server, health.NewServer())
pb.RegisterLLBBridgeServer(server, lbf)
Expand Down Expand Up @@ -523,6 +518,7 @@ type llbBridgeForwarder struct {
isErrServerClosed bool
sid string
sm *session.Manager
executor executor.Executor
*pipe
ctrs map[string]gwclient.Container
ctrsMu sync.Mutex
Expand Down Expand Up @@ -1001,7 +997,7 @@ func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewConta
// and we want the context to live for the duration of the container.
group := session.NewGroup(lbf.sid)

w, err := lbf.workers.GetDefault()
cm, err := lbf.workers.DefaultCacheManager()
if err != nil {
return nil, stack.Enable(err)
}
Expand All @@ -1011,7 +1007,7 @@ func (lbf *llbBridgeForwarder) NewContainer(ctx context.Context, in *pb.NewConta
return nil, stack.Enable(err)
}

ctr, err := NewContainer(context.Background(), w, lbf.sm, group, ctrReq)
ctr, err := NewContainer(context.Background(), cm, lbf.executor, lbf.sm, group, ctrReq)
if err != nil {
return nil, stack.Enable(err)
}
Expand Down
7 changes: 2 additions & 5 deletions snapshot/snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ import (
"github.com/containerd/containerd/pkg/userns"
"github.com/containerd/containerd/snapshots"
"github.com/docker/docker/pkg/idtools"
"github.com/moby/buildkit/executor"
"github.com/pkg/errors"
)

type Mountable interface {
// ID() string
Mount() ([]mount.Mount, func() error, error)
IdentityMapping() *idtools.IdentityMapping
}
type Mountable = executor.MountableRef

// Snapshotter defines interface that any snapshot implementation should satisfy
type Snapshotter interface {
Expand Down
Loading
Loading