Merge pull request #5017 from AkihiroSuda/parse-cap
oci.WithPrivileged: set the current caps, not the known caps
This commit is contained in:
		@@ -38,7 +38,6 @@ import (
 | 
				
			|||||||
	"github.com/opencontainers/runc/libcontainer/user"
 | 
						"github.com/opencontainers/runc/libcontainer/user"
 | 
				
			||||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
						specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
				
			||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"github.com/syndtr/gocapability/capability"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SpecOpts sets spec specific information to a newly generated OCI spec
 | 
					// SpecOpts sets spec specific information to a newly generated OCI spec
 | 
				
			||||||
@@ -776,29 +775,6 @@ func WithCapabilities(caps []string) SpecOpts {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithAllCapabilities sets all linux capabilities for the process
 | 
					 | 
				
			||||||
var WithAllCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
 | 
					 | 
				
			||||||
	return WithCapabilities(GetAllCapabilities())(ctx, client, c, s)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetAllCapabilities returns all caps up to CAP_LAST_CAP
 | 
					 | 
				
			||||||
// or CAP_BLOCK_SUSPEND on RHEL6
 | 
					 | 
				
			||||||
func GetAllCapabilities() []string {
 | 
					 | 
				
			||||||
	last := capability.CAP_LAST_CAP
 | 
					 | 
				
			||||||
	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
 | 
					 | 
				
			||||||
	if last == capability.Cap(63) {
 | 
					 | 
				
			||||||
		last = capability.CAP_BLOCK_SUSPEND
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var caps []string
 | 
					 | 
				
			||||||
	for _, cap := range capability.List() {
 | 
					 | 
				
			||||||
		if cap > last {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		caps = append(caps, "CAP_"+strings.ToUpper(cap.String()))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return caps
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func capsContain(caps []string, s string) bool {
 | 
					func capsContain(caps []string, s string) bool {
 | 
				
			||||||
	for _, c := range caps {
 | 
						for _, c := range caps {
 | 
				
			||||||
		if c == s {
 | 
							if c == s {
 | 
				
			||||||
@@ -1132,7 +1108,7 @@ func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// WithPrivileged sets up options for a privileged container
 | 
					// WithPrivileged sets up options for a privileged container
 | 
				
			||||||
var WithPrivileged = Compose(
 | 
					var WithPrivileged = Compose(
 | 
				
			||||||
	WithAllCapabilities,
 | 
						WithAllCurrentCapabilities,
 | 
				
			||||||
	WithMaskedPaths(nil),
 | 
						WithMaskedPaths(nil),
 | 
				
			||||||
	WithReadonlyPaths(nil),
 | 
						WithReadonlyPaths(nil),
 | 
				
			||||||
	WithWriteableSysfs,
 | 
						WithWriteableSysfs,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/containerd/containerd/containers"
 | 
						"github.com/containerd/containerd/containers"
 | 
				
			||||||
 | 
						"github.com/containerd/containerd/pkg/cap"
 | 
				
			||||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
						specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
				
			||||||
	"golang.org/x/sys/unix"
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -180,3 +181,19 @@ func WithCPUCFS(quota int64, period uint64) SpecOpts {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAllCurrentCapabilities propagates the effective capabilities of the caller process to the container process.
 | 
				
			||||||
 | 
					// The capability set may differ from WithAllKnownCapabilities when running in a container.
 | 
				
			||||||
 | 
					var WithAllCurrentCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
 | 
				
			||||||
 | 
						caps, err := cap.Current()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return WithCapabilities(caps)(ctx, client, c, s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAllKnownCapabilities sets all the the known linux capabilities for the container process
 | 
				
			||||||
 | 
					var WithAllKnownCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
 | 
				
			||||||
 | 
						caps := cap.Known()
 | 
				
			||||||
 | 
						return WithCapabilities(caps)(ctx, client, c, s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										108
									
								
								oci/spec_opts_linux_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								oci/spec_opts_linux_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					   Copyright The containerd Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package oci
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAddCaps(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var s specs.Spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := WithAddedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, cl := range [][]string{
 | 
				
			||||||
 | 
							s.Process.Capabilities.Bounding,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Effective,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Permitted,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Inheritable,
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							if !capsContain(cl, "CAP_CHOWN") {
 | 
				
			||||||
 | 
								t.Errorf("cap list %d does not contain added cap", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDropCaps(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var s specs.Spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := WithAllKnownCapabilities(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := WithDroppedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, cl := range [][]string{
 | 
				
			||||||
 | 
							s.Process.Capabilities.Bounding,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Effective,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Permitted,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Inheritable,
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							if capsContain(cl, "CAP_CHOWN") {
 | 
				
			||||||
 | 
								t.Errorf("cap list %d contains dropped cap", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add all capabilities back and drop a different cap.
 | 
				
			||||||
 | 
						if err := WithAllKnownCapabilities(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := WithDroppedCapabilities([]string{"CAP_FOWNER"})(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, cl := range [][]string{
 | 
				
			||||||
 | 
							s.Process.Capabilities.Bounding,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Effective,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Permitted,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Inheritable,
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							if capsContain(cl, "CAP_FOWNER") {
 | 
				
			||||||
 | 
								t.Errorf("cap list %d contains dropped cap", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !capsContain(cl, "CAP_CHOWN") {
 | 
				
			||||||
 | 
								t.Errorf("cap list %d doesn't contain non-dropped cap", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Drop all duplicated caps.
 | 
				
			||||||
 | 
						if err := WithCapabilities([]string{"CAP_CHOWN", "CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := WithDroppedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, cl := range [][]string{
 | 
				
			||||||
 | 
							s.Process.Capabilities.Bounding,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Effective,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Permitted,
 | 
				
			||||||
 | 
							s.Process.Capabilities.Inheritable,
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							if len(cl) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("cap list %d is not empty", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								oci/spec_opts_nonlinux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								oci/spec_opts_nonlinux.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// +build !linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					   Copyright The containerd Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package oci
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/containerd/containers"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAllCurrentCapabilities propagates the effective capabilities of the caller process to the container process.
 | 
				
			||||||
 | 
					// The capability set may differ from WithAllKnownCapabilities when running in a container.
 | 
				
			||||||
 | 
					//nolint: deadcode, unused
 | 
				
			||||||
 | 
					var WithAllCurrentCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
 | 
				
			||||||
 | 
						return WithCapabilities(nil)(ctx, client, c, s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAllKnownCapabilities sets all the the known linux capabilities for the container process
 | 
				
			||||||
 | 
					//nolint: deadcode, unused
 | 
				
			||||||
 | 
					var WithAllKnownCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
 | 
				
			||||||
 | 
						return WithCapabilities(nil)(ctx, client, c, s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -549,90 +549,6 @@ func TestWithImageConfigArgs(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAddCaps(t *testing.T) {
 | 
					 | 
				
			||||||
	t.Parallel()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var s specs.Spec
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := WithAddedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i, cl := range [][]string{
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Bounding,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Effective,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Permitted,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Inheritable,
 | 
					 | 
				
			||||||
	} {
 | 
					 | 
				
			||||||
		if !capsContain(cl, "CAP_CHOWN") {
 | 
					 | 
				
			||||||
			t.Errorf("cap list %d does not contain added cap", i)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDropCaps(t *testing.T) {
 | 
					 | 
				
			||||||
	t.Parallel()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var s specs.Spec
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := WithAllCapabilities(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := WithDroppedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, cl := range [][]string{
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Bounding,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Effective,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Permitted,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Inheritable,
 | 
					 | 
				
			||||||
	} {
 | 
					 | 
				
			||||||
		if capsContain(cl, "CAP_CHOWN") {
 | 
					 | 
				
			||||||
			t.Errorf("cap list %d contains dropped cap", i)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Add all capabilities back and drop a different cap.
 | 
					 | 
				
			||||||
	if err := WithAllCapabilities(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := WithDroppedCapabilities([]string{"CAP_FOWNER"})(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, cl := range [][]string{
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Bounding,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Effective,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Permitted,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Inheritable,
 | 
					 | 
				
			||||||
	} {
 | 
					 | 
				
			||||||
		if capsContain(cl, "CAP_FOWNER") {
 | 
					 | 
				
			||||||
			t.Errorf("cap list %d contains dropped cap", i)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !capsContain(cl, "CAP_CHOWN") {
 | 
					 | 
				
			||||||
			t.Errorf("cap list %d doesn't contain non-dropped cap", i)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Drop all duplicated caps.
 | 
					 | 
				
			||||||
	if err := WithCapabilities([]string{"CAP_CHOWN", "CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := WithDroppedCapabilities([]string{"CAP_CHOWN"})(context.Background(), nil, nil, &s); err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for i, cl := range [][]string{
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Bounding,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Effective,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Permitted,
 | 
					 | 
				
			||||||
		s.Process.Capabilities.Inheritable,
 | 
					 | 
				
			||||||
	} {
 | 
					 | 
				
			||||||
		if len(cl) != 0 {
 | 
					 | 
				
			||||||
			t.Errorf("cap list %d is not empty", i)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestDevShmSize(t *testing.T) {
 | 
					func TestDevShmSize(t *testing.T) {
 | 
				
			||||||
	t.Parallel()
 | 
						t.Parallel()
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/containerd/containerd/containers"
 | 
						"github.com/containerd/containerd/containers"
 | 
				
			||||||
	"github.com/containerd/containerd/namespaces"
 | 
						"github.com/containerd/containerd/namespaces"
 | 
				
			||||||
 | 
						"github.com/containerd/containerd/pkg/testutil"
 | 
				
			||||||
	specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
						specs "github.com/opencontainers/runtime-spec/specs-go"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -251,6 +252,10 @@ func TestPopulateDefaultUnixSpec(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestWithPrivileged(t *testing.T) {
 | 
					func TestWithPrivileged(t *testing.T) {
 | 
				
			||||||
	t.Parallel()
 | 
						t.Parallel()
 | 
				
			||||||
 | 
						if runtime.GOOS == "linux" {
 | 
				
			||||||
 | 
							// because WithPrivileged depends on CapEff in /proc/self/status
 | 
				
			||||||
 | 
							testutil.RequiresRoot(t)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := namespaces.WithNamespace(context.Background(), "testing")
 | 
						ctx := namespaces.WithNamespace(context.Background(), "testing")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -272,6 +277,10 @@ func TestWithPrivileged(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if runtime.GOOS != "linux" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(s.Process.Capabilities.Bounding) == 0 {
 | 
						if len(s.Process.Capabilities.Bounding) == 0 {
 | 
				
			||||||
		t.Error("Expected capabilities to be set with privileged")
 | 
							t.Error("Expected capabilities to be set with privileged")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										172
									
								
								pkg/cap/cap_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								pkg/cap/cap_linux.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					   Copyright The containerd Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package cap provides Linux capability utility
 | 
				
			||||||
 | 
					package cap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/syndtr/gocapability/capability"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FromUint64 parses an integer into string slice like
 | 
				
			||||||
 | 
					// []{"CAP_SYS_ADMIN", ...}.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unknown cap numbers are returned as []int.
 | 
				
			||||||
 | 
					func FromUint64(v uint64) ([]string, []int) {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							res     []string
 | 
				
			||||||
 | 
							unknown []int
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						knownList := capability.List()
 | 
				
			||||||
 | 
						known := make(map[string]struct{}, len(knownList))
 | 
				
			||||||
 | 
						for _, f := range knownList {
 | 
				
			||||||
 | 
							known[f.String()] = struct{}{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := 0; i <= 63; i++ {
 | 
				
			||||||
 | 
							if b := (v >> i) & 0x1; b == 0x1 {
 | 
				
			||||||
 | 
								c := capability.Cap(i)
 | 
				
			||||||
 | 
								sRaw := c.String()
 | 
				
			||||||
 | 
								if _, ok := known[sRaw]; ok {
 | 
				
			||||||
 | 
									s := "CAP_" + strings.ToUpper(sRaw)
 | 
				
			||||||
 | 
									res = append(res, s)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									unknown = append(unknown, i)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, unknown
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseProcPIDStatus returns uint64 value from /proc/<PID>/status file
 | 
				
			||||||
 | 
					func ParseProcPIDStatus(r io.Reader) (map[capability.CapType]uint64, error) {
 | 
				
			||||||
 | 
						res := make(map[capability.CapType]uint64)
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(r)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							line := scanner.Text()
 | 
				
			||||||
 | 
							pair := strings.SplitN(line, ":", 2)
 | 
				
			||||||
 | 
							if len(pair) != 2 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							k := strings.TrimSpace(pair[0])
 | 
				
			||||||
 | 
							v := strings.TrimSpace(pair[1])
 | 
				
			||||||
 | 
							switch k {
 | 
				
			||||||
 | 
							case "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb":
 | 
				
			||||||
 | 
								ui64, err := strconv.ParseUint(v, 16, 64)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, errors.Errorf("failed to parse line %q", line)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								switch k {
 | 
				
			||||||
 | 
								case "CapInh":
 | 
				
			||||||
 | 
									res[capability.INHERITABLE] = ui64
 | 
				
			||||||
 | 
								case "CapPrm":
 | 
				
			||||||
 | 
									res[capability.PERMITTED] = ui64
 | 
				
			||||||
 | 
								case "CapEff":
 | 
				
			||||||
 | 
									res[capability.EFFECTIVE] = ui64
 | 
				
			||||||
 | 
								case "CapBnd":
 | 
				
			||||||
 | 
									res[capability.BOUNDING] = ui64
 | 
				
			||||||
 | 
								case "CapAmb":
 | 
				
			||||||
 | 
									res[capability.AMBIENT] = ui64
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := scanner.Err(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Current returns the list of the effective and the known caps of
 | 
				
			||||||
 | 
					// the current process.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The result is like []string{"CAP_SYS_ADMIN", ...}.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The result does not contain caps that are not recognized by
 | 
				
			||||||
 | 
					// the "github.com/syndtr/gocapability" library.
 | 
				
			||||||
 | 
					func Current() ([]string, error) {
 | 
				
			||||||
 | 
						f, err := os.Open("/proc/self/status")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
						caps, err := ParseProcPIDStatus(f)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						capEff := caps[capability.EFFECTIVE]
 | 
				
			||||||
 | 
						names, _ := FromUint64(capEff)
 | 
				
			||||||
 | 
						return names, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// caps35 is the caps of kernel 3.5 (37 entries)
 | 
				
			||||||
 | 
						caps35 = []string{
 | 
				
			||||||
 | 
							"CAP_CHOWN",            // 2.2
 | 
				
			||||||
 | 
							"CAP_DAC_OVERRIDE",     // 2.2
 | 
				
			||||||
 | 
							"CAP_DAC_READ_SEARCH",  // 2.2
 | 
				
			||||||
 | 
							"CAP_FOWNER",           // 2.2
 | 
				
			||||||
 | 
							"CAP_FSETID",           // 2.2
 | 
				
			||||||
 | 
							"CAP_KILL",             // 2.2
 | 
				
			||||||
 | 
							"CAP_SETGID",           // 2.2
 | 
				
			||||||
 | 
							"CAP_SETUID",           // 2.2
 | 
				
			||||||
 | 
							"CAP_SETPCAP",          // 2.2
 | 
				
			||||||
 | 
							"CAP_LINUX_IMMUTABLE",  // 2.2
 | 
				
			||||||
 | 
							"CAP_NET_BIND_SERVICE", // 2.2
 | 
				
			||||||
 | 
							"CAP_NET_BROADCAST",    // 2.2
 | 
				
			||||||
 | 
							"CAP_NET_ADMIN",        // 2.2
 | 
				
			||||||
 | 
							"CAP_NET_RAW",          // 2.2
 | 
				
			||||||
 | 
							"CAP_IPC_LOCK",         // 2.2
 | 
				
			||||||
 | 
							"CAP_IPC_OWNER",        // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_MODULE",       // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_RAWIO",        // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_CHROOT",       // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_PTRACE",       // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_PACCT",        // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_ADMIN",        // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_BOOT",         // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_NICE",         // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_RESOURCE",     // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_TIME",         // 2.2
 | 
				
			||||||
 | 
							"CAP_SYS_TTY_CONFIG",   // 2.2
 | 
				
			||||||
 | 
							"CAP_MKNOD",            // 2.4
 | 
				
			||||||
 | 
							"CAP_LEASE",            // 2.4
 | 
				
			||||||
 | 
							"CAP_AUDIT_WRITE",      // 2.6.11
 | 
				
			||||||
 | 
							"CAP_AUDIT_CONTROL",    // 2.6.11
 | 
				
			||||||
 | 
							"CAP_SETFCAP",          // 2.6.24
 | 
				
			||||||
 | 
							"CAP_MAC_OVERRIDE",     // 2.6.25
 | 
				
			||||||
 | 
							"CAP_MAC_ADMIN",        // 2.6.25
 | 
				
			||||||
 | 
							"CAP_SYSLOG",           // 2.6.37
 | 
				
			||||||
 | 
							"CAP_WAKE_ALARM",       // 3.0
 | 
				
			||||||
 | 
							"CAP_BLOCK_SUSPEND",    // 3.5
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// caps316 is the caps of kernel 3.16 (38 entries)
 | 
				
			||||||
 | 
						caps316 = append(caps35, "CAP_AUDIT_READ")
 | 
				
			||||||
 | 
						// caps58 is the caps of kernel 5.8 (40 entries)
 | 
				
			||||||
 | 
						caps58 = append(caps316, []string{"CAP_PERFMON", "CAP_BPF"}...)
 | 
				
			||||||
 | 
						// caps59 is the caps of kernel 5.9 (41 entries)
 | 
				
			||||||
 | 
						caps59 = append(caps58, "CAP_CHECKPOINT_RESTORE")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Known returns the known cap strings as of kernel 5.9
 | 
				
			||||||
 | 
					func Known() []string {
 | 
				
			||||||
 | 
						return caps59
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										161
									
								
								pkg/cap/cap_linux_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								pkg/cap/cap_linux_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					   Copyright The containerd Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					   you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					   You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					   distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					   See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					   limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package cap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/syndtr/gocapability/capability"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCapsList(t *testing.T) {
 | 
				
			||||||
 | 
						assert.Len(t, caps316, 38)
 | 
				
			||||||
 | 
						assert.Len(t, caps58, 40)
 | 
				
			||||||
 | 
						assert.Len(t, caps59, 41)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFromUint64(t *testing.T) {
 | 
				
			||||||
 | 
						type testCase struct {
 | 
				
			||||||
 | 
							comment    string
 | 
				
			||||||
 | 
							v          uint64
 | 
				
			||||||
 | 
							knownNames []string
 | 
				
			||||||
 | 
							unknown    []int
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testCases := []testCase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								comment: "No cap",
 | 
				
			||||||
 | 
								v:       0x0000000000000000,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								// 3.10 (same caps as 3.5) is the oldest kernel version we want to support
 | 
				
			||||||
 | 
								comment:    "All caps on kernel 3.5 (last = CAP_BLOCK_SUSPEND)",
 | 
				
			||||||
 | 
								v:          0x0000001fffffffff,
 | 
				
			||||||
 | 
								knownNames: caps35,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								comment:    "All caps on kernel 3.16 (last = CAP_AUDIT_READ)",
 | 
				
			||||||
 | 
								v:          0x0000003fffffffff,
 | 
				
			||||||
 | 
								knownNames: caps316,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								comment:    "All caps on kernel 5.8 (last = CAP_BPF)",
 | 
				
			||||||
 | 
								v:          0x000000ffffffffff,
 | 
				
			||||||
 | 
								knownNames: caps58,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								comment:    "All caps on kernel 5.9 (last = CAP_CHECKPOINT_RESTORE)",
 | 
				
			||||||
 | 
								v:          0x000001ffffffffff,
 | 
				
			||||||
 | 
								knownNames: caps59,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								comment:    "Unknown caps",
 | 
				
			||||||
 | 
								v:          0xf00001ffffffffff,
 | 
				
			||||||
 | 
								knownNames: caps59,
 | 
				
			||||||
 | 
								unknown:    []int{60, 61, 62, 63},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range testCases {
 | 
				
			||||||
 | 
							knownNames, unknown := FromUint64(tc.v)
 | 
				
			||||||
 | 
							t.Logf("[%s] v=0x%x, got=%+v (%d entries), unknown=%v",
 | 
				
			||||||
 | 
								tc.comment, tc.v, knownNames, len(knownNames), unknown)
 | 
				
			||||||
 | 
							assert.Equal(t, tc.knownNames, knownNames)
 | 
				
			||||||
 | 
							assert.Equal(t, tc.unknown, unknown)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestParseProcPIDStatus(t *testing.T) {
 | 
				
			||||||
 | 
						procPIDStatus := `Name:   cat
 | 
				
			||||||
 | 
					Umask:  0022
 | 
				
			||||||
 | 
					State:  R (running)
 | 
				
			||||||
 | 
					Tgid:   170065
 | 
				
			||||||
 | 
					Ngid:   0
 | 
				
			||||||
 | 
					Pid:    170065
 | 
				
			||||||
 | 
					PPid:   170064
 | 
				
			||||||
 | 
					TracerPid:      0
 | 
				
			||||||
 | 
					Uid:    0       0       0       0
 | 
				
			||||||
 | 
					Gid:    0       0       0       0
 | 
				
			||||||
 | 
					FDSize: 64
 | 
				
			||||||
 | 
					Groups: 0
 | 
				
			||||||
 | 
					NStgid: 170065
 | 
				
			||||||
 | 
					NSpid:  170065
 | 
				
			||||||
 | 
					NSpgid: 170064
 | 
				
			||||||
 | 
					NSsid:  3784
 | 
				
			||||||
 | 
					VmPeak:     8216 kB
 | 
				
			||||||
 | 
					VmSize:     8216 kB
 | 
				
			||||||
 | 
					VmLck:         0 kB
 | 
				
			||||||
 | 
					VmPin:         0 kB
 | 
				
			||||||
 | 
					VmHWM:       676 kB
 | 
				
			||||||
 | 
					VmRSS:       676 kB
 | 
				
			||||||
 | 
					RssAnon:              72 kB
 | 
				
			||||||
 | 
					RssFile:             604 kB
 | 
				
			||||||
 | 
					RssShmem:              0 kB
 | 
				
			||||||
 | 
					VmData:      324 kB
 | 
				
			||||||
 | 
					VmStk:       132 kB
 | 
				
			||||||
 | 
					VmExe:        20 kB
 | 
				
			||||||
 | 
					VmLib:      1612 kB
 | 
				
			||||||
 | 
					VmPTE:        56 kB
 | 
				
			||||||
 | 
					VmSwap:        0 kB
 | 
				
			||||||
 | 
					HugetlbPages:          0 kB
 | 
				
			||||||
 | 
					CoreDumping:    0
 | 
				
			||||||
 | 
					THP_enabled:    1
 | 
				
			||||||
 | 
					Threads:        1
 | 
				
			||||||
 | 
					SigQ:   0/63692
 | 
				
			||||||
 | 
					SigPnd: 0000000000000000
 | 
				
			||||||
 | 
					ShdPnd: 0000000000000000
 | 
				
			||||||
 | 
					SigBlk: 0000000000000000
 | 
				
			||||||
 | 
					SigIgn: 0000000000000000
 | 
				
			||||||
 | 
					SigCgt: 0000000000000000
 | 
				
			||||||
 | 
					CapInh: 0000000000000000
 | 
				
			||||||
 | 
					CapPrm: 000000ffffffffff
 | 
				
			||||||
 | 
					CapEff: 000000ffffffffff
 | 
				
			||||||
 | 
					CapBnd: 000000ffffffffff
 | 
				
			||||||
 | 
					CapAmb: 0000000000000000
 | 
				
			||||||
 | 
					NoNewPrivs:     0
 | 
				
			||||||
 | 
					Seccomp:        0
 | 
				
			||||||
 | 
					Speculation_Store_Bypass:       thread vulnerable
 | 
				
			||||||
 | 
					Cpus_allowed:   00000000,00000000,00000000,0000000f
 | 
				
			||||||
 | 
					Cpus_allowed_list:      0-3
 | 
				
			||||||
 | 
					Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
 | 
				
			||||||
 | 
					Mems_allowed_list:      0
 | 
				
			||||||
 | 
					voluntary_ctxt_switches:        0
 | 
				
			||||||
 | 
					nonvoluntary_ctxt_switches:     0
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
						res, err := ParseProcPIDStatus(strings.NewReader(procPIDStatus))
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						expected := map[capability.CapType]uint64{
 | 
				
			||||||
 | 
							capability.INHERITABLE: 0,
 | 
				
			||||||
 | 
							capability.PERMITTED:   0xffffffffff,
 | 
				
			||||||
 | 
							capability.EFFECTIVE:   0xffffffffff,
 | 
				
			||||||
 | 
							capability.BOUNDING:    0xffffffffff,
 | 
				
			||||||
 | 
							capability.AMBIENT:     0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.EqualValues(t, expected, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCurrent(t *testing.T) {
 | 
				
			||||||
 | 
						caps, err := Current()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						t.Logf("verify the result manually: %+v", caps)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestKnown(t *testing.T) {
 | 
				
			||||||
 | 
						caps := Known()
 | 
				
			||||||
 | 
						assert.EqualValues(t, caps59, caps)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -354,7 +354,7 @@ func WithDevices(osi osinterface.OS, config *runtime.ContainerConfig) oci.SpecOp
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithCapabilities sets the provided capabilities from the security context
 | 
					// WithCapabilities sets the provided capabilities from the security context
 | 
				
			||||||
func WithCapabilities(sc *runtime.LinuxContainerSecurityContext) oci.SpecOpts {
 | 
					func WithCapabilities(sc *runtime.LinuxContainerSecurityContext, allCaps []string) oci.SpecOpts {
 | 
				
			||||||
	capabilities := sc.GetCapabilities()
 | 
						capabilities := sc.GetCapabilities()
 | 
				
			||||||
	if capabilities == nil {
 | 
						if capabilities == nil {
 | 
				
			||||||
		return nullOpt
 | 
							return nullOpt
 | 
				
			||||||
@@ -366,7 +366,7 @@ func WithCapabilities(sc *runtime.LinuxContainerSecurityContext) oci.SpecOpts {
 | 
				
			|||||||
	// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
 | 
						// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
 | 
				
			||||||
	// will be all capabilities without `CAP_CHOWN`.
 | 
						// will be all capabilities without `CAP_CHOWN`.
 | 
				
			||||||
	if util.InStringSlice(capabilities.GetAddCapabilities(), "ALL") {
 | 
						if util.InStringSlice(capabilities.GetAddCapabilities(), "ALL") {
 | 
				
			||||||
		opts = append(opts, oci.WithAllCapabilities)
 | 
							opts = append(opts, oci.WithCapabilities(allCaps))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if util.InStringSlice(capabilities.GetDropCapabilities(), "ALL") {
 | 
						if util.InStringSlice(capabilities.GetDropCapabilities(), "ALL") {
 | 
				
			||||||
		opts = append(opts, oci.WithCapabilities(nil))
 | 
							opts = append(opts, oci.WithCapabilities(nil))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -217,10 +217,12 @@ func (c *criService) containerSpec(
 | 
				
			|||||||
			specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed)
 | 
								specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// add requested devices by the config as host devices are not automatically added
 | 
								// add requested devices by the config as host devices are not automatically added
 | 
				
			||||||
			specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
 | 
								specOpts = append(specOpts, customopts.WithDevices(c.os, config),
 | 
				
			||||||
 | 
									customopts.WithCapabilities(securityContext, c.allCaps))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else { // not privileged
 | 
						} else { // not privileged
 | 
				
			||||||
		specOpts = append(specOpts, customopts.WithDevices(c.os, config), customopts.WithCapabilities(securityContext))
 | 
							specOpts = append(specOpts, customopts.WithDevices(c.os, config),
 | 
				
			||||||
 | 
								customopts.WithCapabilities(securityContext, c.allCaps))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Clear all ambient capabilities. The implication of non-root + caps
 | 
						// Clear all ambient capabilities. The implication of non-root + caps
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ import (
 | 
				
			|||||||
	"github.com/stretchr/testify/require"
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
 | 
						runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/containerd/pkg/cap"
 | 
				
			||||||
	"github.com/containerd/containerd/pkg/cri/annotations"
 | 
						"github.com/containerd/containerd/pkg/cri/annotations"
 | 
				
			||||||
	"github.com/containerd/containerd/pkg/cri/config"
 | 
						"github.com/containerd/containerd/pkg/cri/config"
 | 
				
			||||||
	"github.com/containerd/containerd/pkg/cri/opts"
 | 
						"github.com/containerd/containerd/pkg/cri/opts"
 | 
				
			||||||
@@ -191,6 +192,7 @@ func TestContainerCapabilities(t *testing.T) {
 | 
				
			|||||||
	testSandboxID := "sandbox-id"
 | 
						testSandboxID := "sandbox-id"
 | 
				
			||||||
	testContainerName := "container-name"
 | 
						testContainerName := "container-name"
 | 
				
			||||||
	testPid := uint32(1234)
 | 
						testPid := uint32(1234)
 | 
				
			||||||
 | 
						allCaps := cap.Known()
 | 
				
			||||||
	for desc, test := range map[string]struct {
 | 
						for desc, test := range map[string]struct {
 | 
				
			||||||
		capability *runtime.Capability
 | 
							capability *runtime.Capability
 | 
				
			||||||
		includes   []string
 | 
							includes   []string
 | 
				
			||||||
@@ -208,20 +210,20 @@ func TestContainerCapabilities(t *testing.T) {
 | 
				
			|||||||
			capability: &runtime.Capability{
 | 
								capability: &runtime.Capability{
 | 
				
			||||||
				AddCapabilities: []string{"ALL"},
 | 
									AddCapabilities: []string{"ALL"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			includes: oci.GetAllCapabilities(),
 | 
								includes: allCaps,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"should be able to drop all capabilities": {
 | 
							"should be able to drop all capabilities": {
 | 
				
			||||||
			capability: &runtime.Capability{
 | 
								capability: &runtime.Capability{
 | 
				
			||||||
				DropCapabilities: []string{"ALL"},
 | 
									DropCapabilities: []string{"ALL"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			excludes: oci.GetAllCapabilities(),
 | 
								excludes: allCaps,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"should be able to drop capabilities with add all": {
 | 
							"should be able to drop capabilities with add all": {
 | 
				
			||||||
			capability: &runtime.Capability{
 | 
								capability: &runtime.Capability{
 | 
				
			||||||
				AddCapabilities:  []string{"ALL"},
 | 
									AddCapabilities:  []string{"ALL"},
 | 
				
			||||||
				DropCapabilities: []string{"CHOWN"},
 | 
									DropCapabilities: []string{"CHOWN"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			includes: util.SubtractStringSlice(oci.GetAllCapabilities(), "CAP_CHOWN"),
 | 
								includes: util.SubtractStringSlice(allCaps, "CAP_CHOWN"),
 | 
				
			||||||
			excludes: []string{"CAP_CHOWN"},
 | 
								excludes: []string{"CAP_CHOWN"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"should be able to add capabilities with drop all": {
 | 
							"should be able to add capabilities with drop all": {
 | 
				
			||||||
@@ -230,13 +232,14 @@ func TestContainerCapabilities(t *testing.T) {
 | 
				
			|||||||
				DropCapabilities: []string{"ALL"},
 | 
									DropCapabilities: []string{"ALL"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			includes: []string{"CAP_SYS_ADMIN"},
 | 
								includes: []string{"CAP_SYS_ADMIN"},
 | 
				
			||||||
			excludes: util.SubtractStringSlice(oci.GetAllCapabilities(), "CAP_SYS_ADMIN"),
 | 
								excludes: util.SubtractStringSlice(allCaps, "CAP_SYS_ADMIN"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		t.Logf("TestCase %q", desc)
 | 
							t.Logf("TestCase %q", desc)
 | 
				
			||||||
		containerConfig, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
 | 
							containerConfig, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
 | 
				
			||||||
		ociRuntime := config.Runtime{}
 | 
							ociRuntime := config.Runtime{}
 | 
				
			||||||
		c := newTestCRIService()
 | 
							c := newTestCRIService()
 | 
				
			||||||
 | 
							c.allCaps = allCaps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		containerConfig.Linux.SecurityContext.Capabilities = test.capability
 | 
							containerConfig.Linux.SecurityContext.Capabilities = test.capability
 | 
				
			||||||
		spec, err := c.containerSpec(testID, testSandboxID, testPid, "", testContainerName, testImageName, containerConfig, sandboxConfig, imageConfig, nil, ociRuntime)
 | 
							spec, err := c.containerSpec(testID, testSandboxID, testPid, "", testContainerName, testImageName, containerConfig, sandboxConfig, imageConfig, nil, ociRuntime)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,9 @@ type criService struct {
 | 
				
			|||||||
	cniNetConfMonitor *cniNetConfSyncer
 | 
						cniNetConfMonitor *cniNetConfSyncer
 | 
				
			||||||
	// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
 | 
						// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
 | 
				
			||||||
	baseOCISpecs map[string]*oci.Spec
 | 
						baseOCISpecs map[string]*oci.Spec
 | 
				
			||||||
 | 
						// allCaps is the list of the capabilities.
 | 
				
			||||||
 | 
						// When nil, parsed from CapEff of /proc/self/status.
 | 
				
			||||||
 | 
						allCaps []string // nolint
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewCRIService returns a new instance of CRIService
 | 
					// NewCRIService returns a new instance of CRIService
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@
 | 
				
			|||||||
package server
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/containerd/containerd/pkg/cap"
 | 
				
			||||||
	"github.com/containerd/containerd/sys"
 | 
						"github.com/containerd/containerd/sys"
 | 
				
			||||||
	cni "github.com/containerd/go-cni"
 | 
						cni "github.com/containerd/go-cni"
 | 
				
			||||||
	"github.com/opencontainers/selinux/go-selinux"
 | 
						"github.com/opencontainers/selinux/go-selinux"
 | 
				
			||||||
@@ -61,6 +62,13 @@ func (c *criService) initPlatform() error {
 | 
				
			|||||||
		return errors.Wrap(err, "failed to initialize cni")
 | 
							return errors.Wrap(err, "failed to initialize cni")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if c.allCaps == nil {
 | 
				
			||||||
 | 
							c.allCaps, err = cap.Current()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return errors.Wrap(err, "failed to get caps")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user