Merge pull request #46222 from thaJeztah/23.0_backport_capabilites

[23.0 backport] Do not drop effective&permitted set
This commit is contained in:
Sebastiaan van Stijn 2023-08-29 23:23:26 +02:00 committed by GitHub
commit 436bcf7b33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 383 additions and 79 deletions

View file

@ -109,7 +109,10 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
memory.KernelTCP = &config.KernelMemoryTCP memory.KernelTCP = &config.KernelMemoryTCP
} }
return &memory if memory != (specs.LinuxMemory{}) {
return &memory
}
return nil
} }
func getPidsLimit(config containertypes.Resources) *specs.LinuxPids { func getPidsLimit(config containertypes.Resources) *specs.LinuxPids {
@ -131,7 +134,7 @@ func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) {
if config.CPUShares < 0 { if config.CPUShares < 0 {
return nil, fmt.Errorf("shares: invalid argument") return nil, fmt.Errorf("shares: invalid argument")
} }
if config.CPUShares >= 0 { if config.CPUShares > 0 {
shares := uint64(config.CPUShares) shares := uint64(config.CPUShares)
cpu.Shares = &shares cpu.Shares = &shares
} }
@ -172,7 +175,10 @@ func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) {
cpu.RealtimeRuntime = &c cpu.RealtimeRuntime = &c
} }
return &cpu, nil if cpu != (specs.LinuxCPU{}) {
return &cpu, nil
}
return nil, nil
} }
func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeightDevice, error) { func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeightDevice, error) {

View file

@ -54,6 +54,9 @@ func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
}) })
} }
if s.Process == nil {
s.Process = &specs.Process{}
}
s.Process.Rlimits = rlimits s.Process.Rlimits = rlimits
return nil return nil
} }
@ -114,6 +117,9 @@ func WithRootless(daemon *Daemon) coci.SpecOpts {
// WithOOMScore sets the oom score // WithOOMScore sets the oom score
func WithOOMScore(score *int) coci.SpecOpts { func WithOOMScore(score *int) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if s.Process == nil {
s.Process = &specs.Process{}
}
s.Process.OOMScoreAdj = score s.Process.OOMScoreAdj = score
return nil return nil
} }
@ -122,6 +128,12 @@ func WithOOMScore(score *int) coci.SpecOpts {
// WithSelinux sets the selinux labels // WithSelinux sets the selinux labels
func WithSelinux(c *container.Container) coci.SpecOpts { func WithSelinux(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if s.Process == nil {
s.Process = &specs.Process{}
}
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
s.Process.SelinuxLabel = c.GetProcessLabel() s.Process.SelinuxLabel = c.GetProcessLabel()
s.Linux.MountLabel = c.MountLabel s.Linux.MountLabel = c.MountLabel
return nil return nil
@ -152,6 +164,9 @@ func WithApparmor(c *container.Container) coci.SpecOpts {
return err return err
} }
} }
if s.Process == nil {
s.Process = &specs.Process{}
}
s.Process.ApparmorProfile = appArmorProfile s.Process.ApparmorProfile = appArmorProfile
} }
return nil return nil
@ -214,6 +229,10 @@ func getUser(c *container.Container, username string) (specs.User, error) {
} }
func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) { func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) {
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
for i, n := range s.Linux.Namespaces { for i, n := range s.Linux.Namespaces {
if n.Type == ns.Type { if n.Type == ns.Type {
s.Linux.Namespaces[i] = ns s.Linux.Namespaces[i] = ns
@ -608,6 +627,9 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
} }
rootpg := mountPropagationMap[s.Linux.RootfsPropagation] rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
if rootpg != mount.SHARED && rootpg != mount.RSHARED { if rootpg != mount.SHARED && rootpg != mount.RSHARED {
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED] s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED]
} }
case mount.SLAVE, mount.RSLAVE: case mount.SLAVE, mount.RSLAVE:
@ -636,6 +658,9 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
if !fallback { if !fallback {
rootpg := mountPropagationMap[s.Linux.RootfsPropagation] rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE { if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE] s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
} }
} }
@ -691,8 +716,10 @@ func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
clearReadOnly(&s.Mounts[i]) clearReadOnly(&s.Mounts[i])
} }
} }
s.Linux.ReadonlyPaths = nil if s.Linux != nil {
s.Linux.MaskedPaths = nil s.Linux.ReadonlyPaths = nil
s.Linux.MaskedPaths = nil
}
} }
// TODO: until a kernel/mount solution exists for handling remount in a user namespace, // TODO: until a kernel/mount solution exists for handling remount in a user namespace,
@ -738,6 +765,9 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
if len(cwd) == 0 { if len(cwd) == 0 {
cwd = "/" cwd = "/"
} }
if s.Process == nil {
s.Process = &specs.Process{}
}
s.Process.Args = append([]string{c.Path}, c.Args...) s.Process.Args = append([]string{c.Path}, c.Args...)
// only add the custom init if it is specified and the container is running in its // only add the custom init if it is specified and the container is running in its
@ -817,6 +847,9 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
} else { } else {
cgroupsPath = filepath.Join(parent, c.ID) cgroupsPath = filepath.Join(parent, c.ID)
} }
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
s.Linux.CgroupsPath = cgroupsPath s.Linux.CgroupsPath = cgroupsPath
// the rest is only needed for CPU RT controller // the rest is only needed for CPU RT controller
@ -917,8 +950,14 @@ func WithDevices(daemon *Daemon, c *container.Container) coci.SpecOpts {
} }
} }
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
if s.Linux.Resources == nil {
s.Linux.Resources = &specs.LinuxResources{}
}
s.Linux.Devices = append(s.Linux.Devices, devs...) s.Linux.Devices = append(s.Linux.Devices, devs...)
s.Linux.Resources.Devices = devPermissions s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, devPermissions...)
for _, req := range c.HostConfig.DeviceRequests { for _, req := range c.HostConfig.DeviceRequests {
if err := daemon.handleDevice(req, s); err != nil { if err := daemon.handleDevice(req, s); err != nil {
@ -959,27 +998,28 @@ func WithResources(c *container.Container) coci.SpecOpts {
if err != nil { if err != nil {
return err return err
} }
blkioWeight := r.BlkioWeight
specResources := &specs.LinuxResources{ if s.Linux == nil {
Memory: memoryRes, s.Linux = &specs.Linux{}
CPU: cpuRes,
BlockIO: &specs.LinuxBlockIO{
Weight: &blkioWeight,
WeightDevice: weightDevices,
ThrottleReadBpsDevice: readBpsDevice,
ThrottleWriteBpsDevice: writeBpsDevice,
ThrottleReadIOPSDevice: readIOpsDevice,
ThrottleWriteIOPSDevice: writeIOpsDevice,
},
Pids: getPidsLimit(r),
} }
if s.Linux.Resources == nil {
if s.Linux.Resources != nil && len(s.Linux.Resources.Devices) > 0 { s.Linux.Resources = &specs.LinuxResources{}
specResources.Devices = s.Linux.Resources.Devices
} }
s.Linux.Resources.Memory = memoryRes
s.Linux.Resources.CPU = cpuRes
s.Linux.Resources.BlockIO = &specs.LinuxBlockIO{
WeightDevice: weightDevices,
ThrottleReadBpsDevice: readBpsDevice,
ThrottleWriteBpsDevice: writeBpsDevice,
ThrottleReadIOPSDevice: readIOpsDevice,
ThrottleWriteIOPSDevice: writeIOpsDevice,
}
if r.BlkioWeight != 0 {
w := r.BlkioWeight
s.Linux.Resources.BlockIO.Weight = &w
}
s.Linux.Resources.Pids = getPidsLimit(r)
s.Linux.Resources = specResources
return nil return nil
} }
} }
@ -987,6 +1027,15 @@ func WithResources(c *container.Container) coci.SpecOpts {
// WithSysctls sets the container's sysctls // WithSysctls sets the container's sysctls
func WithSysctls(c *container.Container) coci.SpecOpts { func WithSysctls(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if len(c.HostConfig.Sysctls) == 0 {
return nil
}
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
if s.Linux.Sysctl == nil {
s.Linux.Sysctl = make(map[string]string)
}
// We merge the sysctls injected above with the HostConfig (latter takes // We merge the sysctls injected above with the HostConfig (latter takes
// precedence for backwards-compatibility reasons). // precedence for backwards-compatibility reasons).
for k, v := range c.HostConfig.Sysctls { for k, v := range c.HostConfig.Sysctls {
@ -999,6 +1048,9 @@ func WithSysctls(c *container.Container) coci.SpecOpts {
// WithUser sets the container's user // WithUser sets the container's user
func WithUser(c *container.Container) coci.SpecOpts { func WithUser(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if s.Process == nil {
s.Process = &specs.Process{}
}
var err error var err error
s.Process.User, err = getUser(c, c.Config.User) s.Process.User, err = getUser(c, c.Config.User)
return err return err

View file

@ -11,17 +11,20 @@ import (
"github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/network"
"github.com/docker/docker/libnetwork" "github.com/docker/docker/libnetwork"
"github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/containerfs"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip" "gotest.tools/v3/skip"
) )
func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon { func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
root, err := os.MkdirTemp("", "oci_linux_test-root") t.Helper()
assert.NilError(t, err) root := t.TempDir()
rootfs := filepath.Join(root, "rootfs") rootfs := filepath.Join(root, "rootfs")
err = os.MkdirAll(rootfs, 0755) err := os.MkdirAll(rootfs, 0755)
assert.NilError(t, err) assert.NilError(t, err)
netController, err := libnetwork.New() netController, err := libnetwork.New()
@ -48,11 +51,19 @@ func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
} }
return d // HORRIBLE HACK: clean up shm mounts leaked by some tests. Otherwise the
} // offending tests would fail due to the mounts blocking the temporary
// directory from being cleaned up.
t.Cleanup(func() {
if c.ShmPath != "" {
var err error
for err == nil { // Some tests over-mount over the same path multiple times.
err = unix.Unmount(c.ShmPath, unix.MNT_DETACH)
}
}
})
func cleanupFakeContainer(c *container.Container) { return d
_ = os.RemoveAll(c.Root)
} }
// TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs // TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs
@ -72,7 +83,6 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
}, },
} }
d := setupFakeDaemon(t, c) d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
_, err := d.createSpec(c) _, err := d.createSpec(c)
assert.Check(t, err) assert.Check(t, err)
@ -91,7 +101,6 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
}, },
} }
d := setupFakeDaemon(t, c) d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
s, err := d.createSpec(c) s, err := d.createSpec(c)
assert.Check(t, err) assert.Check(t, err)
@ -120,7 +129,6 @@ func TestSysctlOverride(t *testing.T) {
}, },
} }
d := setupFakeDaemon(t, c) d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
// Ensure that the implicit sysctl is set correctly. // Ensure that the implicit sysctl is set correctly.
s, err := d.createSpec(c) s, err := d.createSpec(c)
@ -172,7 +180,6 @@ func TestSysctlOverrideHost(t *testing.T) {
}, },
} }
d := setupFakeDaemon(t, c) d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
// Ensure that the implicit sysctl is not set // Ensure that the implicit sysctl is not set
s, err := d.createSpec(c) s, err := d.createSpec(c)
@ -200,3 +207,38 @@ func TestGetSourceMount(t *testing.T) {
_, _, err = getSourceMount(cwd) _, _, err = getSourceMount(cwd)
assert.NilError(t, err) assert.NilError(t, err)
} }
func TestDefaultResources(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root") // TODO: is this actually true? I'm guilty of following the cargo cult here.
c := &container.Container{
HostConfig: &containertypes.HostConfig{
IpcMode: containertypes.IPCModeNone,
},
}
d := setupFakeDaemon(t, c)
s, err := d.createSpec(c)
assert.NilError(t, err)
checkResourcesAreUnset(t, s.Linux.Resources)
}
func checkResourcesAreUnset(t *testing.T, r *specs.LinuxResources) {
t.Helper()
if r != nil {
if r.Memory != nil {
assert.Check(t, is.DeepEqual(r.Memory, &specs.LinuxMemory{}))
}
if r.CPU != nil {
assert.Check(t, is.DeepEqual(r.CPU, &specs.LinuxCPU{}))
}
assert.Check(t, is.Nil(r.Pids))
if r.BlockIO != nil {
assert.Check(t, is.DeepEqual(r.BlockIO, &specs.LinuxBlockIO{}, cmpopts.EquateEmpty()))
}
if r.Network != nil {
assert.Check(t, is.DeepEqual(r.Network, &specs.LinuxNetwork{}, cmpopts.EquateEmpty()))
}
}
}

