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

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