github.com/containers/podman/v4@v4.9.4/pkg/specgen/generate/config_linux.go (about) 1 //go:build !remote 2 // +build !remote 3 4 package generate 5 6 import ( 7 "fmt" 8 "io/fs" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 14 "github.com/containers/podman/v4/libpod/define" 15 "github.com/containers/podman/v4/pkg/rootless" 16 "github.com/containers/podman/v4/pkg/util" 17 18 spec "github.com/opencontainers/runtime-spec/specs-go" 19 "github.com/opencontainers/runtime-tools/generate" 20 "github.com/sirupsen/logrus" 21 "golang.org/x/sys/unix" 22 "tags.cncf.io/container-device-interface/pkg/cdi" 23 ) 24 25 // DevicesFromPath computes a list of devices 26 func DevicesFromPath(g *generate.Generator, devicePath string) error { 27 if isCDIDevice(devicePath) { 28 registry := cdi.GetRegistry( 29 cdi.WithAutoRefresh(false), 30 ) 31 if err := registry.Refresh(); err != nil { 32 logrus.Debugf("The following error was triggered when refreshing the CDI registry: %v", err) 33 } 34 _, err := registry.InjectDevices(g.Config, devicePath) 35 if err != nil { 36 return fmt.Errorf("setting up CDI devices: %w", err) 37 } 38 return nil 39 } 40 devs := strings.Split(devicePath, ":") 41 resolvedDevicePath := devs[0] 42 // check if it is a symbolic link 43 if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink { 44 if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil { 45 resolvedDevicePath = linkedPathOnHost 46 } 47 } 48 st, err := os.Stat(resolvedDevicePath) 49 if err != nil { 50 return err 51 } 52 if st.IsDir() { 53 found := false 54 src := resolvedDevicePath 55 dest := src 56 var devmode string 57 if len(devs) > 1 { 58 if len(devs[1]) > 0 && devs[1][0] == '/' { 59 dest = devs[1] 60 } else { 61 devmode = devs[1] 62 } 63 } 64 if len(devs) > 2 { 65 if devmode != "" { 66 return fmt.Errorf("invalid device specification %s: %w", devicePath, unix.EINVAL) 67 } 68 devmode = devs[2] 69 } 70 71 // mount the internal devices recursively 72 if err := filepath.WalkDir(resolvedDevicePath, func(dpath string, d fs.DirEntry, e error) error { 73 if d.Type()&os.ModeDevice == os.ModeDevice { 74 found = true 75 device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src))) 76 if devmode != "" { 77 device = fmt.Sprintf("%s:%s", device, devmode) 78 } 79 if err := addDevice(g, device); err != nil { 80 return fmt.Errorf("failed to add %s device: %w", dpath, err) 81 } 82 } 83 return nil 84 }); err != nil { 85 return err 86 } 87 if !found { 88 return fmt.Errorf("no devices found in %s: %w", devicePath, unix.EINVAL) 89 } 90 return nil 91 } 92 return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) 93 } 94 95 func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) { 96 defaultMaskPaths := []string{"/proc/acpi", 97 "/proc/kcore", 98 "/proc/keys", 99 "/proc/latency_stats", 100 "/proc/timer_list", 101 "/proc/timer_stats", 102 "/proc/sched_debug", 103 "/proc/scsi", 104 "/sys/firmware", 105 "/sys/fs/selinux", 106 "/sys/dev/block", 107 } 108 109 if !privileged { 110 for _, mp := range defaultMaskPaths { 111 // check that the path to mask is not in the list of paths to unmask 112 if shouldMask(mp, unmask) { 113 g.AddLinuxMaskedPaths(mp) 114 } 115 } 116 for _, rp := range []string{ 117 "/proc/asound", 118 "/proc/bus", 119 "/proc/fs", 120 "/proc/irq", 121 "/proc/sys", 122 "/proc/sysrq-trigger", 123 } { 124 if shouldMask(rp, unmask) { 125 g.AddLinuxReadonlyPaths(rp) 126 } 127 } 128 129 if pidModeIsHost && rootless.IsRootless() { 130 return 131 } 132 } 133 134 // mask the paths provided by the user 135 for _, mp := range mask { 136 if !path.IsAbs(mp) && mp != "" { 137 logrus.Errorf("Path %q is not an absolute path, skipping...", mp) 138 continue 139 } 140 g.AddLinuxMaskedPaths(mp) 141 } 142 } 143 144 func addDevice(g *generate.Generator, device string) error { 145 src, dst, permissions, err := ParseDevice(device) 146 if err != nil { 147 return err 148 } 149 dev, err := util.DeviceFromPath(src) 150 if err != nil { 151 return fmt.Errorf("%s is not a valid device: %w", src, err) 152 } 153 if rootless.IsRootless() { 154 if _, err := os.Stat(src); err != nil { 155 return err 156 } 157 perm := "ro" 158 if strings.Contains(permissions, "w") { 159 perm = "rw" 160 } 161 devMnt := spec.Mount{ 162 Destination: dst, 163 Type: define.TypeBind, 164 Source: src, 165 Options: []string{"slave", "nosuid", "noexec", perm, "rbind"}, 166 } 167 g.Config.Mounts = append(g.Config.Mounts, devMnt) 168 return nil 169 } else if src == "/dev/fuse" { 170 // if the user is asking for fuse inside the container 171 // make sure the module is loaded. 172 f, err := unix.Open(src, unix.O_RDONLY|unix.O_NONBLOCK, 0) 173 if err == nil { 174 unix.Close(f) 175 } 176 } 177 dev.Path = dst 178 g.AddDevice(*dev) 179 g.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, permissions) 180 return nil 181 } 182 183 func supportAmbientCapabilities() bool { 184 err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0) 185 return err == nil 186 } 187 188 func shouldMask(mask string, unmask []string) bool { 189 for _, m := range unmask { 190 if strings.ToLower(m) == "all" { 191 return false 192 } 193 for _, m1 := range strings.Split(m, ":") { 194 match, err := filepath.Match(m1, mask) 195 if err != nil { 196 logrus.Errorf(err.Error()) 197 } 198 if match { 199 return false 200 } 201 } 202 } 203 return true 204 }