github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/namespaces.go (about)

     1  package generate
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/containers/common/libimage"
     9  	"github.com/containers/common/libnetwork/types"
    10  	"github.com/containers/common/pkg/config"
    11  	"github.com/hanks177/podman/v4/libpod"
    12  	"github.com/hanks177/podman/v4/libpod/define"
    13  	"github.com/hanks177/podman/v4/pkg/rootless"
    14  	"github.com/hanks177/podman/v4/pkg/specgen"
    15  	"github.com/hanks177/podman/v4/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 == "net" && pod.NetworkMode() == "host":
    46  			toReturn.NSMode = specgen.Host
    47  			return toReturn, nil
    48  		case nsType == "cgroup" && pod.SharesCgroup():
    49  			podMode = true
    50  		}
    51  		if podMode {
    52  			toReturn.NSMode = specgen.FromPod
    53  			return toReturn, nil
    54  		}
    55  	}
    56  
    57  	if cfg == nil {
    58  		cfg = &config.Config{}
    59  	}
    60  	switch nsType {
    61  	case "pid":
    62  		return specgen.ParseNamespace(cfg.Containers.PidNS)
    63  	case "ipc":
    64  		return specgen.ParseIPCNamespace(cfg.Containers.IPCNS)
    65  	case "uts":
    66  		return specgen.ParseNamespace(cfg.Containers.UTSNS)
    67  	case "user":
    68  		return specgen.ParseUserNamespace(cfg.Containers.UserNS)
    69  	case "cgroup":
    70  		return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS)
    71  	case "net":
    72  		ns, _, _, err := specgen.ParseNetworkFlag(nil)
    73  		return ns, err
    74  	}
    75  
    76  	return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType)
    77  }
    78  
    79  // namespaceOptions generates container creation options for all
    80  // namespaces in a SpecGenerator.
    81  // Pod is the pod the container will join. May be nil is the container is not
    82  // joining a pod.
    83  // TODO: Consider grouping options that are not directly attached to a namespace
    84  // elsewhere.
    85  func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, imageData *libimage.ImageData) ([]libpod.CtrCreateOption, error) {
    86  	toReturn := []libpod.CtrCreateOption{}
    87  
    88  	// If pod is not nil, get infra container.
    89  	var infraCtr *libpod.Container
    90  	if pod != nil {
    91  		infraID, err := pod.InfraContainerID()
    92  		if err != nil {
    93  			// This is likely to be of the fatal kind (pod was
    94  			// removed) so hard fail
    95  			return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID())
    96  		}
    97  		if infraID != "" {
    98  			ctr, err := rt.GetContainer(infraID)
    99  			if err != nil {
   100  				return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID)
   101  			}
   102  			infraCtr = ctr
   103  		}
   104  	}
   105  
   106  	errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container")
   107  
   108  	// PID
   109  	switch s.PidNS.NSMode {
   110  	case specgen.FromPod:
   111  		if pod == nil || infraCtr == nil {
   112  			return nil, errNoInfra
   113  		}
   114  		toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr))
   115  	case specgen.FromContainer:
   116  		pidCtr, err := rt.LookupContainer(s.PidNS.Value)
   117  		if err != nil {
   118  			return nil, errors.Wrapf(err, "error looking up container to share pid namespace with")
   119  		}
   120  		toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr))
   121  	}
   122  
   123  	// IPC
   124  	switch s.IpcNS.NSMode {
   125  	case specgen.Host:
   126  		// Force use of host /dev/shm for host namespace
   127  		toReturn = append(toReturn, libpod.WithShmDir("/dev/shm"))
   128  	case specgen.FromPod:
   129  		if pod == nil || infraCtr == nil {
   130  			return nil, errNoInfra
   131  		}
   132  		toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr))
   133  		toReturn = append(toReturn, libpod.WithShmDir(infraCtr.ShmDir()))
   134  	case specgen.FromContainer:
   135  		ipcCtr, err := rt.LookupContainer(s.IpcNS.Value)
   136  		if err != nil {
   137  			return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with")
   138  		}
   139  		if ipcCtr.ConfigNoCopy().NoShmShare {
   140  			return nil, errors.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID())
   141  		}
   142  		toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr))
   143  		if !ipcCtr.ConfigNoCopy().NoShm {
   144  			toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
   145  		}
   146  	case specgen.None:
   147  		toReturn = append(toReturn, libpod.WithNoShm(true))
   148  	case specgen.Private:
   149  		toReturn = append(toReturn, libpod.WithNoShmShare(true))
   150  	}
   151  
   152  	// UTS
   153  	switch s.UtsNS.NSMode {
   154  	case specgen.FromPod:
   155  		if pod == nil || infraCtr == nil {
   156  			return nil, errNoInfra
   157  		}
   158  		toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr))
   159  	case specgen.FromContainer:
   160  		utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
   161  		if err != nil {
   162  			return nil, errors.Wrapf(err, "error looking up container to share uts namespace with")
   163  		}
   164  		toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr))
   165  	}
   166  
   167  	// User
   168  	switch s.UserNS.NSMode {
   169  	case specgen.KeepID:
   170  		if !rootless.IsRootless() {
   171  			return nil, errors.New("keep-id is only supported in rootless mode")
   172  		}
   173  		toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry())
   174  
   175  		// If user is not overridden, set user in the container
   176  		// to user running Podman.
   177  		if s.User == "" {
   178  			_, uid, gid, err := util.GetKeepIDMapping()
   179  			if err != nil {
   180  				return nil, err
   181  			}
   182  			toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid)))
   183  		}
   184  	case specgen.FromPod:
   185  		if pod == nil || infraCtr == nil {
   186  			return nil, errNoInfra
   187  		}
   188  		// Inherit the user from the infra container if it is set and --user has not
   189  		// been set explicitly
   190  		if infraCtr.User() != "" && s.User == "" {
   191  			toReturn = append(toReturn, libpod.WithUser(infraCtr.User()))
   192  		}
   193  		toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr))
   194  	case specgen.FromContainer:
   195  		userCtr, err := rt.LookupContainer(s.UserNS.Value)
   196  		if err != nil {
   197  			return nil, errors.Wrapf(err, "error looking up container to share user namespace with")
   198  		}
   199  		toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr))
   200  	}
   201  
   202  	// This wipes the UserNS settings that get set from the infra container
   203  	// when we are inheritting from the pod. So only apply this if the container
   204  	// is not being created in a pod.
   205  	if s.IDMappings != nil {
   206  		if pod == nil {
   207  			toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings))
   208  		} else if pod.HasInfraContainer() && (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) {
   209  			return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify a new uid/gid map when entering a pod with an infra container")
   210  		}
   211  	}
   212  	if s.User != "" {
   213  		toReturn = append(toReturn, libpod.WithUser(s.User))
   214  	}
   215  	if len(s.Groups) > 0 {
   216  		toReturn = append(toReturn, libpod.WithGroups(s.Groups))
   217  	}
   218  
   219  	// Cgroup
   220  	switch s.CgroupNS.NSMode {
   221  	case specgen.FromPod:
   222  		if pod == nil || infraCtr == nil {
   223  			return nil, errNoInfra
   224  		}
   225  		toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr))
   226  	case specgen.FromContainer:
   227  		cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value)
   228  		if err != nil {
   229  			return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with")
   230  		}
   231  		toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr))
   232  	}
   233  
   234  	if s.CgroupParent != "" {
   235  		toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent))
   236  	}
   237  
   238  	if s.CgroupsMode != "" {
   239  		toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode))
   240  	}
   241  
   242  	postConfigureNetNS := !s.UserNS.IsHost()
   243  	// when we are rootless we default to slirp4netns
   244  	if rootless.IsRootless() && (s.NetNS.IsPrivate() || s.NetNS.IsDefault()) {
   245  		s.NetNS.NSMode = specgen.Slirp
   246  	}
   247  
   248  	switch s.NetNS.NSMode {
   249  	case specgen.FromPod:
   250  		if pod == nil || infraCtr == nil {
   251  			return nil, errNoInfra
   252  		}
   253  		toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr))
   254  	case specgen.FromContainer:
   255  		netCtr, err := rt.LookupContainer(s.NetNS.Value)
   256  		if err != nil {
   257  			return nil, errors.Wrapf(err, "error looking up container to share net namespace with")
   258  		}
   259  		toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr))
   260  	case specgen.Slirp:
   261  		portMappings, expose, err := createPortMappings(s, imageData)
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  		val := "slirp4netns"
   266  		if s.NetNS.Value != "" {
   267  			val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
   268  		}
   269  		toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
   270  	case specgen.Bridge, specgen.Private, specgen.Default:
   271  		portMappings, expose, err := createPortMappings(s, imageData)
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  
   276  		rtConfig, err := rt.GetConfigNoCopy()
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  		// if no network was specified use add the default
   281  		if len(s.Networks) == 0 {
   282  			// backwards config still allow the old cni networks list and convert to new format
   283  			if len(s.CNINetworks) > 0 {
   284  				logrus.Warn(`specgen "cni_networks" option is deprecated use the "networks" map instead`)
   285  				networks := make(map[string]types.PerNetworkOptions, len(s.CNINetworks))
   286  				for _, net := range s.CNINetworks {
   287  					networks[net] = types.PerNetworkOptions{}
   288  				}
   289  				s.Networks = networks
   290  			} else {
   291  				// no networks given but bridge is set so use default network
   292  				s.Networks = map[string]types.PerNetworkOptions{
   293  					rtConfig.Network.DefaultNetwork: {},
   294  				}
   295  			}
   296  		}
   297  		// rename the "default" network to the correct default name
   298  		if opts, ok := s.Networks["default"]; ok {
   299  			s.Networks[rtConfig.Network.DefaultNetwork] = opts
   300  			delete(s.Networks, "default")
   301  		}
   302  		toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.Networks))
   303  	}
   304  
   305  	if s.UseImageHosts {
   306  		toReturn = append(toReturn, libpod.WithUseImageHosts())
   307  	} else if len(s.HostAdd) > 0 {
   308  		toReturn = append(toReturn, libpod.WithHosts(s.HostAdd))
   309  	}
   310  	if len(s.DNSSearch) > 0 {
   311  		toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch))
   312  	}
   313  	if s.UseImageResolvConf {
   314  		toReturn = append(toReturn, libpod.WithUseImageResolvConf())
   315  	} else if len(s.DNSServers) > 0 {
   316  		var dnsServers []string
   317  		for _, d := range s.DNSServers {
   318  			dnsServers = append(dnsServers, d.String())
   319  		}
   320  		toReturn = append(toReturn, libpod.WithDNS(dnsServers))
   321  	}
   322  	if len(s.DNSOptions) > 0 {
   323  		toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions))
   324  	}
   325  	if s.NetworkOptions != nil {
   326  		toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions))
   327  	}
   328  
   329  	return toReturn, nil
   330  }
   331  
   332  func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error {
   333  	// PID
   334  	switch s.PidNS.NSMode {
   335  	case specgen.Path:
   336  		if _, err := os.Stat(s.PidNS.Value); err != nil {
   337  			return errors.Wrap(err, "cannot find specified PID namespace path")
   338  		}
   339  		if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil {
   340  			return err
   341  		}
   342  	case specgen.Host:
   343  		if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil {
   344  			return err
   345  		}
   346  	case specgen.Private:
   347  		if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil {
   348  			return err
   349  		}
   350  	}
   351  
   352  	// IPC
   353  	switch s.IpcNS.NSMode {
   354  	case specgen.Path:
   355  		if _, err := os.Stat(s.IpcNS.Value); err != nil {
   356  			return errors.Wrap(err, "cannot find specified IPC namespace path")
   357  		}
   358  		if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil {
   359  			return err
   360  		}
   361  	case specgen.Host:
   362  		if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil {
   363  			return err
   364  		}
   365  	case specgen.Private:
   366  		if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil {
   367  			return err
   368  		}
   369  	}
   370  
   371  	// UTS
   372  	switch s.UtsNS.NSMode {
   373  	case specgen.Path:
   374  		if _, err := os.Stat(s.UtsNS.Value); err != nil {
   375  			return errors.Wrap(err, "cannot find specified UTS namespace path")
   376  		}
   377  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil {
   378  			return err
   379  		}
   380  	case specgen.Host:
   381  		if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil {
   382  			return err
   383  		}
   384  	case specgen.Private:
   385  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil {
   386  			return err
   387  		}
   388  	}
   389  
   390  	hostname := s.Hostname
   391  	if hostname == "" {
   392  		switch {
   393  		case s.UtsNS.NSMode == specgen.FromPod:
   394  			hostname = pod.Hostname()
   395  		case s.UtsNS.NSMode == specgen.FromContainer:
   396  			utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
   397  			if err != nil {
   398  				return errors.Wrapf(err, "error looking up container to share uts namespace with")
   399  			}
   400  			hostname = utsCtr.Hostname()
   401  		case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host:
   402  			tmpHostname, err := os.Hostname()
   403  			if err != nil {
   404  				return errors.Wrap(err, "unable to retrieve hostname of the host")
   405  			}
   406  			hostname = tmpHostname
   407  		default:
   408  			logrus.Debug("No hostname set; container's hostname will default to runtime default")
   409  		}
   410  	}
   411  
   412  	g.RemoveHostname()
   413  	if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host {
   414  		// Set the hostname in the OCI configuration only if specified by
   415  		// the user or if we are creating a new UTS namespace.
   416  		// TODO: Should we be doing this for pod or container shared
   417  		// namespaces?
   418  		g.SetHostname(hostname)
   419  	}
   420  	if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" {
   421  		g.AddProcessEnv("HOSTNAME", hostname)
   422  	}
   423  
   424  	// User
   425  	if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil {
   426  		return err
   427  	}
   428  
   429  	// Cgroup
   430  	switch s.CgroupNS.NSMode {
   431  	case specgen.Path:
   432  		if _, err := os.Stat(s.CgroupNS.Value); err != nil {
   433  			return errors.Wrap(err, "cannot find specified cgroup namespace path")
   434  		}
   435  		if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil {
   436  			return err
   437  		}
   438  	case specgen.Host:
   439  		if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil {
   440  			return err
   441  		}
   442  	case specgen.Private:
   443  		if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil {
   444  			return err
   445  		}
   446  	}
   447  
   448  	// Net
   449  	switch s.NetNS.NSMode {
   450  	case specgen.Path:
   451  		if _, err := os.Stat(s.NetNS.Value); err != nil {
   452  			return errors.Wrap(err, "cannot find specified network namespace path")
   453  		}
   454  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil {
   455  			return err
   456  		}
   457  	case specgen.Host:
   458  		if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
   459  			return err
   460  		}
   461  	case specgen.Private, specgen.NoNetwork:
   462  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil {
   463  			return err
   464  		}
   465  	}
   466  
   467  	if g.Config.Annotations == nil {
   468  		g.Config.Annotations = make(map[string]string)
   469  	}
   470  	if s.PublishExposedPorts {
   471  		g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
   472  	} else {
   473  		g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
   474  	}
   475  
   476  	return nil
   477  }
   478  
   479  // GetNamespaceOptions transforms a slice of kernel namespaces
   480  // into a slice of pod create options. Currently, not all
   481  // kernel namespaces are supported, and they will be returned in an error
   482  func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOption, error) {
   483  	var options []libpod.PodCreateOption
   484  	var erroredOptions []libpod.PodCreateOption
   485  	if ns == nil {
   486  		// set the default namespaces
   487  		ns = strings.Split(specgen.DefaultKernelNamespaces, ",")
   488  	}
   489  	for _, toShare := range ns {
   490  		switch toShare {
   491  		case "cgroup":
   492  			options = append(options, libpod.WithPodCgroup())
   493  		case "net":
   494  			// share the netns setting with other containers in the pod only when it is not set to host
   495  			if !netnsIsHost {
   496  				options = append(options, libpod.WithPodNet())
   497  			}
   498  		case "mnt":
   499  			return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
   500  		case "pid":
   501  			options = append(options, libpod.WithPodPID())
   502  		case "user":
   503  			continue
   504  		case "ipc":
   505  			options = append(options, libpod.WithPodIPC())
   506  		case "uts":
   507  			options = append(options, libpod.WithPodUTS())
   508  		case "":
   509  		case "none":
   510  			return erroredOptions, nil
   511  		default:
   512  			return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare)
   513  		}
   514  	}
   515  	return options, nil
   516  }