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

     1  package createconfig
     2  
     3  import (
     4  	"net"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/containers/libpod/libpod"
    10  	"github.com/containers/libpod/libpod/define"
    11  	"github.com/containers/libpod/pkg/cgroups"
    12  	"github.com/cri-o/ocicni/pkg/ocicni"
    13  	"github.com/docker/go-connections/nat"
    14  	spec "github.com/opencontainers/runtime-spec/specs-go"
    15  	"github.com/opencontainers/runtime-tools/generate"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  // ToCreateOptions converts the input to a slice of container create options.
    21  func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
    22  	var portBindings []ocicni.PortMapping
    23  	var err error
    24  	if len(c.PortBindings) > 0 {
    25  		portBindings, err = NatToOCIPortBindings(c.PortBindings)
    26  		if err != nil {
    27  			return nil, errors.Wrapf(err, "unable to create port bindings")
    28  		}
    29  	}
    30  
    31  	options := make([]libpod.CtrCreateOption, 0)
    32  	userNetworks := c.NetMode.UserDefined()
    33  	networks := make([]string, 0)
    34  
    35  	if IsPod(userNetworks) {
    36  		userNetworks = ""
    37  	}
    38  	if userNetworks != "" {
    39  		for _, netName := range strings.Split(userNetworks, ",") {
    40  			if netName == "" {
    41  				return nil, errors.Errorf("container networks %q invalid", userNetworks)
    42  			}
    43  			networks = append(networks, netName)
    44  		}
    45  	}
    46  
    47  	switch {
    48  	case c.NetMode.IsNS():
    49  		ns := c.NetMode.NS()
    50  		if ns == "" {
    51  			return nil, errors.Errorf("invalid empty user-defined network namespace")
    52  		}
    53  		_, err := os.Stat(ns)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  	case c.NetMode.IsContainer():
    58  		connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
    59  		if err != nil {
    60  			return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
    61  		}
    62  		options = append(options, libpod.WithNetNSFrom(connectedCtr))
    63  	case !c.NetMode.IsHost() && !c.NetMode.IsNone():
    64  		postConfigureNetNS := userns.getPostConfigureNetNS()
    65  		options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
    66  	}
    67  
    68  	if len(c.DNSSearch) > 0 {
    69  		options = append(options, libpod.WithDNSSearch(c.DNSSearch))
    70  	}
    71  	if len(c.DNSServers) > 0 {
    72  		if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
    73  			options = append(options, libpod.WithUseImageResolvConf())
    74  		} else {
    75  			options = append(options, libpod.WithDNS(c.DNSServers))
    76  		}
    77  	}
    78  	if len(c.DNSOpt) > 0 {
    79  		options = append(options, libpod.WithDNSOption(c.DNSOpt))
    80  	}
    81  	if c.IPAddress != "" {
    82  		ip := net.ParseIP(c.IPAddress)
    83  		if ip == nil {
    84  			return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
    85  		} else if ip.To4() == nil {
    86  			return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
    87  		}
    88  		options = append(options, libpod.WithStaticIP(ip))
    89  	}
    90  
    91  	if c.MacAddress != "" {
    92  		mac, err := net.ParseMAC(c.MacAddress)
    93  		if err != nil {
    94  			return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
    95  		}
    96  		options = append(options, libpod.WithStaticMAC(mac))
    97  	}
    98  
    99  	return options, nil
   100  }
   101  
   102  // ConfigureGenerator configures the generator based according to the current
   103  // state of the NetworkConfig.
   104  func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
   105  	netMode := c.NetMode
   106  	netCtr := netMode.Container()
   107  	switch {
   108  	case netMode.IsHost():
   109  		logrus.Debug("Using host netmode")
   110  		if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
   111  			return err
   112  		}
   113  	case netMode.IsNone():
   114  		logrus.Debug("Using none netmode")
   115  	case netMode.IsBridge():
   116  		logrus.Debug("Using bridge netmode")
   117  	case netCtr != "":
   118  		logrus.Debugf("using container %s netmode", netCtr)
   119  	case IsNS(string(netMode)):
   120  		logrus.Debug("Using ns netmode")
   121  		if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
   122  			return err
   123  		}
   124  	case IsPod(string(netMode)):
   125  		logrus.Debug("Using pod netmode, unless pod is not sharing")
   126  	case netMode.IsSlirp4netns():
   127  		logrus.Debug("Using slirp4netns netmode")
   128  	case netMode.IsUserDefined():
   129  		logrus.Debug("Using user defined netmode")
   130  	default:
   131  		return errors.Errorf("unknown network mode")
   132  	}
   133  
   134  	if c.HTTPProxy {
   135  		for _, envSpec := range []string{
   136  			"http_proxy",
   137  			"HTTP_PROXY",
   138  			"https_proxy",
   139  			"HTTPS_PROXY",
   140  			"ftp_proxy",
   141  			"FTP_PROXY",
   142  			"no_proxy",
   143  			"NO_PROXY",
   144  		} {
   145  			envVal := os.Getenv(envSpec)
   146  			if envVal != "" {
   147  				g.AddProcessEnv(envSpec, envVal)
   148  			}
   149  		}
   150  	}
   151  
   152  	if g.Config.Annotations == nil {
   153  		g.Config.Annotations = make(map[string]string)
   154  	}
   155  
   156  	if c.PublishAll {
   157  		g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
   158  	} else {
   159  		g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
   166  func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
   167  	var portBindings []ocicni.PortMapping
   168  	for containerPb, hostPb := range ports {
   169  		var pm ocicni.PortMapping
   170  		pm.ContainerPort = int32(containerPb.Int())
   171  		for _, i := range hostPb {
   172  			var hostPort int
   173  			var err error
   174  			pm.HostIP = i.HostIP
   175  			if i.HostPort == "" {
   176  				hostPort = containerPb.Int()
   177  			} else {
   178  				hostPort, err = strconv.Atoi(i.HostPort)
   179  				if err != nil {
   180  					return nil, errors.Wrapf(err, "unable to convert host port to integer")
   181  				}
   182  			}
   183  
   184  			pm.HostPort = int32(hostPort)
   185  			pm.Protocol = containerPb.Proto()
   186  			portBindings = append(portBindings, pm)
   187  		}
   188  	}
   189  	return portBindings, nil
   190  }
   191  
   192  // ToCreateOptions converts the input to container create options.
   193  func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
   194  	options := make([]libpod.CtrCreateOption, 0)
   195  	if c.CgroupMode.IsNS() {
   196  		ns := c.CgroupMode.NS()
   197  		if ns == "" {
   198  			return nil, errors.Errorf("invalid empty user-defined network namespace")
   199  		}
   200  		_, err := os.Stat(ns)
   201  		if err != nil {
   202  			return nil, err
   203  		}
   204  	} else if c.CgroupMode.IsContainer() {
   205  		connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
   206  		if err != nil {
   207  			return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
   208  		}
   209  		options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
   210  	}
   211  
   212  	if c.CgroupParent != "" {
   213  		options = append(options, libpod.WithCgroupParent(c.CgroupParent))
   214  	}
   215  
   216  	if c.Cgroups != "" {
   217  		options = append(options, libpod.WithCgroupsMode(c.Cgroups))
   218  	}
   219  
   220  	return options, nil
   221  }
   222  
   223  // ToCreateOptions converts the input to container create options.
   224  func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
   225  	options := make([]libpod.CtrCreateOption, 0)
   226  	switch {
   227  	case c.UsernsMode.IsNS():
   228  		ns := c.UsernsMode.NS()
   229  		if ns == "" {
   230  			return nil, errors.Errorf("invalid empty user-defined user namespace")
   231  		}
   232  		_, err := os.Stat(ns)
   233  		if err != nil {
   234  			return nil, err
   235  		}
   236  		options = append(options, libpod.WithIDMappings(*c.IDMappings))
   237  	case c.UsernsMode.IsContainer():
   238  		connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
   239  		if err != nil {
   240  			return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
   241  		}
   242  		options = append(options, libpod.WithUserNSFrom(connectedCtr))
   243  	default:
   244  		options = append(options, libpod.WithIDMappings(*c.IDMappings))
   245  	}
   246  
   247  	options = append(options, libpod.WithUser(c.User))
   248  	options = append(options, libpod.WithGroups(c.GroupAdd))
   249  
   250  	return options, nil
   251  }
   252  
   253  // ConfigureGenerator configures the generator according to the current state
   254  // of the UserConfig.
   255  func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
   256  	if IsNS(string(c.UsernsMode)) {
   257  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
   258  			return err
   259  		}
   260  		// runc complains if no mapping is specified, even if we join another ns.  So provide a dummy mapping
   261  		g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
   262  		g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
   263  	}
   264  
   265  	if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
   266  		if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
   267  			return err
   268  		}
   269  	}
   270  	for _, uidmap := range c.IDMappings.UIDMap {
   271  		g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
   272  	}
   273  	for _, gidmap := range c.IDMappings.GIDMap {
   274  		g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
   275  	}
   276  	return nil
   277  }
   278  
   279  func (c *UserConfig) getPostConfigureNetNS() bool {
   280  	hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
   281  	postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
   282  	return postConfigureNetNS
   283  }
   284  
   285  // InNS returns true if the UserConfig indicates to be in a dedicated user
   286  // namespace.
   287  func (c *UserConfig) InNS(isRootless bool) bool {
   288  	hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
   289  	return isRootless || (hasUserns && !c.UsernsMode.IsHost())
   290  }
   291  
   292  // ToCreateOptions converts the input to container create options.
   293  func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
   294  	options := make([]libpod.CtrCreateOption, 0)
   295  	if c.IpcMode.IsHost() {
   296  		options = append(options, libpod.WithShmDir("/dev/shm"))
   297  	} else if c.IpcMode.IsContainer() {
   298  		connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
   299  		if err != nil {
   300  			return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
   301  		}
   302  
   303  		options = append(options, libpod.WithIPCNSFrom(connectedCtr))
   304  		options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
   305  	}
   306  
   307  	return options, nil
   308  }
   309  
   310  // ConfigureGenerator configures the generator according to the current state
   311  // of the IpcConfig.
   312  func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
   313  	ipcMode := c.IpcMode
   314  	if IsNS(string(ipcMode)) {
   315  		return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
   316  	}
   317  	if ipcMode.IsHost() {
   318  		return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
   319  	}
   320  	if ipcCtr := ipcMode.Container(); ipcCtr != "" {
   321  		logrus.Debugf("Using container %s ipcmode", ipcCtr)
   322  	}
   323  
   324  	return nil
   325  }
   326  
   327  // ConfigureGenerator configures the generator according to the current state
   328  // of the CgroupConfig.
   329  func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
   330  	cgroupMode := c.CgroupMode
   331  	if cgroupMode.IsDefaultValue() {
   332  		// If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
   333  		unified, err := cgroups.IsCgroup2UnifiedMode()
   334  		if err != nil {
   335  			return err
   336  		}
   337  		if unified {
   338  			cgroupMode = "private"
   339  		} else {
   340  			cgroupMode = "host"
   341  		}
   342  	}
   343  	if cgroupMode.IsNS() {
   344  		return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
   345  	}
   346  	if cgroupMode.IsHost() {
   347  		return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
   348  	}
   349  	if cgroupMode.IsPrivate() {
   350  		return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
   351  	}
   352  	if cgCtr := cgroupMode.Container(); cgCtr != "" {
   353  		logrus.Debugf("Using container %s cgroup mode", cgCtr)
   354  	}
   355  	return nil
   356  }
   357  
   358  // ToCreateOptions converts the input to container create options.
   359  func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
   360  	options := make([]libpod.CtrCreateOption, 0)
   361  	if c.PidMode.IsContainer() {
   362  		connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
   363  		if err != nil {
   364  			return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
   365  		}
   366  
   367  		options = append(options, libpod.WithPIDNSFrom(connectedCtr))
   368  	}
   369  
   370  	return options, nil
   371  }
   372  
   373  // ConfigureGenerator configures the generator according to the current state
   374  // of the PidConfig.
   375  func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
   376  	pidMode := c.PidMode
   377  	if IsNS(string(pidMode)) {
   378  		return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
   379  	}
   380  	if pidMode.IsHost() {
   381  		return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
   382  	}
   383  	if pidCtr := pidMode.Container(); pidCtr != "" {
   384  		logrus.Debugf("using container %s pidmode", pidCtr)
   385  	}
   386  	if IsPod(string(pidMode)) {
   387  		logrus.Debug("using pod pidmode")
   388  	}
   389  	return nil
   390  }
   391  
   392  // ToCreateOptions converts the input to container create options.
   393  func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
   394  	options := make([]libpod.CtrCreateOption, 0)
   395  	if IsPod(string(c.UtsMode)) {
   396  		options = append(options, libpod.WithUTSNSFromPod(pod))
   397  	}
   398  	if c.UtsMode.IsContainer() {
   399  		connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
   400  		if err != nil {
   401  			return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
   402  		}
   403  
   404  		options = append(options, libpod.WithUTSNSFrom(connectedCtr))
   405  	}
   406  	if c.NoHosts {
   407  		options = append(options, libpod.WithUseImageHosts())
   408  	}
   409  	if len(c.HostAdd) > 0 && !c.NoHosts {
   410  		options = append(options, libpod.WithHosts(c.HostAdd))
   411  	}
   412  
   413  	return options, nil
   414  }
   415  
   416  // ConfigureGenerator configures the generator according to the current state
   417  // of the UtsConfig.
   418  func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
   419  	hostname := c.Hostname
   420  	utsCtrID := c.UtsMode.Container()
   421  	var err error
   422  	if hostname == "" {
   423  		switch {
   424  		case utsCtrID != "":
   425  			utsCtr, err := runtime.LookupContainer(utsCtrID)
   426  			if err != nil {
   427  				return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
   428  			}
   429  			hostname = utsCtr.Hostname()
   430  		case net.NetMode.IsHost() || c.UtsMode.IsHost():
   431  			hostname, err = os.Hostname()
   432  			if err != nil {
   433  				return errors.Wrap(err, "unable to retrieve hostname of the host")
   434  			}
   435  		default:
   436  			logrus.Debug("No hostname set; container's hostname will default to runtime default")
   437  		}
   438  	}
   439  	g.RemoveHostname()
   440  	if c.Hostname != "" || !c.UtsMode.IsHost() {
   441  		// Set the hostname in the OCI configuration only
   442  		// if specified by the user or if we are creating
   443  		// a new UTS namespace.
   444  		g.SetHostname(hostname)
   445  	}
   446  	g.AddProcessEnv("HOSTNAME", hostname)
   447  
   448  	utsMode := c.UtsMode
   449  	if IsNS(string(utsMode)) {
   450  		return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
   451  	}
   452  	if utsMode.IsHost() {
   453  		return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
   454  	}
   455  	if utsCtr := utsMode.Container(); utsCtr != "" {
   456  		logrus.Debugf("using container %s utsmode", utsCtr)
   457  	}
   458  	return nil
   459  }