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

     1  package generate
     2  
     3  import (
     4  	"os"
     5  
     6  	"github.com/containers/common/pkg/capabilities"
     7  	"github.com/containers/libpod/libpod"
     8  	"github.com/containers/libpod/libpod/image"
     9  	"github.com/containers/libpod/pkg/specgen"
    10  	"github.com/cri-o/ocicni/pkg/ocicni"
    11  	spec "github.com/opencontainers/runtime-spec/specs-go"
    12  	"github.com/opencontainers/runtime-tools/generate"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  func GenerateNamespaceContainerOpts(s *specgen.SpecGenerator, rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
    18  	var portBindings []ocicni.PortMapping
    19  	options := make([]libpod.CtrCreateOption, 0)
    20  
    21  	// Cgroups
    22  	switch {
    23  	case s.CgroupNS.IsPrivate():
    24  		ns := s.CgroupNS.Value
    25  		if _, err := os.Stat(ns); err != nil {
    26  			return nil, err
    27  		}
    28  	case s.CgroupNS.IsContainer():
    29  		connectedCtr, err := rt.LookupContainer(s.CgroupNS.Value)
    30  		if err != nil {
    31  			return nil, errors.Wrapf(err, "container %q not found", s.CgroupNS.Value)
    32  		}
    33  		options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
    34  		//	TODO
    35  		//default:
    36  		//	return nil, errors.New("cgroup name only supports private and container")
    37  	}
    38  
    39  	if s.CgroupParent != "" {
    40  		options = append(options, libpod.WithCgroupParent(s.CgroupParent))
    41  	}
    42  
    43  	if s.CgroupsMode != "" {
    44  		options = append(options, libpod.WithCgroupsMode(s.CgroupsMode))
    45  	}
    46  
    47  	// ipc
    48  	switch {
    49  	case s.IpcNS.IsHost():
    50  		options = append(options, libpod.WithShmDir("/dev/shm"))
    51  	case s.IpcNS.IsContainer():
    52  		connectedCtr, err := rt.LookupContainer(s.IpcNS.Value)
    53  		if err != nil {
    54  			return nil, errors.Wrapf(err, "container %q not found", s.IpcNS.Value)
    55  		}
    56  		options = append(options, libpod.WithIPCNSFrom(connectedCtr))
    57  		options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
    58  	}
    59  
    60  	// pid
    61  	if s.PidNS.IsContainer() {
    62  		connectedCtr, err := rt.LookupContainer(s.PidNS.Value)
    63  		if err != nil {
    64  			return nil, errors.Wrapf(err, "container %q not found", s.PidNS.Value)
    65  		}
    66  		options = append(options, libpod.WithPIDNSFrom(connectedCtr))
    67  	}
    68  
    69  	// uts
    70  	switch {
    71  	case s.UtsNS.IsPod():
    72  		connectedPod, err := rt.LookupPod(s.UtsNS.Value)
    73  		if err != nil {
    74  			return nil, errors.Wrapf(err, "pod %q not found", s.UtsNS.Value)
    75  		}
    76  		options = append(options, libpod.WithUTSNSFromPod(connectedPod))
    77  	case s.UtsNS.IsContainer():
    78  		connectedCtr, err := rt.LookupContainer(s.UtsNS.Value)
    79  		if err != nil {
    80  			return nil, errors.Wrapf(err, "container %q not found", s.UtsNS.Value)
    81  		}
    82  
    83  		options = append(options, libpod.WithUTSNSFrom(connectedCtr))
    84  	}
    85  
    86  	if s.UseImageHosts {
    87  		options = append(options, libpod.WithUseImageHosts())
    88  	} else if len(s.HostAdd) > 0 {
    89  		options = append(options, libpod.WithHosts(s.HostAdd))
    90  	}
    91  
    92  	// User
    93  
    94  	switch {
    95  	case s.UserNS.IsPath():
    96  		ns := s.UserNS.Value
    97  		if ns == "" {
    98  			return nil, errors.Errorf("invalid empty user-defined user namespace")
    99  		}
   100  		_, err := os.Stat(ns)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		if s.IDMappings != nil {
   105  			options = append(options, libpod.WithIDMappings(*s.IDMappings))
   106  		}
   107  	case s.UserNS.IsContainer():
   108  		connectedCtr, err := rt.LookupContainer(s.UserNS.Value)
   109  		if err != nil {
   110  			return nil, errors.Wrapf(err, "container %q not found", s.UserNS.Value)
   111  		}
   112  		options = append(options, libpod.WithUserNSFrom(connectedCtr))
   113  	default:
   114  		if s.IDMappings != nil {
   115  			options = append(options, libpod.WithIDMappings(*s.IDMappings))
   116  		}
   117  	}
   118  
   119  	options = append(options, libpod.WithUser(s.User))
   120  	options = append(options, libpod.WithGroups(s.Groups))
   121  
   122  	if len(s.PortMappings) > 0 {
   123  		portBindings = s.PortMappings
   124  	}
   125  
   126  	switch {
   127  	case s.NetNS.IsPath():
   128  		ns := s.NetNS.Value
   129  		if ns == "" {
   130  			return nil, errors.Errorf("invalid empty user-defined network namespace")
   131  		}
   132  		_, err := os.Stat(ns)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  	case s.NetNS.IsContainer():
   137  		connectedCtr, err := rt.LookupContainer(s.NetNS.Value)
   138  		if err != nil {
   139  			return nil, errors.Wrapf(err, "container %q not found", s.NetNS.Value)
   140  		}
   141  		options = append(options, libpod.WithNetNSFrom(connectedCtr))
   142  	case !s.NetNS.IsHost() && s.NetNS.NSMode != specgen.NoNetwork:
   143  		postConfigureNetNS := !s.UserNS.IsHost()
   144  		options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(s.NetNS.NSMode), s.CNINetworks))
   145  	}
   146  
   147  	if len(s.DNSSearch) > 0 {
   148  		options = append(options, libpod.WithDNSSearch(s.DNSSearch))
   149  	}
   150  	if len(s.DNSServer) > 0 {
   151  		// TODO I'm not sure how we are going to handle this given the input
   152  		if len(s.DNSServer) == 1 { //&& strings.ToLower(s.DNSServer[0].) == "none" {
   153  			options = append(options, libpod.WithUseImageResolvConf())
   154  		} else {
   155  			var dnsServers []string
   156  			for _, d := range s.DNSServer {
   157  				dnsServers = append(dnsServers, d.String())
   158  			}
   159  			options = append(options, libpod.WithDNS(dnsServers))
   160  		}
   161  	}
   162  	if len(s.DNSOption) > 0 {
   163  		options = append(options, libpod.WithDNSOption(s.DNSOption))
   164  	}
   165  	if s.StaticIP != nil {
   166  		options = append(options, libpod.WithStaticIP(*s.StaticIP))
   167  	}
   168  
   169  	if s.StaticMAC != nil {
   170  		options = append(options, libpod.WithStaticMAC(*s.StaticMAC))
   171  	}
   172  	return options, nil
   173  }
   174  
   175  func pidConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error {
   176  	if s.PidNS.IsPath() {
   177  		return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value)
   178  	}
   179  	if s.PidNS.IsHost() {
   180  		return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
   181  	}
   182  	if s.PidNS.IsContainer() {
   183  		logrus.Debugf("using container %s pidmode", s.PidNS.Value)
   184  	}
   185  	if s.PidNS.IsPod() {
   186  		logrus.Debug("using pod pidmode")
   187  	}
   188  	return nil
   189  }
   190  
   191  func utsConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, runtime *libpod.Runtime) error {
   192  	hostname := s.Hostname
   193  	var err error
   194  	if hostname == "" {
   195  		switch {
   196  		case s.UtsNS.IsContainer():
   197  			utsCtr, err := runtime.LookupContainer(s.UtsNS.Value)
   198  			if err != nil {
   199  				return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", s.UtsNS.Value)
   200  			}
   201  			hostname = utsCtr.Hostname()
   202  		case s.NetNS.IsHost() || s.UtsNS.IsHost():
   203  			hostname, err = os.Hostname()
   204  			if err != nil {
   205  				return errors.Wrap(err, "unable to retrieve hostname of the host")
   206  			}
   207  		default:
   208  			logrus.Debug("No hostname set; container's hostname will default to runtime default")
   209  		}
   210  	}
   211  	g.RemoveHostname()
   212  	if s.Hostname != "" || !s.UtsNS.IsHost() {
   213  		// Set the hostname in the OCI configuration only
   214  		// if specified by the user or if we are creating
   215  		// a new UTS namespace.
   216  		g.SetHostname(hostname)
   217  	}
   218  	g.AddProcessEnv("HOSTNAME", hostname)
   219  
   220  	if s.UtsNS.IsPath() {
   221  		return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value)
   222  	}
   223  	if s.UtsNS.IsHost() {
   224  		return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
   225  	}
   226  	if s.UtsNS.IsContainer() {
   227  		logrus.Debugf("using container %s utsmode", s.UtsNS.Value)
   228  	}
   229  	return nil
   230  }
   231  
   232  func ipcConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error {
   233  	if s.IpcNS.IsPath() {
   234  		return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value)
   235  	}
   236  	if s.IpcNS.IsHost() {
   237  		return g.RemoveLinuxNamespace(s.IpcNS.Value)
   238  	}
   239  	if s.IpcNS.IsContainer() {
   240  		logrus.Debugf("Using container %s ipcmode", s.IpcNS.Value)
   241  	}
   242  	return nil
   243  }
   244  
   245  func cgroupConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error {
   246  	if s.CgroupNS.IsPath() {
   247  		return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value)
   248  	}
   249  	if s.CgroupNS.IsHost() {
   250  		return g.RemoveLinuxNamespace(s.CgroupNS.Value)
   251  	}
   252  	if s.CgroupNS.IsPrivate() {
   253  		return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
   254  	}
   255  	if s.CgroupNS.IsContainer() {
   256  		logrus.Debugf("Using container %s cgroup mode", s.CgroupNS.Value)
   257  	}
   258  	return nil
   259  }
   260  
   261  func networkConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error {
   262  	switch {
   263  	case s.NetNS.IsHost():
   264  		logrus.Debug("Using host netmode")
   265  		if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
   266  			return err
   267  		}
   268  
   269  	case s.NetNS.NSMode == specgen.NoNetwork:
   270  		logrus.Debug("Using none netmode")
   271  	case s.NetNS.NSMode == specgen.Bridge:
   272  		logrus.Debug("Using bridge netmode")
   273  	case s.NetNS.IsContainer():
   274  		logrus.Debugf("using container %s netmode", s.NetNS.Value)
   275  	case s.NetNS.IsPath():
   276  		logrus.Debug("Using ns netmode")
   277  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil {
   278  			return err
   279  		}
   280  	case s.NetNS.IsPod():
   281  		logrus.Debug("Using pod netmode, unless pod is not sharing")
   282  	case s.NetNS.NSMode == specgen.Slirp:
   283  		logrus.Debug("Using slirp4netns netmode")
   284  	default:
   285  		return errors.Errorf("unknown network mode")
   286  	}
   287  
   288  	if g.Config.Annotations == nil {
   289  		g.Config.Annotations = make(map[string]string)
   290  	}
   291  
   292  	if s.PublishImagePorts {
   293  		g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
   294  	} else {
   295  		g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func userConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error {
   302  	if s.UserNS.IsPath() {
   303  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil {
   304  			return err
   305  		}
   306  		// runc complains if no mapping is specified, even if we join another ns.  So provide a dummy mapping
   307  		g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
   308  		g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
   309  	}
   310  
   311  	if s.IDMappings != nil {
   312  		if (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) && !s.UserNS.IsHost() {
   313  			if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
   314  				return err
   315  			}
   316  		}
   317  		for _, uidmap := range s.IDMappings.UIDMap {
   318  			g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
   319  		}
   320  		for _, gidmap := range s.IDMappings.GIDMap {
   321  			g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
   322  		}
   323  	}
   324  	return nil
   325  }
   326  
   327  func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error {
   328  	// HANDLE CAPABILITIES
   329  	// NOTE: Must happen before SECCOMP
   330  	if s.Privileged {
   331  		g.SetupPrivileged(true)
   332  	}
   333  
   334  	useNotRoot := func(user string) bool {
   335  		if user == "" || user == "root" || user == "0" {
   336  			return false
   337  		}
   338  		return true
   339  	}
   340  	configSpec := g.Config
   341  	var err error
   342  	var caplist []string
   343  	bounding := configSpec.Process.Capabilities.Bounding
   344  	if useNotRoot(s.User) {
   345  		configSpec.Process.Capabilities.Bounding = caplist
   346  	}
   347  	caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop)
   348  	if err != nil {
   349  		return err
   350  	}
   351  
   352  	configSpec.Process.Capabilities.Bounding = caplist
   353  	configSpec.Process.Capabilities.Permitted = caplist
   354  	configSpec.Process.Capabilities.Inheritable = caplist
   355  	configSpec.Process.Capabilities.Effective = caplist
   356  	configSpec.Process.Capabilities.Ambient = caplist
   357  	if useNotRoot(s.User) {
   358  		caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop)
   359  		if err != nil {
   360  			return err
   361  		}
   362  	}
   363  	configSpec.Process.Capabilities.Bounding = caplist
   364  
   365  	// HANDLE SECCOMP
   366  	if s.SeccompProfilePath != "unconfined" {
   367  		seccompConfig, err := getSeccompConfig(s, configSpec, newImage)
   368  		if err != nil {
   369  			return err
   370  		}
   371  		configSpec.Linux.Seccomp = seccompConfig
   372  	}
   373  
   374  	// Clear default Seccomp profile from Generator for privileged containers
   375  	if s.SeccompProfilePath == "unconfined" || s.Privileged {
   376  		configSpec.Linux.Seccomp = nil
   377  	}
   378  
   379  	g.SetRootReadonly(s.ReadOnlyFilesystem)
   380  	for sysctlKey, sysctlVal := range s.Sysctl {
   381  		g.AddLinuxSysctl(sysctlKey, sysctlVal)
   382  	}
   383  
   384  	return nil
   385  }
   386  
   387  // GetNamespaceOptions transforms a slice of kernel namespaces
   388  // into a slice of pod create options. Currently, not all
   389  // kernel namespaces are supported, and they will be returned in an error
   390  func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
   391  	var options []libpod.PodCreateOption
   392  	var erroredOptions []libpod.PodCreateOption
   393  	for _, toShare := range ns {
   394  		switch toShare {
   395  		case "cgroup":
   396  			options = append(options, libpod.WithPodCgroups())
   397  		case "net":
   398  			options = append(options, libpod.WithPodNet())
   399  		case "mnt":
   400  			return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
   401  		case "pid":
   402  			options = append(options, libpod.WithPodPID())
   403  		case "user":
   404  			return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level")
   405  		case "ipc":
   406  			options = append(options, libpod.WithPodIPC())
   407  		case "uts":
   408  			options = append(options, libpod.WithPodUTS())
   409  		case "":
   410  		case "none":
   411  			return erroredOptions, nil
   412  		default:
   413  			return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare)
   414  		}
   415  	}
   416  	return options, nil
   417  }