View file

@ -13,6 +13,9 @@ import (
func WithConsoleSize(c *container.Container) coci.SpecOpts { func WithConsoleSize(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if c.HostConfig.ConsoleSize[0] > 0 || c.HostConfig.ConsoleSize[1] > 0 { if c.HostConfig.ConsoleSize[0] > 0 || c.HostConfig.ConsoleSize[1] > 0 {
if s.Process == nil {
s.Process = &specs.Process{}
}
s.Process.ConsoleSize = &specs.Box{ s.Process.ConsoleSize = &specs.Box{
Height: c.HostConfig.ConsoleSize[0], Height: c.HostConfig.ConsoleSize[0],
Width: c.HostConfig.ConsoleSize[1], Width: c.HostConfig.ConsoleSize[1],

View file

@ -9,7 +9,12 @@ func setLinuxDomainname(c *container.Container, s *specs.Spec) {
// There isn't a field in the OCI for the NIS domainname, but luckily there // There isn't a field in the OCI for the NIS domainname, but luckily there
// is a sysctl which has an identical effect to setdomainname(2) so there's // is a sysctl which has an identical effect to setdomainname(2) so there's
// no explicit need for runtime support. // no explicit need for runtime support.
s.Linux.Sysctl = make(map[string]string) if s.Linux == nil {
s.Linux = &specs.Linux{}
}
if s.Linux.Sysctl == nil {
s.Linux.Sysctl = make(map[string]string)
}
if c.Config.Domainname != "" { if c.Config.Domainname != "" {
s.Linux.Sysctl["kernel.domainname"] = c.Config.Domainname s.Linux.Sysctl["kernel.domainname"] = c.Config.Domainname
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/docker/container" "github.com/docker/docker/container"
dconfig "github.com/docker/docker/daemon/config" dconfig "github.com/docker/docker/daemon/config"
"github.com/docker/docker/profiles/seccomp" "github.com/docker/docker/profiles/seccomp"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -31,6 +32,9 @@ func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
c.SeccompProfile = dconfig.SeccompProfileUnconfined c.SeccompProfile = dconfig.SeccompProfileUnconfined
return nil return nil
} }
if s.Linux == nil {
s.Linux = &specs.Linux{}
}
var err error var err error
switch { switch {
case c.SeccompProfile == dconfig.SeccompProfileDefault: case c.SeccompProfile == dconfig.SeccompProfileDefault:

View file

@ -11,15 +11,19 @@ import (
func toContainerdResources(resources container.Resources) *libcontainerdtypes.Resources { func toContainerdResources(resources container.Resources) *libcontainerdtypes.Resources {
var r libcontainerdtypes.Resources var r libcontainerdtypes.Resources
r.BlockIO = &specs.LinuxBlockIO{ if resources.BlkioWeight != 0 {
Weight: &resources.BlkioWeight, r.BlockIO = &specs.LinuxBlockIO{
Weight: &resources.BlkioWeight,
}
} }
shares := uint64(resources.CPUShares) cpu := specs.LinuxCPU{
r.CPU = &specs.LinuxCPU{ Cpus: resources.CpusetCpus,
Shares: &shares, Mems: resources.CpusetMems,
Cpus: resources.CpusetCpus, }
Mems: resources.CpusetMems, if resources.CPUShares != 0 {
shares := uint64(resources.CPUShares)
cpu.Shares = &shares
} }
var ( var (
@ -37,17 +41,33 @@ func toContainerdResources(resources container.Resources) *libcontainerdtypes.Re
period = uint64(resources.CPUPeriod) period = uint64(resources.CPUPeriod)
} }
r.CPU.Period = &period if period != 0 {
r.CPU.Quota = &quota cpu.Period = &period
}
r.Memory = &specs.LinuxMemory{ if quota != 0 {
Limit: &resources.Memory, cpu.Quota = &quota
Reservation: &resources.MemoryReservation,
Kernel: &resources.KernelMemory,
} }
if cpu != (specs.LinuxCPU{}) {
r.CPU = &cpu
}
var memory specs.LinuxMemory
if resources.Memory != 0 {
memory.Limit = &resources.Memory
}
if resources.MemoryReservation != 0 {
memory.Reservation = &resources.MemoryReservation
}
if resources.KernelMemory != 0 {
memory.Kernel = &resources.KernelMemory
}
if resources.MemorySwap > 0 { if resources.MemorySwap > 0 {
r.Memory.Swap = &resources.MemorySwap memory.Swap = &resources.MemorySwap
}
if memory != (specs.LinuxMemory{}) {
r.Memory = &memory
} }
r.Pids = getPidsLimit(resources) r.Pids = getPidsLimit(resources)

View file

@ -0,0 +1,11 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"testing"
"github.com/docker/docker/api/types/container"
)
func TestToContainerdResources_Defaults(t *testing.T) {
checkResourcesAreUnset(t, toContainerdResources(container.Resources{}))
}

View file

@ -0,0 +1,108 @@
package capabilities
import (
"bytes"
"context"
"io"
"strings"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/testutil/fakecontext"
"gotest.tools/v3/assert"
"gotest.tools/v3/poll"
)
func TestNoNewPrivileges(t *testing.T) {
defer setupTest(t)()
withFileCapability := `
FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y libcap2-bin --no-install-recommends
RUN setcap CAP_DAC_OVERRIDE=+eip /bin/cat
RUN echo "hello" > /txt && chown 0:0 /txt && chmod 700 /txt
RUN useradd -u 1500 test
`
imageTag := "captest"
source := fakecontext.New(t, "", fakecontext.WithDockerfile(withFileCapability))
defer source.Close()
client := testEnv.APIClient()
// Build image
ctx := context.TODO()
resp, err := client.ImageBuild(ctx,
source.AsTarReader(t),
types.ImageBuildOptions{
Tags: []string{imageTag},
})
assert.NilError(t, err)
_, err = io.Copy(io.Discard, resp.Body)
assert.NilError(t, err)
resp.Body.Close()
testCases := []struct {
doc string
opts []func(*container.TestContainerConfig)
stdOut, stdErr string
}{
{
doc: "CapabilityRequested=true",
opts: []func(*container.TestContainerConfig){
container.WithUser("test"),
container.WithCapability("CAP_DAC_OVERRIDE"),
},
stdOut: "hello",
},
{
doc: "CapabilityRequested=false",
opts: []func(*container.TestContainerConfig){
container.WithUser("test"),
container.WithDropCapability("CAP_DAC_OVERRIDE"),
},
stdErr: "exec /bin/cat: operation not permitted",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
// Run the container with the image
opts := append(tc.opts,
container.WithImage(imageTag),
container.WithCmd("/bin/cat", "/txt"),
container.WithSecurityOpt("no-new-privileges=true"),
)
cid := container.Run(ctx, t, client, opts...)
poll.WaitOn(t, container.IsInState(ctx, client, cid, "exited"), poll.WithDelay(100*time.Millisecond))
// Assert on outputs
logReader, err := client.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
})
assert.NilError(t, err)
defer logReader.Close()
var actualStdout, actualStderr bytes.Buffer
_, err = stdcopy.StdCopy(&actualStdout, &actualStderr, logReader)
assert.NilError(t, err)
stdOut := strings.TrimSpace(actualStdout.String())
stdErr := strings.TrimSpace(actualStderr.String())
if stdOut != tc.stdOut {
t.Fatalf("test produced invalid output: %q, expected %q. Stderr:%q", stdOut, tc.stdOut, stdErr)
}
if stdErr != tc.stdErr {
t.Fatalf("test produced invalid error: %q, expected %q. Stdout:%q", stdErr, tc.stdErr, stdOut)
}
})
}
}

View file

@ -0,0 +1,33 @@
package capabilities
import (
"fmt"
"os"
"testing"
"github.com/docker/docker/testutil/environment"
)
var testEnv *environment.Execution
func TestMain(m *testing.M) {
var err error
testEnv, err = environment.New()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = environment.EnsureFrozenImagesLinux(testEnv)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
testEnv.Print()
os.Exit(m.Run())
}
func setupTest(t *testing.T) func() {
environment.ProtectAll(t, testEnv)
return func() { testEnv.Clean(t) }
}

View file

@ -241,3 +241,21 @@ func WithRuntime(name string) func(*TestContainerConfig) {
c.HostConfig.Runtime = name c.HostConfig.Runtime = name
} }
} }
func WithCapability(capabilities ...string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.CapAdd = append(c.HostConfig.CapAdd, capabilities...)
}
}
func WithDropCapability(capabilities ...string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.CapDrop = append(c.HostConfig.CapDrop, capabilities...)
}
}
func WithSecurityOpt(opt string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.SecurityOpt = append(c.HostConfig.SecurityOpt, opt)
}
}

View file

@ -26,9 +26,7 @@ func (c *client) UpdateResources(ctx context.Context, containerID string, resour
return err return err
} }
// go doesn't like the alias in 1.8, this means this need to be return p.(containerd.Task).Update(ctx, containerd.WithResources(resources))
// platform specific
return p.(containerd.Task).Update(ctx, containerd.WithResources((*specs.LinuxResources)(resources)))
} }
func hostIDFromMap(id uint32, mp []specs.LinuxIDMapping) int { func hostIDFromMap(id uint32, mp []specs.LinuxIDMapping) int {

View file

@ -27,7 +27,7 @@ func InterfaceToStats(read time.Time, v interface{}) *Stats {
} }
// Resources defines updatable container resource values. TODO: it must match containerd upcoming API // Resources defines updatable container resource values. TODO: it must match containerd upcoming API
type Resources specs.LinuxResources type Resources = specs.LinuxResources
// Checkpoints contains the details of a checkpoint // Checkpoints contains the details of a checkpoint
type Checkpoints struct{} type Checkpoints struct{}

View file

@ -4,6 +4,9 @@ import specs "github.com/opencontainers/runtime-spec/specs-go"
// RemoveNamespace removes the `nsType` namespace from OCI spec `s` // RemoveNamespace removes the `nsType` namespace from OCI spec `s`
func RemoveNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) { func RemoveNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) {
if s.Linux == nil {
return
}
for i, n := range s.Linux.Namespaces { for i, n := range s.Linux.Namespaces {
if n.Type == nsType { if n.Type == nsType {
s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)

View file

@ -20,19 +20,13 @@ var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\
// SetCapabilities sets the provided capabilities on the spec // SetCapabilities sets the provided capabilities on the spec
// All capabilities are added if privileged is true. // All capabilities are added if privileged is true.
func SetCapabilities(s *specs.Spec, caplist []string) error { func SetCapabilities(s *specs.Spec, caplist []string) error {
// setUser has already been executed here if s.Process == nil {
if s.Process.User.UID == 0 { s.Process = &specs.Process{}
s.Process.Capabilities = &specs.LinuxCapabilities{ }
Effective: caplist, s.Process.Capabilities = &specs.LinuxCapabilities{
Bounding: caplist, Effective: caplist,
Permitted: caplist, Bounding: caplist,
} Permitted: caplist,
} else {
// Do not set Effective and Permitted capabilities for non-root users,
// to match what execve does.
s.Process.Capabilities = &specs.LinuxCapabilities{
Bounding: caplist,
}
} }
return nil return nil
} }

View file

@ -40,11 +40,13 @@ func getCurrentOOMScoreAdj() int {
func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error { func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error {
if len(v2Controllers) == 0 { if len(v2Controllers) == 0 {
// Remove cgroup settings. if spec.Linux != nil {
spec.Linux.Resources = nil // Remove cgroup settings.
spec.Linux.CgroupsPath = "" spec.Linux.Resources = nil
spec.Linux.CgroupsPath = ""
}
} else { } else {
if spec.Linux.Resources != nil { if spec.Linux != nil && spec.Linux.Resources != nil {
m := make(map[string]struct{}) m := make(map[string]struct{})
for _, s := range v2Controllers { for _, s := range v2Controllers {
m[s] = struct{}{} m[s] = struct{}{}
@ -77,7 +79,7 @@ func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int
} }
} }
if spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj { if spec.Process != nil && spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj {
*spec.Process.OOMScoreAdj = currentOOMScoreAdj *spec.Process.OOMScoreAdj = currentOOMScoreAdj
} }
@ -110,6 +112,9 @@ func isHostNS(spec *specs.Spec, nsType specs.LinuxNamespaceType) (bool, error) {
if strings.Contains(string(nsType), string(os.PathSeparator)) { if strings.Contains(string(nsType), string(os.PathSeparator)) {
return false, fmt.Errorf("unexpected namespace type %q", nsType) return false, fmt.Errorf("unexpected namespace type %q", nsType)
} }
if spec.Linux == nil {
return false, nil
}
for _, ns := range spec.Linux.Namespaces { for _, ns := range spec.Linux.Namespaces {
if ns.Type == nsType { if ns.Type == nsType {
if ns.Path == "" { if ns.Path == "" {
@ -144,15 +149,17 @@ func bindMountHostProcfs(spec *specs.Spec) error {
} }
} }
// Remove ReadonlyPaths for /proc/* if spec.Linux != nil {
newROP := spec.Linux.ReadonlyPaths[:0] // Remove ReadonlyPaths for /proc/*
for _, s := range spec.Linux.ReadonlyPaths { newROP := spec.Linux.ReadonlyPaths[:0]
s = path.Clean(s) for _, s := range spec.Linux.ReadonlyPaths {
if !strings.HasPrefix(s, "/proc/") { s = path.Clean(s)
newROP = append(newROP, s) if !strings.HasPrefix(s, "/proc/") {
newROP = append(newROP, s)
}
} }
spec.Linux.ReadonlyPaths = newROP
} }
spec.Linux.ReadonlyPaths = newROP
return nil return nil
} }