mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
108ee85b82
The kubelet uses libct/cgroups code to set up cgroups. It creates a parent cgroup (kubepods) to put the containers into. The problem (for cgroupv2 that uses eBPF for device configuration) is the hard requirement to have devices cgroup configured results in leaking an eBPF program upon every kubelet restart. program. If kubelet is restarted 64+ times, the cgroup can't be configured anymore. Work around this by adding a SkipDevices flag to Resources. A check was added so that if SkipDevices is set, such a "container" can't be started (to make sure it is only used for non-containers). Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
// +build linux
|
|
|
|
package fs2
|
|
|
|
import (
|
|
"github.com/opencontainers/runc/libcontainer/cgroups/ebpf"
|
|
"github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func isRWM(perms configs.DevicePermissions) bool {
|
|
var r, w, m bool
|
|
for _, perm := range perms {
|
|
switch perm {
|
|
case 'r':
|
|
r = true
|
|
case 'w':
|
|
w = true
|
|
case 'm':
|
|
m = true
|
|
}
|
|
}
|
|
return r && w && m
|
|
}
|
|
|
|
// the logic is from crun
|
|
// https://github.com/containers/crun/blob/0.10.2/src/libcrun/cgroup.c#L1644-L1652
|
|
func canSkipEBPFError(cgroup *configs.Cgroup) bool {
|
|
for _, dev := range cgroup.Resources.Devices {
|
|
if dev.Allow || !isRWM(dev.Permissions) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func setDevices(dirPath string, cgroup *configs.Cgroup) error {
|
|
if cgroup.SkipDevices {
|
|
return nil
|
|
}
|
|
// XXX: This is currently a white-list (but all callers pass a blacklist of
|
|
// devices). This is bad for a whole variety of reasons, but will need
|
|
// to be fixed with co-ordinated effort with downstreams.
|
|
devices := cgroup.Devices
|
|
insts, license, err := devicefilter.DeviceFilter(devices)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dirFD, err := unix.Open(dirPath, unix.O_DIRECTORY|unix.O_RDONLY, 0600)
|
|
if err != nil {
|
|
return errors.Errorf("cannot get dir FD for %s", dirPath)
|
|
}
|
|
defer unix.Close(dirFD)
|
|
// XXX: This code is currently incorrect when it comes to updating an
|
|
// existing cgroup with new rules (new rulesets are just appended to
|
|
// the program list because this uses BPF_F_ALLOW_MULTI). If we didn't
|
|
// use BPF_F_ALLOW_MULTI we could actually atomically swap the
|
|
// programs.
|
|
//
|
|
// The real issue is that BPF_F_ALLOW_MULTI makes it hard to have a
|
|
// race-free blacklist because it acts as a whitelist by default, and
|
|
// having a deny-everything program cannot be overriden by other
|
|
// programs. You could temporarily insert a deny-everything program
|
|
// but that would result in spurrious failures during updates.
|
|
if _, err := ebpf.LoadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil {
|
|
if !canSkipEBPFError(cgroup) {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|