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  }