diff --git a/client/build_test.go b/client/build_test.go index 2470148e..af187f41 100644 --- a/client/build_test.go +++ b/client/build_test.go @@ -22,6 +22,7 @@ import ( "github.com/moby/buildkit/session/sshforward/sshprovider" "github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/entitlements" utilsystem "github.com/moby/buildkit/util/system" "github.com/moby/buildkit/util/testutil/integration" "github.com/pkg/errors" @@ -49,6 +50,15 @@ func TestClientGatewayIntegration(t *testing.T) { testClientGatewaySlowCacheExecError, testClientGatewayExecFileActionError, }, integration.WithMirroredImages(integration.OfficialImages("busybox:latest"))) + + integration.Run(t, []integration.Test{ + testClientGatewayContainerSecurityMode, + }, integration.WithMirroredImages(integration.OfficialImages("busybox:latest")), + integration.WithMatrix("secmode", map[string]interface{}{ + "sandbox": securitySandbox, + "insecure": securityInsecure, + }), + ) } func testClientGatewaySolve(t *testing.T, sb integration.Sandbox) { @@ -1478,6 +1488,107 @@ func testClientGatewayExecFileActionError(t *testing.T, sb integration.Sandbox) checkAllReleasable(t, c, sb, true) } +// testClientGatewayContainerSecurityMode ensures that the correct security mode +// is propagated to the gateway container +func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + + ctx := sb.Context() + + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + product := "buildkit_test" + + var command []string + mode := llb.SecurityModeSandbox + var allowedEntitlements []entitlements.Entitlement + secMode := sb.Value("secmode") + if secMode == securitySandbox { + /* + $ capsh --decode=00000000a80425fb + 0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap, + cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap + */ + command = []string{"sh", "-c", `cat /proc/self/status | grep CapEff | grep "00000000a80425fb"`} + allowedEntitlements = []entitlements.Entitlement{} + } else { + skipDockerd(t, sb) + /* + $ capsh --decode=0000003fffffffff + 0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid, + cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw, + cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin, + cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write, + cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read + */ + command = []string{"sh", "-c", `cat /proc/self/status | grep CapEff | grep "0000003fffffffff"`} + mode = llb.SecurityModeInsecure + allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure} + } + + b := func(ctx context.Context, c client.Client) (*client.Result, error) { + st := llb.Image("busybox:latest") + + def, err := st.Marshal(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal state") + } + + r, err := c.Solve(ctx, client.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to solve") + } + + ctr, err := c.NewContainer(ctx, client.NewContainerRequest{ + Mounts: []client.Mount{{ + Dest: "/", + MountType: pb.MountType_BIND, + Ref: r.Ref, + }}, + }) + + if err != nil { + return nil, err + } + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + + pid, err := ctr.Start(ctx, client.StartRequest{ + Args: command, + Stdout: &nopCloser{stdout}, + Stderr: &nopCloser{stderr}, + SecurityMode: mode, + }) + if err != nil { + ctr.Release(ctx) + return nil, err + } + defer ctr.Release(ctx) + + err = pid.Wait() + + t.Logf("Stdout: %q", stdout.String()) + t.Logf("Stderr: %q", stderr.String()) + + require.NoError(t, err) + + return &client.Result{}, nil + } + + solveOpts := SolveOpt{ + AllowedEntitlements: allowedEntitlements, + } + _, err = c.Build(ctx, solveOpts, product, b, nil) + require.NoError(t, err) + + checkAllReleasable(t, c, sb, true) +} + type nopCloser struct { io.Writer } diff --git a/frontend/gateway/gateway.go b/frontend/gateway/gateway.go index 60625787..67de2b74 100644 --- a/frontend/gateway/gateway.go +++ b/frontend/gateway/gateway.go @@ -1135,14 +1135,15 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e pios[pid] = pio proc, err := ctr.Start(initCtx, gwclient.StartRequest{ - Args: init.Meta.Args, - Env: init.Meta.Env, - User: init.Meta.User, - Cwd: init.Meta.Cwd, - Tty: init.Tty, - Stdin: pio.processReaders[0], - Stdout: pio.processWriters[1], - Stderr: pio.processWriters[2], + Args: init.Meta.Args, + Env: init.Meta.Env, + User: init.Meta.User, + Cwd: init.Meta.Cwd, + Tty: init.Tty, + Stdin: pio.processReaders[0], + Stdout: pio.processWriters[1], + Stderr: pio.processWriters[2], + SecurityMode: init.Security, }) if err != nil { return stack.Enable(err)