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  }