github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/config_linux.go (about) 1 package generate 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/containers/podman/v2/pkg/rootless" 11 spec "github.com/opencontainers/runtime-spec/specs-go" 12 "github.com/opencontainers/runtime-tools/generate" 13 "github.com/pkg/errors" 14 "golang.org/x/sys/unix" 15 ) 16 17 var ( 18 errNotADevice = errors.New("not a device node") 19 ) 20 21 func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } 22 func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } 23 24 func addPrivilegedDevices(g *generate.Generator) error { 25 hostDevices, err := getDevices("/dev") 26 if err != nil { 27 return err 28 } 29 g.ClearLinuxDevices() 30 31 if rootless.IsRootless() { 32 mounts := make(map[string]interface{}) 33 for _, m := range g.Mounts() { 34 mounts[m.Destination] = true 35 } 36 newMounts := []spec.Mount{} 37 for _, d := range hostDevices { 38 devMnt := spec.Mount{ 39 Destination: d.Path, 40 Type: TypeBind, 41 Source: d.Path, 42 Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, 43 } 44 if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { 45 continue 46 } 47 if _, found := mounts[d.Path]; found { 48 continue 49 } 50 st, err := os.Stat(d.Path) 51 if err != nil { 52 if err == unix.EPERM { 53 continue 54 } 55 return err 56 } 57 // Skip devices that the user has not access to. 58 if st.Mode()&0007 == 0 { 59 continue 60 } 61 newMounts = append(newMounts, devMnt) 62 } 63 g.Config.Mounts = append(newMounts, g.Config.Mounts...) 64 if g.Config.Linux.Resources != nil { 65 g.Config.Linux.Resources.Devices = nil 66 } 67 } else { 68 for _, d := range hostDevices { 69 g.AddDevice(d) 70 } 71 // Add resources device - need to clear the existing one first. 72 if g.Config.Linux.Resources != nil { 73 g.Config.Linux.Resources.Devices = nil 74 } 75 g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") 76 } 77 78 return nil 79 } 80 81 // DevicesFromPath computes a list of devices 82 func DevicesFromPath(g *generate.Generator, devicePath string) error { 83 devs := strings.Split(devicePath, ":") 84 resolvedDevicePath := devs[0] 85 // check if it is a symbolic link 86 if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink { 87 if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil { 88 resolvedDevicePath = linkedPathOnHost 89 } 90 } 91 st, err := os.Stat(resolvedDevicePath) 92 if err != nil { 93 return err 94 } 95 if st.IsDir() { 96 found := false 97 src := resolvedDevicePath 98 dest := src 99 var devmode string 100 if len(devs) > 1 { 101 if len(devs[1]) > 0 && devs[1][0] == '/' { 102 dest = devs[1] 103 } else { 104 devmode = devs[1] 105 } 106 } 107 if len(devs) > 2 { 108 if devmode != "" { 109 return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath) 110 } 111 devmode = devs[2] 112 } 113 114 // mount the internal devices recursively 115 if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error { 116 117 if f.Mode()&os.ModeDevice == os.ModeDevice { 118 found = true 119 device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src))) 120 if devmode != "" { 121 device = fmt.Sprintf("%s:%s", device, devmode) 122 } 123 if err := addDevice(g, device); err != nil { 124 return errors.Wrapf(err, "failed to add %s device", dpath) 125 } 126 } 127 return nil 128 }); err != nil { 129 return err 130 } 131 if !found { 132 return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath) 133 } 134 return nil 135 } 136 137 return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) 138 } 139 140 func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) { 141 if !privileged { 142 for _, mp := range []string{ 143 "/proc/acpi", 144 "/proc/kcore", 145 "/proc/keys", 146 "/proc/latency_stats", 147 "/proc/timer_list", 148 "/proc/timer_stats", 149 "/proc/sched_debug", 150 "/proc/scsi", 151 "/sys/firmware", 152 "/sys/fs/selinux", 153 "/sys/dev", 154 } { 155 g.AddLinuxMaskedPaths(mp) 156 } 157 158 if pidModeIsHost && rootless.IsRootless() { 159 return 160 } 161 162 for _, rp := range []string{ 163 "/proc/asound", 164 "/proc/bus", 165 "/proc/fs", 166 "/proc/irq", 167 "/proc/sys", 168 "/proc/sysrq-trigger", 169 } { 170 g.AddLinuxReadonlyPaths(rp) 171 } 172 } 173 } 174 175 // based on getDevices from runc (libcontainer/devices/devices.go) 176 func getDevices(path string) ([]spec.LinuxDevice, error) { 177 files, err := ioutil.ReadDir(path) 178 if err != nil { 179 if rootless.IsRootless() && os.IsPermission(err) { 180 return nil, nil 181 } 182 return nil, err 183 } 184 out := []spec.LinuxDevice{} 185 for _, f := range files { 186 switch { 187 case f.IsDir(): 188 switch f.Name() { 189 // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 190 case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": 191 continue 192 default: 193 sub, err := getDevices(filepath.Join(path, f.Name())) 194 if err != nil { 195 return nil, err 196 } 197 if sub != nil { 198 out = append(out, sub...) 199 } 200 continue 201 } 202 case f.Name() == "console": 203 continue 204 case f.Mode()&os.ModeSymlink != 0: 205 continue 206 } 207 208 device, err := deviceFromPath(filepath.Join(path, f.Name())) 209 if err != nil { 210 if err == errNotADevice { 211 continue 212 } 213 if os.IsNotExist(err) { 214 continue 215 } 216 return nil, err 217 } 218 out = append(out, *device) 219 } 220 return out, nil 221 } 222 223 func addDevice(g *generate.Generator, device string) error { 224 src, dst, permissions, err := ParseDevice(device) 225 if err != nil { 226 return err 227 } 228 dev, err := deviceFromPath(src) 229 if err != nil { 230 return errors.Wrapf(err, "%s is not a valid device", src) 231 } 232 if rootless.IsRootless() { 233 if _, err := os.Stat(src); err != nil { 234 return err 235 } 236 perm := "ro" 237 if strings.Contains(permissions, "w") { 238 perm = "rw" 239 } 240 devMnt := spec.Mount{ 241 Destination: dst, 242 Type: TypeBind, 243 Source: src, 244 Options: []string{"slave", "nosuid", "noexec", perm, "rbind"}, 245 } 246 g.Config.Mounts = append(g.Config.Mounts, devMnt) 247 return nil 248 } else if src == "/dev/fuse" { 249 // if the user is asking for fuse inside the container 250 // make sure the module is loaded. 251 f, err := unix.Open(src, unix.O_RDONLY|unix.O_NONBLOCK, 0) 252 if err == nil { 253 unix.Close(f) 254 } 255 } 256 dev.Path = dst 257 g.AddDevice(*dev) 258 g.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, permissions) 259 return nil 260 } 261 262 // ParseDevice parses device mapping string to a src, dest & permissions string 263 func ParseDevice(device string) (string, string, string, error) { //nolint 264 src := "" 265 dst := "" 266 permissions := "rwm" 267 arr := strings.Split(device, ":") 268 switch len(arr) { 269 case 3: 270 if !IsValidDeviceMode(arr[2]) { 271 return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2]) 272 } 273 permissions = arr[2] 274 fallthrough 275 case 2: 276 if IsValidDeviceMode(arr[1]) { 277 permissions = arr[1] 278 } else { 279 if arr[1][0] != '/' { 280 return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1]) 281 } 282 dst = arr[1] 283 } 284 fallthrough 285 case 1: 286 src = arr[0] 287 default: 288 return "", "", "", fmt.Errorf("invalid device specification: %s", device) 289 } 290 291 if dst == "" { 292 dst = src 293 } 294 return src, dst, permissions, nil 295 } 296 297 // IsValidDeviceMode checks if the mode for device is valid or not. 298 // IsValid mode is a composition of r (read), w (write), and m (mknod). 299 func IsValidDeviceMode(mode string) bool { 300 var legalDeviceMode = map[rune]bool{ 301 'r': true, 302 'w': true, 303 'm': true, 304 } 305 if mode == "" { 306 return false 307 } 308 for _, c := range mode { 309 if !legalDeviceMode[c] { 310 return false 311 } 312 legalDeviceMode[c] = false 313 } 314 return true 315 } 316 317 // Copied from github.com/opencontainers/runc/libcontainer/devices 318 // Given the path to a device look up the information about a linux device 319 func deviceFromPath(path string) (*spec.LinuxDevice, error) { 320 var stat unix.Stat_t 321 err := unix.Lstat(path, &stat) 322 if err != nil { 323 return nil, err 324 } 325 var ( 326 devType string 327 mode = stat.Mode 328 devNumber = uint64(stat.Rdev) 329 m = os.FileMode(mode) 330 ) 331 332 switch { 333 case mode&unix.S_IFBLK == unix.S_IFBLK: 334 devType = "b" 335 case mode&unix.S_IFCHR == unix.S_IFCHR: 336 devType = "c" 337 case mode&unix.S_IFIFO == unix.S_IFIFO: 338 devType = "p" 339 default: 340 return nil, errNotADevice 341 } 342 343 return &spec.LinuxDevice{ 344 Type: devType, 345 Path: path, 346 FileMode: &m, 347 UID: &stat.Uid, 348 GID: &stat.Gid, 349 Major: int64(unix.Major(devNumber)), 350 Minor: int64(unix.Minor(devNumber)), 351 }, nil 352 } 353 354 func supportAmbientCapabilities() bool { 355 err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0) 356 return err == nil 357 }