Merge pull request #3358 from dweomer/fix/3296/caps

containerd: v1.4.4-k3s2
This commit is contained in:
Erik Wilson 2021-05-20 13:01:19 -07:00 committed by GitHub
commit c2e561f25e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 696 additions and 237 deletions

4
go.mod
View File

@ -8,9 +8,9 @@ replace (
github.com/containerd/btrfs => github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e
github.com/containerd/cgroups => github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340
github.com/containerd/console => github.com/containerd/console v1.0.0
github.com/containerd/containerd => github.com/k3s-io/containerd v1.4.4-k3s1
github.com/containerd/containerd => github.com/k3s-io/containerd v1.4.4-k3s2 // k3s-release/1.4
github.com/containerd/continuity => github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1
github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.5 // k3s-release/1.4
github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.6 // k3s-release/1.4
github.com/containerd/fifo => github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b
github.com/containerd/go-runc => github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328
github.com/containerd/ttrpc => github.com/containerd/ttrpc v1.0.1

8
go.sum
View File

@ -530,12 +530,12 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k3s-io/containerd v1.4.4-k3s1 h1:9D9ynaNMW0vT/Kycr7qLstBGby6T84iJpbKFXKGhAPk=
github.com/k3s-io/containerd v1.4.4-k3s1/go.mod h1:M0uyTU3ZQ0WEBn5YcYPDs/7LpW/0mqjt5im7Wn2z1kc=
github.com/k3s-io/containerd v1.4.4-k3s2 h1:Jgpa+oLISVQuib3YEM9lNCG/rmJwhVW95PNVuxsUZdM=
github.com/k3s-io/containerd v1.4.4-k3s2/go.mod h1:0fNz8l5ogmvtVmaK9rW4mUxz6doAlDqFv/CrxGUSiyo=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1 h1:KEz2rd9IDbrQT8w6RibEYlwfTXiu0P6hQDE+6O4IJdI=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/k3s-io/cri v1.4.0-k3s.5 h1:Uh7QkRKibPJqSBbtCtgpjYsdNVcx3Tx+7HHlhxKBtu8=
github.com/k3s-io/cri v1.4.0-k3s.5/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0=
github.com/k3s-io/cri v1.4.0-k3s.6 h1:Hn/0KRtv62B/KNT8oULl1hWnViyuJ+RB0x9y9zZsmCw=
github.com/k3s-io/cri v1.4.0-k3s.6/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0=
github.com/k3s-io/cri-tools v1.21.0-k3s1 h1:MWQtAsx4HCNXenqU/B4V9eU6HMyafkd1PnW6d4HCfos=
github.com/k3s-io/cri-tools v1.21.0-k3s1/go.mod h1:Qsz54zxINPR+WVWX9Kc3CTmuDFB1dNLCNV8jE8lUbtU=
github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea h1:7cwby0GoNAi8IsVrT0q+JfQpB6V76ZaEGhj6qts/mvU=

View File

@ -4,7 +4,7 @@ go 1.14
replace (
github.com/containerd/continuity => github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1
github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.5 // k3s-release/1.4
github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.6 // k3s-release/1.4
github.com/golang/protobuf => github.com/golang/protobuf v1.3.5
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
k8s.io/api => k8s.io/api v0.19.5
@ -73,7 +73,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.7.1
github.com/sirupsen/logrus v1.7.0
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/stretchr/testify v1.4.0
github.com/tchap/go-patricia v2.2.6+incompatible // indirect
github.com/urfave/cli v1.22.2
go.etcd.io/bbolt v1.3.5

View File

@ -402,8 +402,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1 h1:KEz2rd9IDbrQT8w6RibEYlwfTXiu0P6hQDE+6O4IJdI=
github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/k3s-io/cri v1.4.0-k3s.5 h1:Uh7QkRKibPJqSBbtCtgpjYsdNVcx3Tx+7HHlhxKBtu8=
github.com/k3s-io/cri v1.4.0-k3s.5/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0=
github.com/k3s-io/cri v1.4.0-k3s.6 h1:Hn/0KRtv62B/KNT8oULl1hWnViyuJ+RB0x9y9zZsmCw=
github.com/k3s-io/cri v1.4.0-k3s.6/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0=
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=

View File

@ -38,7 +38,6 @@ import (
"github.com/opencontainers/runc/libcontainer/user"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/syndtr/gocapability/capability"
)
// SpecOpts sets spec specific information to a newly generated OCI spec
@ -274,6 +273,28 @@ func WithMounts(mounts []specs.Mount) SpecOpts {
}
}
// WithoutMounts removes mounts
func WithoutMounts(dests ...string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
var (
mounts []specs.Mount
current = s.Mounts
)
mLoop:
for _, m := range current {
mDestination := filepath.Clean(m.Destination)
for _, dest := range dests {
if mDestination == dest {
continue mLoop
}
}
mounts = append(mounts, m)
}
s.Mounts = mounts
return nil
}
}
// WithHostNamespace allows a task to run inside the host's linux namespace
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
@ -295,10 +316,7 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
setLinux(s)
for i, n := range s.Linux.Namespaces {
if n.Type == ns.Type {
before := s.Linux.Namespaces[:i]
after := s.Linux.Namespaces[i+1:]
s.Linux.Namespaces = append(before, ns)
s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
s.Linux.Namespaces[i] = ns
return nil
}
}
@ -536,7 +554,7 @@ func WithUser(userstr string) SpecOpts {
}
f := func(root string) error {
if username != "" {
user, err := getUserFromPath(root, func(u user.User) bool {
user, err := UserFromPath(root, func(u user.User) bool {
return u.Name == username
})
if err != nil {
@ -545,7 +563,7 @@ func WithUser(userstr string) SpecOpts {
uid = uint32(user.Uid)
}
if groupname != "" {
gid, err = getGIDFromPath(root, func(g user.Group) bool {
gid, err = GIDFromPath(root, func(g user.Group) bool {
return g.Name == groupname
})
if err != nil {
@ -600,11 +618,11 @@ func WithUserID(uid uint32) SpecOpts {
if !isRootfsAbs(s.Root.Path) {
return errors.Errorf("rootfs absolute path is required")
}
user, err := getUserFromPath(s.Root.Path, func(u user.User) bool {
user, err := UserFromPath(s.Root.Path, func(u user.User) bool {
return u.Uid == int(uid)
})
if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound {
if os.IsNotExist(err) || err == ErrNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, 0
return nil
}
@ -626,11 +644,11 @@ func WithUserID(uid uint32) SpecOpts {
return err
}
return mount.WithTempMount(ctx, mounts, func(root string) error {
user, err := getUserFromPath(root, func(u user.User) bool {
user, err := UserFromPath(root, func(u user.User) bool {
return u.Uid == int(uid)
})
if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound {
if os.IsNotExist(err) || err == ErrNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, 0
return nil
}
@ -654,7 +672,7 @@ func WithUsername(username string) SpecOpts {
if !isRootfsAbs(s.Root.Path) {
return errors.Errorf("rootfs absolute path is required")
}
user, err := getUserFromPath(s.Root.Path, func(u user.User) bool {
user, err := UserFromPath(s.Root.Path, func(u user.User) bool {
return u.Name == username
})
if err != nil {
@ -675,7 +693,7 @@ func WithUsername(username string) SpecOpts {
return err
}
return mount.WithTempMount(ctx, mounts, func(root string) error {
user, err := getUserFromPath(root, func(u user.User) bool {
user, err := UserFromPath(root, func(u user.User) bool {
return u.Name == username
})
if err != nil {
@ -707,11 +725,11 @@ func WithAdditionalGIDs(userstr string) SpecOpts {
var username string
uid, err := strconv.Atoi(userstr)
if err == nil {
user, err := getUserFromPath(root, func(u user.User) bool {
user, err := UserFromPath(root, func(u user.User) bool {
return u.Uid == uid
})
if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound {
if os.IsNotExist(err) || err == ErrNoUsersFound {
return nil
}
return err
@ -776,29 +794,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 {
for _, c := range caps {
if c == s {
@ -869,9 +864,12 @@ func WithAmbientCapabilities(caps []string) SpecOpts {
}
}
var errNoUsersFound = errors.New("no users found")
// ErrNoUsersFound can be returned from UserFromPath
var ErrNoUsersFound = errors.New("no users found")
func getUserFromPath(root string, filter func(user.User) bool) (user.User, error) {
// UserFromPath inspects the user object using /etc/passwd in the specified rootfs.
// filter can be nil.
func UserFromPath(root string, filter func(user.User) bool) (user.User, error) {
ppath, err := fs.RootPath(root, "/etc/passwd")
if err != nil {
return user.User{}, err
@ -881,14 +879,17 @@ func getUserFromPath(root string, filter func(user.User) bool) (user.User, error
return user.User{}, err
}
if len(users) == 0 {
return user.User{}, errNoUsersFound
return user.User{}, ErrNoUsersFound
}
return users[0], nil
}
var errNoGroupsFound = errors.New("no groups found")
// ErrNoGroupsFound can be returned from GIDFromPath
var ErrNoGroupsFound = errors.New("no groups found")
func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
// GIDFromPath inspects the GID using /etc/passwd in the specified rootfs.
// filter can be nil.
func GIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) {
gpath, err := fs.RootPath(root, "/etc/group")
if err != nil {
return 0, err
@ -898,7 +899,7 @@ func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err
return 0, err
}
if len(groups) == 0 {
return 0, errNoGroupsFound
return 0, ErrNoGroupsFound
}
g := groups[0]
return uint32(g.Gid), nil
@ -948,16 +949,13 @@ func WithReadonlyPaths(paths []string) SpecOpts {
// WithWriteableSysfs makes any sysfs mounts writeable
func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
for i, m := range s.Mounts {
for _, m := range s.Mounts {
if m.Type == "sysfs" {
var options []string
for _, o := range m.Options {
for i, o := range m.Options {
if o == "ro" {
o = "rw"
m.Options[i] = "rw"
}
options = append(options, o)
}
s.Mounts[i].Options = options
}
}
return nil
@ -965,16 +963,13 @@ func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s
// WithWriteableCgroupfs makes any cgroup mounts writeable
func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
for i, m := range s.Mounts {
for _, m := range s.Mounts {
if m.Type == "cgroup" {
var options []string
for _, o := range m.Options {
for i, o := range m.Options {
if o == "ro" {
o = "rw"
m.Options[i] = "rw"
}
options = append(options, o)
}
s.Mounts[i].Options = options
}
}
return nil
@ -1126,7 +1121,7 @@ func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container
// WithPrivileged sets up options for a privileged container
var WithPrivileged = Compose(
WithAllCapabilities,
WithAllCurrentCapabilities,
WithMaskedPaths(nil),
WithReadonlyPaths(nil),
WithWriteableSysfs,
@ -1199,15 +1194,13 @@ func WithLinuxDevices(devices []specs.LinuxDevice) SpecOpts {
}
}
var ErrNotADevice = errors.New("not a device node")
// WithLinuxDevice adds the device specified by path to the spec
func WithLinuxDevice(path, permissions string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setLinux(s)
setResources(s)
dev, err := deviceFromPath(path, permissions)
dev, err := deviceFromPath(path)
if err != nil {
return err
}

View File

@ -20,20 +20,17 @@ package oci
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/pkg/cap"
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)
// WithHostDevices adds all the hosts device nodes to the container's spec
func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setLinux(s)
devs, err := getDevices("/dev")
devs, err := HostDevices()
if err != nil {
return err
}
@ -41,83 +38,28 @@ func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Sp
return nil
}
func getDevices(path string) ([]specs.LinuxDevice, error) {
files, err := ioutil.ReadDir(path)
// WithDevices recursively adds devices from the passed in path and associated cgroup rules for that device.
// If devicePath is a dir it traverses the dir to add all devices in that dir.
// If devicePath is not a dir, it attempts to add the single device.
// If containerPath is not set then the device path is used for the container path.
func WithDevices(devicePath, containerPath, permissions string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
devs, err := getDevices(devicePath, containerPath)
if err != nil {
return nil, err
return err
}
var out []specs.LinuxDevice
for _, f := range files {
switch {
case f.IsDir():
switch f.Name() {
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
// ".udev" added to address https://github.com/opencontainers/runc/issues/2093
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
continue
default:
sub, err := getDevices(filepath.Join(path, f.Name()))
if err != nil {
return nil, err
for _, dev := range devs {
s.Linux.Devices = append(s.Linux.Devices, dev)
s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, specs.LinuxDeviceCgroup{
Allow: true,
Type: dev.Type,
Major: &dev.Major,
Minor: &dev.Minor,
Access: permissions,
})
}
out = append(out, sub...)
continue
return nil
}
case f.Name() == "console":
continue
}
device, err := deviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil {
if err == ErrNotADevice {
continue
}
if os.IsNotExist(err) {
continue
}
return nil, err
}
out = append(out, *device)
}
return out, nil
}
func deviceFromPath(path, permissions string) (*specs.LinuxDevice, error) {
var stat unix.Stat_t
if err := unix.Lstat(path, &stat); err != nil {
return nil, err
}
var (
// The type is 32bit on mips.
devNumber = uint64(stat.Rdev) // nolint: unconvert
major = unix.Major(devNumber)
minor = unix.Minor(devNumber)
)
if major == 0 {
return nil, ErrNotADevice
}
var (
devType string
mode = stat.Mode
)
switch {
case mode&unix.S_IFBLK == unix.S_IFBLK:
devType = "b"
case mode&unix.S_IFCHR == unix.S_IFCHR:
devType = "c"
}
fm := os.FileMode(mode)
return &specs.LinuxDevice{
Type: devType,
Path: path,
Major: int64(major),
Minor: int64(minor),
FileMode: &fm,
UID: &stat.Uid,
GID: &stat.Gid,
}, nil
}
// WithMemorySwap sets the container's swap in bytes
@ -180,3 +122,24 @@ func WithCPUCFS(quota int64, period uint64) SpecOpts {
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)
}
// WithoutRunMount removes the `/run` inside the spec
func WithoutRunMount(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
return WithoutMounts("/run")(ctx, client, c, s)
}

View 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)
}

View File

@ -20,20 +20,15 @@ package oci
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)
// WithHostDevices adds all the hosts device nodes to the container's spec
func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setLinux(s)
devs, err := getDevices("/dev")
devs, err := HostDevices()
if err != nil {
return err
}
@ -41,82 +36,18 @@ func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Sp
return nil
}
func getDevices(path string) ([]specs.LinuxDevice, error) {
files, err := ioutil.ReadDir(path)
// WithDevices recursively adds devices from the passed in path and associated cgroup rules for that device.
// If devicePath is a dir it traverses the dir to add all devices in that dir.
// If devicePath is not a dir, it attempts to add the single device.
func WithDevices(devicePath, containerPath, permissions string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
devs, err := getDevices(devicePath, containerPath)
if err != nil {
return nil, err
return err
}
var out []specs.LinuxDevice
for _, f := range files {
switch {
case f.IsDir():
switch f.Name() {
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
// ".udev" added to address https://github.com/opencontainers/runc/issues/2093
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
continue
default:
sub, err := getDevices(filepath.Join(path, f.Name()))
if err != nil {
return nil, err
s.Linux.Devices = append(s.Linux.Devices, devs...)
return nil
}
out = append(out, sub...)
continue
}
case f.Name() == "console":
continue
}
device, err := deviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil {
if err == ErrNotADevice {
continue
}
if os.IsNotExist(err) {
continue
}
return nil, err
}
out = append(out, *device)
}
return out, nil
}
func deviceFromPath(path, permissions string) (*specs.LinuxDevice, error) {
var stat unix.Stat_t
if err := unix.Lstat(path, &stat); err != nil {
return nil, err
}
var (
devNumber = uint64(stat.Rdev)
major = unix.Major(devNumber)
minor = unix.Minor(devNumber)
)
if major == 0 {
return nil, ErrNotADevice
}
var (
devType string
mode = stat.Mode
)
switch {
case mode&unix.S_IFBLK == unix.S_IFBLK:
devType = "b"
case mode&unix.S_IFCHR == unix.S_IFCHR:
devType = "c"
}
fm := os.FileMode(mode)
return &specs.LinuxDevice{
Type: devType,
Path: path,
Major: int64(major),
Minor: int64(minor),
FileMode: &fm,
UID: &stat.Uid,
GID: &stat.Gid,
}, nil
}
// WithCPUCFS sets the container's Completely fair scheduling (CFS) quota and period

View File

@ -74,6 +74,6 @@ func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Sp
return nil
}
func deviceFromPath(path, permissions string) (*specs.LinuxDevice, error) {
func deviceFromPath(path string) (*specs.LinuxDevice, error) {
return nil, errors.New("device from path not supported on Windows")
}

View File

@ -0,0 +1,137 @@
// +build !windows
/*
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 (
"io/ioutil"
"os"
"path/filepath"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
var errNotADevice = errors.New("not a device node")
// HostDevices returns all devices that can be found under /dev directory.
func HostDevices() ([]specs.LinuxDevice, error) {
return getDevices("/dev", "")
}
func getDevices(path, containerPath string) ([]specs.LinuxDevice, error) {
stat, err := os.Stat(path)
if err != nil {
return nil, errors.Wrap(err, "error stating device path")
}
if !stat.IsDir() {
dev, err := deviceFromPath(path)
if err != nil {
return nil, err
}
if containerPath != "" {
dev.Path = containerPath
}
return []specs.LinuxDevice{*dev}, nil
}
files, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
var out []specs.LinuxDevice
for _, f := range files {
switch {
case f.IsDir():
switch f.Name() {
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
// ".udev" added to address https://github.com/opencontainers/runc/issues/2093
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
continue
default:
var cp string
if containerPath != "" {
cp = filepath.Join(containerPath, filepath.Base(f.Name()))
}
sub, err := getDevices(filepath.Join(path, f.Name()), cp)
if err != nil {
return nil, err
}
out = append(out, sub...)
continue
}
case f.Name() == "console":
continue
}
device, err := deviceFromPath(filepath.Join(path, f.Name()))
if err != nil {
if err == errNotADevice {
continue
}
if os.IsNotExist(err) {
continue
}
return nil, err
}
if containerPath != "" {
device.Path = filepath.Join(containerPath, filepath.Base(f.Name()))
}
out = append(out, *device)
}
return out, nil
}
func deviceFromPath(path string) (*specs.LinuxDevice, error) {
var stat unix.Stat_t
if err := unix.Lstat(path, &stat); err != nil {
return nil, err
}
var (
devNumber = uint64(stat.Rdev) //nolint: unconvert // the type is 32bit on mips.
major = unix.Major(devNumber)
minor = unix.Minor(devNumber)
)
if major == 0 {
return nil, errNotADevice
}
var (
devType string
mode = stat.Mode
)
switch {
case mode&unix.S_IFBLK == unix.S_IFBLK:
devType = "b"
case mode&unix.S_IFCHR == unix.S_IFCHR:
devType = "c"
}
fm := os.FileMode(mode &^ unix.S_IFMT)
return &specs.LinuxDevice{
Type: devType,
Path: path,
Major: int64(major),
Minor: int64(minor),
FileMode: &fm,
UID: &stat.Uid,
GID: &stat.Gid,
}, nil
}

View File

@ -0,0 +1,192 @@
/*
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"
)
// FromNumber returns a cap string like "CAP_SYS_ADMIN"
// that corresponds to the given number like 21.
//
// FromNumber returns an empty string for unknown cap number.
func FromNumber(num int) string {
if num < 0 || num > len(capsLatest)-1 {
return ""
}
return capsLatest[num]
}
// FromBitmap parses an uint64 bitmap into string slice like
// []{"CAP_SYS_ADMIN", ...}.
//
// Unknown cap numbers are returned as []int.
func FromBitmap(v uint64) ([]string, []int) {
var (
res []string
unknown []int
)
for i := 0; i <= 63; i++ {
if b := (v >> i) & 0x1; b == 0x1 {
if s := FromNumber(i); s != "" {
res = append(res, s)
} else {
unknown = append(unknown, i)
}
}
}
return res, unknown
}
// Type is the type of capability
type Type int
const (
// Effective is CapEff
Effective Type = 1 << iota
// Permitted is CapPrm
Permitted
// Inheritable is CapInh
Inheritable
// Bounding is CapBnd
Bounding
// Ambient is CapAmb
Ambient
)
// ParseProcPIDStatus returns uint64 bitmap value from /proc/<PID>/status file
func ParseProcPIDStatus(r io.Reader) (map[Type]uint64, error) {
res := make(map[Type]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[Inheritable] = ui64
case "CapPrm":
res[Permitted] = ui64
case "CapEff":
res[Effective] = ui64
case "CapBnd":
res[Bounding] = ui64
case "CapAmb":
res[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[Effective]
names, _ := FromBitmap(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")
capsLatest = caps59
)
// Known returns the known cap strings of the latest kernel.
// The current latest kernel is 5.9.
func Known() []string {
return capsLatest
}

192
vendor/github.com/containerd/cri/pkg/cap/cap_linux.go generated vendored Normal file
View File

@ -0,0 +1,192 @@
/*
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"
)
// FromNumber returns a cap string like "CAP_SYS_ADMIN"
// that corresponds to the given number like 21.
//
// FromNumber returns an empty string for unknown cap number.
func FromNumber(num int) string {
if num < 0 || num > len(capsLatest)-1 {
return ""
}
return capsLatest[num]
}
// FromBitmap parses an uint64 bitmap into string slice like
// []{"CAP_SYS_ADMIN", ...}.
//
// Unknown cap numbers are returned as []int.
func FromBitmap(v uint64) ([]string, []int) {
var (
res []string
unknown []int
)
for i := 0; i <= 63; i++ {
if b := (v >> i) & 0x1; b == 0x1 {
if s := FromNumber(i); s != "" {
res = append(res, s)
} else {
unknown = append(unknown, i)
}
}
}
return res, unknown
}
// Type is the type of capability
type Type int
const (
// Effective is CapEff
Effective Type = 1 << iota
// Permitted is CapPrm
Permitted
// Inheritable is CapInh
Inheritable
// Bounding is CapBnd
Bounding
// Ambient is CapAmb
Ambient
)
// ParseProcPIDStatus returns uint64 bitmap value from /proc/<PID>/status file
func ParseProcPIDStatus(r io.Reader) (map[Type]uint64, error) {
res := make(map[Type]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[Inheritable] = ui64
case "CapPrm":
res[Permitted] = ui64
case "CapEff":
res[Effective] = ui64
case "CapBnd":
res[Bounding] = ui64
case "CapAmb":
res[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[Effective]
names, _ := FromBitmap(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")
capsLatest = caps59
)
// Known returns the known cap strings of the latest kernel.
// The current latest kernel is 5.9.
func Known() []string {
return capsLatest
}

View File

@ -332,7 +332,7 @@ func WithDevices(osi osinterface.OS, config *runtime.ContainerConfig) oci.SpecOp
}
// WithCapabilities sets the provided capabilties from the security context
func WithCapabilities(sc *runtime.LinuxContainerSecurityContext) oci.SpecOpts {
func WithCapabilities(sc *runtime.LinuxContainerSecurityContext, allCaps []string) oci.SpecOpts {
capabilities := sc.GetCapabilities()
if capabilities == nil {
return nullOpt
@ -344,7 +344,7 @@ func WithCapabilities(sc *runtime.LinuxContainerSecurityContext) oci.SpecOpts {
// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
// will be all capabilities without `CAP_CHOWN`.
if util.InStringSlice(capabilities.GetAddCapabilities(), "ALL") {
opts = append(opts, oci.WithAllCapabilities)
opts = append(opts, oci.WithCapabilities(allCaps))
}
if util.InStringSlice(capabilities.GetDropCapabilities(), "ALL") {
opts = append(opts, oci.WithCapabilities(nil))

View File

@ -203,10 +203,10 @@ func (c *criService) containerSpec(id string, sandboxID string, sandboxPid uint3
specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed)
} else {
// 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
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

View File

@ -101,6 +101,9 @@ type criService struct {
cniNetConfMonitor *cniNetConfSyncer
// baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec`
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

View File

@ -20,6 +20,7 @@ package server
import (
"github.com/containerd/containerd/sys"
"github.com/containerd/cri/pkg/cap"
cni "github.com/containerd/go-cni"
"github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
@ -63,6 +64,13 @@ func (c *criService) initPlatform() error {
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
}

10
vendor/modules.txt vendored
View File

@ -173,7 +173,7 @@ github.com/containerd/cgroups/v2
github.com/containerd/cgroups/v2/stats
# github.com/containerd/console v1.0.2 => github.com/containerd/console v1.0.0
github.com/containerd/console
# github.com/containerd/containerd v1.5.0-beta.4 => github.com/k3s-io/containerd v1.4.4-k3s1
# github.com/containerd/containerd v1.5.0-beta.4 => github.com/k3s-io/containerd v1.4.4-k3s2
## explicit
github.com/containerd/containerd
github.com/containerd/containerd/api/events
@ -249,6 +249,7 @@ github.com/containerd/containerd/metrics/types/v2
github.com/containerd/containerd/mount
github.com/containerd/containerd/namespaces
github.com/containerd/containerd/oci
github.com/containerd/containerd/pkg/cap
github.com/containerd/containerd/pkg/dialer
github.com/containerd/containerd/pkg/oom
github.com/containerd/containerd/pkg/oom/v1
@ -317,12 +318,13 @@ github.com/containerd/continuity/fs
github.com/containerd/continuity/pathdriver
github.com/containerd/continuity/proto
github.com/containerd/continuity/sysx
# github.com/containerd/cri v1.11.1-0.20200820101445-b0cc07999aa5 => github.com/k3s-io/cri v1.4.0-k3s.5
# github.com/containerd/cri v1.11.1-0.20200820101445-b0cc07999aa5 => github.com/k3s-io/cri v1.4.0-k3s.6
## explicit
github.com/containerd/cri
github.com/containerd/cri/pkg/annotations
github.com/containerd/cri/pkg/api/runtimeoptions/v1
github.com/containerd/cri/pkg/atomic
github.com/containerd/cri/pkg/cap
github.com/containerd/cri/pkg/config
github.com/containerd/cri/pkg/constants
github.com/containerd/cri/pkg/containerd/opts
@ -3179,9 +3181,9 @@ sigs.k8s.io/yaml
# github.com/containerd/btrfs => github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e
# github.com/containerd/cgroups => github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340
# github.com/containerd/console => github.com/containerd/console v1.0.0
# github.com/containerd/containerd => github.com/k3s-io/containerd v1.4.4-k3s1
# github.com/containerd/containerd => github.com/k3s-io/containerd v1.4.4-k3s2
# github.com/containerd/continuity => github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1
# github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.5
# github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.6
# github.com/containerd/fifo => github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b
# github.com/containerd/go-runc => github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328
# github.com/containerd/ttrpc => github.com/containerd/ttrpc v1.0.1