Merge pull request #46222 from thaJeztah/23.0_backport_capabilites
[23.0 backport] Do not drop effective&permitted set
This commit is contained in:
commit
436bcf7b33
16 changed files with 383 additions and 79 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 = "a
|
cpu.Period = &period
|
||||||
|
}
|
||||||
r.Memory = &specs.LinuxMemory{
|
if quota != 0 {
|
||||||
Limit: &resources.Memory,
|
cpu.Quota = "a
|
||||||
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)
|
||||||
|
|
11
daemon/update_linux_test.go
Normal file
11
daemon/update_linux_test.go
Normal 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{}))
|
||||||
|
}
|
108
integration/capabilities/capabilities_linux_test.go
Normal file
108
integration/capabilities/capabilities_linux_test.go
Normal 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)
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
integration/capabilities/main_linux_test.go
Normal file
33
integration/capabilities/main_linux_test.go
Normal 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) }
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -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:]...)
|
||||||
|
|
20
oci/oci.go
20
oci/oci.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue