github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/specgen/generate/oci.go (about)

     1  package generate
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/containers/libpod/libpod"
     7  	"github.com/containers/libpod/libpod/image"
     8  	"github.com/containers/libpod/pkg/rootless"
     9  	createconfig "github.com/containers/libpod/pkg/spec"
    10  	"github.com/containers/libpod/pkg/specgen"
    11  	spec "github.com/opencontainers/runtime-spec/specs-go"
    12  	"github.com/opencontainers/runtime-tools/generate"
    13  )
    14  
    15  func SpecGenToOCI(s *specgen.SpecGenerator, rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) {
    16  	var (
    17  		inUserNS bool
    18  	)
    19  	cgroupPerm := "ro"
    20  	g, err := generate.New("linux")
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  	// Remove the default /dev/shm mount to ensure we overwrite it
    25  	g.RemoveMount("/dev/shm")
    26  	g.HostSpecific = true
    27  	addCgroup := true
    28  	canMountSys := true
    29  
    30  	isRootless := rootless.IsRootless()
    31  	if isRootless {
    32  		inUserNS = true
    33  	}
    34  	if !s.UserNS.IsHost() {
    35  		if s.UserNS.IsContainer() || s.UserNS.IsPath() {
    36  			inUserNS = true
    37  		}
    38  		if s.UserNS.IsPrivate() {
    39  			inUserNS = true
    40  		}
    41  	}
    42  	if inUserNS && s.NetNS.IsHost() {
    43  		canMountSys = false
    44  	}
    45  
    46  	if s.Privileged && canMountSys {
    47  		cgroupPerm = "rw"
    48  		g.RemoveMount("/sys")
    49  		sysMnt := spec.Mount{
    50  			Destination: "/sys",
    51  			Type:        "sysfs",
    52  			Source:      "sysfs",
    53  			Options:     []string{"rprivate", "nosuid", "noexec", "nodev", "rw"},
    54  		}
    55  		g.AddMount(sysMnt)
    56  	} else if !canMountSys {
    57  		addCgroup = false
    58  		g.RemoveMount("/sys")
    59  		r := "ro"
    60  		if s.Privileged {
    61  			r = "rw"
    62  		}
    63  		sysMnt := spec.Mount{
    64  			Destination: "/sys",
    65  			Type:        "bind", // should we use a constant for this, like createconfig?
    66  			Source:      "/sys",
    67  			Options:     []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
    68  		}
    69  		g.AddMount(sysMnt)
    70  		if !s.Privileged && isRootless {
    71  			g.AddLinuxMaskedPaths("/sys/kernel")
    72  		}
    73  	}
    74  	gid5Available := true
    75  	if isRootless {
    76  		nGids, err := createconfig.GetAvailableGids()
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		gid5Available = nGids >= 5
    81  	}
    82  	// When using a different user namespace, check that the GID 5 is mapped inside
    83  	// the container.
    84  	if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) {
    85  		mappingFound := false
    86  		for _, r := range s.IDMappings.GIDMap {
    87  			if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
    88  				mappingFound = true
    89  				break
    90  			}
    91  		}
    92  		if !mappingFound {
    93  			gid5Available = false
    94  		}
    95  
    96  	}
    97  	if !gid5Available {
    98  		// If we have no GID mappings, the gid=5 default option would fail, so drop it.
    99  		g.RemoveMount("/dev/pts")
   100  		devPts := spec.Mount{
   101  			Destination: "/dev/pts",
   102  			Type:        "devpts",
   103  			Source:      "devpts",
   104  			Options:     []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
   105  		}
   106  		g.AddMount(devPts)
   107  	}
   108  
   109  	if inUserNS && s.IpcNS.IsHost() {
   110  		g.RemoveMount("/dev/mqueue")
   111  		devMqueue := spec.Mount{
   112  			Destination: "/dev/mqueue",
   113  			Type:        "bind", // constant ?
   114  			Source:      "/dev/mqueue",
   115  			Options:     []string{"bind", "nosuid", "noexec", "nodev"},
   116  		}
   117  		g.AddMount(devMqueue)
   118  	}
   119  	if inUserNS && s.PidNS.IsHost() {
   120  		g.RemoveMount("/proc")
   121  		procMount := spec.Mount{
   122  			Destination: "/proc",
   123  			Type:        createconfig.TypeBind,
   124  			Source:      "/proc",
   125  			Options:     []string{"rbind", "nosuid", "noexec", "nodev"},
   126  		}
   127  		g.AddMount(procMount)
   128  	}
   129  
   130  	if addCgroup {
   131  		cgroupMnt := spec.Mount{
   132  			Destination: "/sys/fs/cgroup",
   133  			Type:        "cgroup",
   134  			Source:      "cgroup",
   135  			Options:     []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm},
   136  		}
   137  		g.AddMount(cgroupMnt)
   138  	}
   139  	g.SetProcessCwd(s.WorkDir)
   140  	g.SetProcessArgs(s.Command)
   141  	g.SetProcessTerminal(s.Terminal)
   142  
   143  	for key, val := range s.Annotations {
   144  		g.AddAnnotation(key, val)
   145  	}
   146  	g.AddProcessEnv("container", "podman")
   147  
   148  	g.Config.Linux.Resources = s.ResourceLimits
   149  
   150  	// Devices
   151  	if s.Privileged {
   152  		// If privileged, we need to add all the host devices to the
   153  		// spec.  We do not add the user provided ones because we are
   154  		// already adding them all.
   155  		if err := createconfig.AddPrivilegedDevices(&g); err != nil {
   156  			return nil, err
   157  		}
   158  	} else {
   159  		for _, device := range s.Devices {
   160  			if err := createconfig.DevicesFromPath(&g, device.Path); err != nil {
   161  				return nil, err
   162  			}
   163  		}
   164  	}
   165  
   166  	// SECURITY OPTS
   167  	g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
   168  
   169  	if !s.Privileged {
   170  		g.SetProcessApparmorProfile(s.ApparmorProfile)
   171  	}
   172  
   173  	createconfig.BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g)
   174  
   175  	for name, val := range s.Env {
   176  		g.AddProcessEnv(name, val)
   177  	}
   178  
   179  	// TODO rlimits and ulimits needs further refinement by someone more
   180  	// familiar with the code.
   181  	//if err := addRlimits(config, &g); err != nil {
   182  	//	return nil, err
   183  	//}
   184  
   185  	// NAMESPACES
   186  
   187  	if err := pidConfigureGenerator(s, &g); err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	if err := userConfigureGenerator(s, &g); err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	if err := networkConfigureGenerator(s, &g); err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	if err := utsConfigureGenerator(s, &g, rt); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	if err := ipcConfigureGenerator(s, &g); err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	if err := cgroupConfigureGenerator(s, &g); err != nil {
   208  		return nil, err
   209  	}
   210  	configSpec := g.Config
   211  
   212  	if err := securityConfigureGenerator(s, &g, newImage); err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	// BIND MOUNTS
   217  	configSpec.Mounts = createconfig.SupercedeUserMounts(s.Mounts, configSpec.Mounts)
   218  	// Process mounts to ensure correct options
   219  	if err := createconfig.InitFSMounts(configSpec.Mounts); err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	// Add annotations
   224  	if configSpec.Annotations == nil {
   225  		configSpec.Annotations = make(map[string]string)
   226  	}
   227  
   228  	// TODO cidfile is not in specgen; when wiring up cli, we will need to move this out of here
   229  	// leaving as a reminder
   230  	//if config.CidFile != "" {
   231  	//	configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
   232  	//}
   233  
   234  	if s.Remove {
   235  		configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
   236  	} else {
   237  		configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
   238  	}
   239  
   240  	if len(s.VolumesFrom) > 0 {
   241  		configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
   242  	}
   243  
   244  	if s.Privileged {
   245  		configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
   246  	} else {
   247  		configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
   248  	}
   249  
   250  	// TODO Init might not make it into the specgen and therefore is not available here.  We should deal
   251  	// with this when we wire up the CLI; leaving as a reminder
   252  	//if s.Init {
   253  	//	configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
   254  	//} else {
   255  	//	configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
   256  	//}
   257  
   258  	return configSpec, nil
   259  }