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