devices.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package fs2
  2. import (
  3. "fmt"
  4. "golang.org/x/sys/unix"
  5. "github.com/opencontainers/runc/libcontainer/cgroups/ebpf"
  6. "github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter"
  7. "github.com/opencontainers/runc/libcontainer/configs"
  8. "github.com/opencontainers/runc/libcontainer/devices"
  9. "github.com/opencontainers/runc/libcontainer/userns"
  10. )
  11. func isRWM(perms devices.Permissions) bool {
  12. var r, w, m bool
  13. for _, perm := range perms {
  14. switch perm {
  15. case 'r':
  16. r = true
  17. case 'w':
  18. w = true
  19. case 'm':
  20. m = true
  21. }
  22. }
  23. return r && w && m
  24. }
  25. // This is similar to the logic applied in crun for handling errors from bpf(2)
  26. // <https://github.com/containers/crun/blob/0.17/src/libcrun/cgroup.c#L2438-L2470>.
  27. func canSkipEBPFError(r *configs.Resources) bool {
  28. // If we're running in a user namespace we can ignore eBPF rules because we
  29. // usually cannot use bpf(2), as well as rootless containers usually don't
  30. // have the necessary privileges to mknod(2) device inodes or access
  31. // host-level instances (though ideally we would be blocking device access
  32. // for rootless containers anyway).
  33. if userns.RunningInUserNS() {
  34. return true
  35. }
  36. // We cannot ignore an eBPF load error if any rule if is a block rule or it
  37. // doesn't permit all access modes.
  38. //
  39. // NOTE: This will sometimes trigger in cases where access modes are split
  40. // between different rules but to handle this correctly would require
  41. // using ".../libcontainer/cgroup/devices".Emulator.
  42. for _, dev := range r.Devices {
  43. if !dev.Allow || !isRWM(dev.Permissions) {
  44. return false
  45. }
  46. }
  47. return true
  48. }
  49. func setDevices(dirPath string, r *configs.Resources) error {
  50. if r.SkipDevices {
  51. return nil
  52. }
  53. insts, license, err := devicefilter.DeviceFilter(r.Devices)
  54. if err != nil {
  55. return err
  56. }
  57. dirFD, err := unix.Open(dirPath, unix.O_DIRECTORY|unix.O_RDONLY, 0o600)
  58. if err != nil {
  59. return fmt.Errorf("cannot get dir FD for %s", dirPath)
  60. }
  61. defer unix.Close(dirFD)
  62. if _, err := ebpf.LoadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil {
  63. if !canSkipEBPFError(r) {
  64. return err
  65. }
  66. }
  67. return nil
  68. }