github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/namespaces.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/containers/common/pkg/config"
    10  	"github.com/containers/podman/v2/libpod"
    11  	"github.com/containers/podman/v2/libpod/define"
    12  	"github.com/containers/podman/v2/libpod/image"
    13  	"github.com/containers/podman/v2/pkg/rootless"
    14  	"github.com/containers/podman/v2/pkg/specgen"
    15  	"github.com/containers/podman/v2/pkg/util"
    16  	spec "github.com/opencontainers/runtime-spec/specs-go"
    17  	"github.com/opencontainers/runtime-tools/generate"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  // Get the default namespace mode for any given namespace type.
    23  func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) {
    24  	// The default for most is private
    25  	toReturn := specgen.Namespace{}
    26  	toReturn.NSMode = specgen.Private
    27  
    28  	// Ensure case insensitivity
    29  	nsType = strings.ToLower(nsType)
    30  
    31  	// If the pod is not nil - check shared namespaces
    32  	if pod != nil && pod.HasInfraContainer() {
    33  		podMode := false
    34  		switch {
    35  		case nsType == "pid" && pod.SharesPID():
    36  			podMode = true
    37  		case nsType == "ipc" && pod.SharesIPC():
    38  			podMode = true
    39  		case nsType == "uts" && pod.SharesUTS():
    40  			podMode = true
    41  		case nsType == "user" && pod.SharesUser():
    42  			podMode = true
    43  		case nsType == "net" && pod.SharesNet():
    44  			podMode = true
    45  		case nsType == "cgroup" && pod.SharesCgroup():
    46  			podMode = true
    47  		}
    48  		if podMode {
    49  			toReturn.NSMode = specgen.FromPod
    50  			return toReturn, nil
    51  		}
    52  	}
    53  
    54  	if cfg == nil {
    55  		cfg = &config.Config{}
    56  	}
    57  	switch nsType {
    58  	case "pid":
    59  		return specgen.ParseNamespace(cfg.Containers.PidNS)
    60  	case "ipc":
    61  		return specgen.ParseNamespace(cfg.Containers.IPCNS)
    62  	case "uts":
    63  		return specgen.ParseNamespace(cfg.Containers.UTSNS)
    64  	case "user":
    65  		return specgen.ParseUserNamespace(cfg.Containers.UserNS)
    66  	case "cgroup":
    67  		return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS)
    68  	case "net":
    69  		ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS)
    70  		return ns, err
    71  	}
    72  
    73  	return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType)
    74  }
    75  
    76  // namespaceOptions generates container creation options for all
    77  // namespaces in a SpecGenerator.
    78  // Pod is the pod the container will join. May be nil is the container is not
    79  // joining a pod.
    80  // TODO: Consider grouping options that are not directly attached to a namespace
    81  // elsewhere.
    82  func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, img *image.Image) ([]libpod.CtrCreateOption, error) {
    83  	toReturn := []libpod.CtrCreateOption{}
    84  
    85  	// If pod is not nil, get infra container.
    86  	var infraCtr *libpod.Container
    87  	if pod != nil {
    88  		infraID, err := pod.InfraContainerID()
    89  		if err != nil {
    90  			// This is likely to be of the fatal kind (pod was
    91  			// removed) so hard fail
    92  			return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID())
    93  		}
    94  		if infraID != "" {
    95  			ctr, err := rt.GetContainer(infraID)
    96  			if err != nil {
    97  				return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID)
    98  			}
    99  			infraCtr = ctr
   100  		}
   101  	}
   102  
   103  	errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container")
   104  
   105  	// PID
   106  	switch s.PidNS.NSMode {
   107  	case specgen.FromPod:
   108  		if pod == nil || infraCtr == nil {
   109  			return nil, errNoInfra
   110  		}
   111  		toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr))
   112  	case specgen.FromContainer:
   113  		pidCtr, err := rt.LookupContainer(s.PidNS.Value)
   114  		if err != nil {
   115  			return nil, errors.Wrapf(err, "error looking up container to share pid namespace with")
   116  		}
   117  		toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr))
   118  	}
   119  
   120  	// IPC
   121  	switch s.IpcNS.NSMode {
   122  	case specgen.Host:
   123  		// Force use of host /dev/shm for host namespace
   124  		toReturn = append(toReturn, libpod.WithShmDir("/dev/shm"))
   125  	case specgen.FromPod:
   126  		if pod == nil || infraCtr == nil {
   127  			return nil, errNoInfra
   128  		}
   129  		toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr))
   130  		toReturn = append(toReturn, libpod.WithShmDir(infraCtr.ShmDir()))
   131  	case specgen.FromContainer:
   132  		ipcCtr, err := rt.LookupContainer(s.IpcNS.Value)
   133  		if err != nil {
   134  			return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with")
   135  		}
   136  		toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr))
   137  		toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
   138  	}
   139  
   140  	// UTS
   141  	switch s.UtsNS.NSMode {
   142  	case specgen.FromPod:
   143  		if pod == nil || infraCtr == nil {
   144  			return nil, errNoInfra
   145  		}
   146  		toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr))
   147  	case specgen.FromContainer:
   148  		utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
   149  		if err != nil {
   150  			return nil, errors.Wrapf(err, "error looking up container to share uts namespace with")
   151  		}
   152  		toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr))
   153  	}
   154  
   155  	// User
   156  	switch s.UserNS.NSMode {
   157  	case specgen.KeepID:
   158  		if rootless.IsRootless() {
   159  			toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
   160  		} else {
   161  			// keep-id as root doesn't need a user namespace
   162  			s.UserNS.NSMode = specgen.Host
   163  		}
   164  	case specgen.FromPod:
   165  		if pod == nil || infraCtr == nil {
   166  			return nil, errNoInfra
   167  		}
   168  		toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr))
   169  	case specgen.FromContainer:
   170  		userCtr, err := rt.LookupContainer(s.UserNS.Value)
   171  		if err != nil {
   172  			return nil, errors.Wrapf(err, "error looking up container to share user namespace with")
   173  		}
   174  		toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr))
   175  	}
   176  
   177  	if s.IDMappings != nil {
   178  		toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings))
   179  	}
   180  	if s.User != "" {
   181  		toReturn = append(toReturn, libpod.WithUser(s.User))
   182  	}
   183  	if len(s.Groups) > 0 {
   184  		toReturn = append(toReturn, libpod.WithGroups(s.Groups))
   185  	}
   186  
   187  	// Cgroup
   188  	switch s.CgroupNS.NSMode {
   189  	case specgen.FromPod:
   190  		if pod == nil || infraCtr == nil {
   191  			return nil, errNoInfra
   192  		}
   193  		toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr))
   194  	case specgen.FromContainer:
   195  		cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value)
   196  		if err != nil {
   197  			return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with")
   198  		}
   199  		toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr))
   200  	}
   201  
   202  	if s.CgroupParent != "" {
   203  		toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent))
   204  	}
   205  
   206  	if s.CgroupsMode != "" {
   207  		toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode))
   208  	}
   209  
   210  	// Net
   211  	// TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we
   212  	// are in bridge mode.
   213  	postConfigureNetNS := !s.UserNS.IsHost()
   214  	switch s.NetNS.NSMode {
   215  	case specgen.FromPod:
   216  		if pod == nil || infraCtr == nil {
   217  			return nil, errNoInfra
   218  		}
   219  		toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr))
   220  	case specgen.FromContainer:
   221  		netCtr, err := rt.LookupContainer(s.NetNS.Value)
   222  		if err != nil {
   223  			return nil, errors.Wrapf(err, "error looking up container to share net namespace with")
   224  		}
   225  		toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr))
   226  	case specgen.Slirp:
   227  		portMappings, err := createPortMappings(ctx, s, img)
   228  		if err != nil {
   229  			return nil, err
   230  		}
   231  		val := "slirp4netns"
   232  		if s.NetNS.Value != "" {
   233  			val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
   234  		}
   235  		toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil))
   236  	case specgen.Private:
   237  		fallthrough
   238  	case specgen.Bridge:
   239  		portMappings, err := createPortMappings(ctx, s, img)
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  		toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "bridge", s.CNINetworks))
   244  	}
   245  
   246  	if s.UseImageHosts {
   247  		toReturn = append(toReturn, libpod.WithUseImageHosts())
   248  	} else if len(s.HostAdd) > 0 {
   249  		toReturn = append(toReturn, libpod.WithHosts(s.HostAdd))
   250  	}
   251  	if len(s.DNSSearch) > 0 {
   252  		toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch))
   253  	}
   254  	if s.UseImageResolvConf {
   255  		toReturn = append(toReturn, libpod.WithUseImageResolvConf())
   256  	} else if len(s.DNSServers) > 0 {
   257  		var dnsServers []string
   258  		for _, d := range s.DNSServers {
   259  			dnsServers = append(dnsServers, d.String())
   260  		}
   261  		toReturn = append(toReturn, libpod.WithDNS(dnsServers))
   262  	}
   263  	if len(s.DNSOptions) > 0 {
   264  		toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions))
   265  	}
   266  	if s.StaticIP != nil {
   267  		toReturn = append(toReturn, libpod.WithStaticIP(*s.StaticIP))
   268  	}
   269  	if s.StaticMAC != nil {
   270  		toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC))
   271  	}
   272  	if s.NetworkOptions != nil {
   273  		toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions))
   274  	}
   275  
   276  	return toReturn, nil
   277  }
   278  
   279  func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error {
   280  	// PID
   281  	switch s.PidNS.NSMode {
   282  	case specgen.Path:
   283  		if _, err := os.Stat(s.PidNS.Value); err != nil {
   284  			return errors.Wrap(err, "cannot find specified PID namespace path")
   285  		}
   286  		if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil {
   287  			return err
   288  		}
   289  	case specgen.Host:
   290  		if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil {
   291  			return err
   292  		}
   293  	case specgen.Private:
   294  		if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil {
   295  			return err
   296  		}
   297  	}
   298  
   299  	// IPC
   300  	switch s.IpcNS.NSMode {
   301  	case specgen.Path:
   302  		if _, err := os.Stat(s.IpcNS.Value); err != nil {
   303  			return errors.Wrap(err, "cannot find specified IPC namespace path")
   304  		}
   305  		if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil {
   306  			return err
   307  		}
   308  	case specgen.Host:
   309  		if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil {
   310  			return err
   311  		}
   312  	case specgen.Private:
   313  		if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil {
   314  			return err
   315  		}
   316  	}
   317  
   318  	// UTS
   319  	switch s.UtsNS.NSMode {
   320  	case specgen.Path:
   321  		if _, err := os.Stat(s.UtsNS.Value); err != nil {
   322  			return errors.Wrap(err, "cannot find specified UTS namespace path")
   323  		}
   324  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil {
   325  			return err
   326  		}
   327  	case specgen.Host:
   328  		if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil {
   329  			return err
   330  		}
   331  	case specgen.Private:
   332  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil {
   333  			return err
   334  		}
   335  	}
   336  
   337  	hostname := s.Hostname
   338  	if hostname == "" {
   339  		switch {
   340  		case s.UtsNS.NSMode == specgen.FromPod:
   341  			hostname = pod.Hostname()
   342  		case s.UtsNS.NSMode == specgen.FromContainer:
   343  			utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
   344  			if err != nil {
   345  				return errors.Wrapf(err, "error looking up container to share uts namespace with")
   346  			}
   347  			hostname = utsCtr.Hostname()
   348  		case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host:
   349  			tmpHostname, err := os.Hostname()
   350  			if err != nil {
   351  				return errors.Wrap(err, "unable to retrieve hostname of the host")
   352  			}
   353  			hostname = tmpHostname
   354  		default:
   355  			logrus.Debug("No hostname set; container's hostname will default to runtime default")
   356  		}
   357  	}
   358  
   359  	g.RemoveHostname()
   360  	if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host {
   361  		// Set the hostname in the OCI configuration only if specified by
   362  		// the user or if we are creating a new UTS namespace.
   363  		// TODO: Should we be doing this for pod or container shared
   364  		// namespaces?
   365  		g.SetHostname(hostname)
   366  	}
   367  	g.AddProcessEnv("HOSTNAME", hostname)
   368  
   369  	// User
   370  	switch s.UserNS.NSMode {
   371  	case specgen.Path:
   372  		if _, err := os.Stat(s.UserNS.Value); err != nil {
   373  			return errors.Wrap(err, "cannot find specified user namespace path")
   374  		}
   375  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil {
   376  			return err
   377  		}
   378  		// runc complains if no mapping is specified, even if we join another ns.  So provide a dummy mapping
   379  		g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
   380  		g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
   381  	case specgen.Host:
   382  		if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil {
   383  			return err
   384  		}
   385  	case specgen.KeepID:
   386  		var (
   387  			err      error
   388  			uid, gid int
   389  		)
   390  		s.IDMappings, uid, gid, err = util.GetKeepIDMapping()
   391  		if err != nil {
   392  			return err
   393  		}
   394  		g.SetProcessUID(uint32(uid))
   395  		g.SetProcessGID(uint32(gid))
   396  		fallthrough
   397  	case specgen.Private:
   398  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
   399  			return err
   400  		}
   401  		if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) {
   402  			return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace")
   403  		}
   404  		for _, uidmap := range s.IDMappings.UIDMap {
   405  			g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
   406  		}
   407  		for _, gidmap := range s.IDMappings.GIDMap {
   408  			g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
   409  		}
   410  	}
   411  
   412  	// Cgroup
   413  	switch s.CgroupNS.NSMode {
   414  	case specgen.Path:
   415  		if _, err := os.Stat(s.CgroupNS.Value); err != nil {
   416  			return errors.Wrap(err, "cannot find specified cgroup namespace path")
   417  		}
   418  		if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil {
   419  			return err
   420  		}
   421  	case specgen.Host:
   422  		if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil {
   423  			return err
   424  		}
   425  	case specgen.Private:
   426  		if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil {
   427  			return err
   428  		}
   429  	}
   430  
   431  	// Net
   432  	switch s.NetNS.NSMode {
   433  	case specgen.Path:
   434  		if _, err := os.Stat(s.NetNS.Value); err != nil {
   435  			return errors.Wrap(err, "cannot find specified network namespace path")
   436  		}
   437  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil {
   438  			return err
   439  		}
   440  	case specgen.Host:
   441  		if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
   442  			return err
   443  		}
   444  	case specgen.Private, specgen.NoNetwork:
   445  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil {
   446  			return err
   447  		}
   448  	}
   449  
   450  	if g.Config.Annotations == nil {
   451  		g.Config.Annotations = make(map[string]string)
   452  	}
   453  	if s.PublishExposedPorts {
   454  		g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
   455  	} else {
   456  		g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
   457  	}
   458  
   459  	return nil
   460  }
   461  
   462  // GetNamespaceOptions transforms a slice of kernel namespaces
   463  // into a slice of pod create options. Currently, not all
   464  // kernel namespaces are supported, and they will be returned in an error
   465  func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
   466  	var options []libpod.PodCreateOption
   467  	var erroredOptions []libpod.PodCreateOption
   468  	if ns == nil {
   469  		//set the default namespaces
   470  		ns = strings.Split(specgen.DefaultKernelNamespaces, ",")
   471  	}
   472  	for _, toShare := range ns {
   473  		switch toShare {
   474  		case "cgroup":
   475  			options = append(options, libpod.WithPodCgroups())
   476  		case "net":
   477  			options = append(options, libpod.WithPodNet())
   478  		case "mnt":
   479  			return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
   480  		case "pid":
   481  			options = append(options, libpod.WithPodPID())
   482  		case "user":
   483  			continue
   484  		case "ipc":
   485  			options = append(options, libpod.WithPodIPC())
   486  		case "uts":
   487  			options = append(options, libpod.WithPodUTS())
   488  		case "":
   489  		case "none":
   490  			return erroredOptions, nil
   491  		default:
   492  			return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare)
   493  		}
   494  	}
   495  	return options, nil
   496  }