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

     1  package specgen
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/containers/podman/v2/pkg/cgroups"
     7  	"github.com/containers/podman/v2/pkg/rootless"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  type NamespaceMode string
    12  
    13  const (
    14  	// Default indicates the spec generator should determine
    15  	// a sane default
    16  	Default NamespaceMode = "default"
    17  	// Host means the the namespace is derived from
    18  	// the host
    19  	Host NamespaceMode = "host"
    20  	// Path is the path to a namespace
    21  	Path NamespaceMode = "path"
    22  	// FromContainer means namespace is derived from a
    23  	// different container
    24  	FromContainer NamespaceMode = "container"
    25  	// FromPod indicates the namespace is derived from a pod
    26  	FromPod NamespaceMode = "pod"
    27  	// Private indicates the namespace is private
    28  	Private NamespaceMode = "private"
    29  	// NoNetwork indicates no network namespace should
    30  	// be joined.  loopback should still exists.
    31  	// Only used with the network namespace, invalid otherwise.
    32  	NoNetwork NamespaceMode = "none"
    33  	// Bridge indicates that a CNI network stack
    34  	// should be used.
    35  	// Only used with the network namespace, invalid otherwise.
    36  	Bridge NamespaceMode = "bridge"
    37  	// Slirp indicates that a slirp4netns network stack should
    38  	// be used.
    39  	// Only used with the network namespace, invalid otherwise.
    40  	Slirp NamespaceMode = "slirp4netns"
    41  	// KeepId indicates a user namespace to keep the owner uid inside
    42  	// of the namespace itself.
    43  	// Only used with the user namespace, invalid otherwise.
    44  	KeepID NamespaceMode = "keep-id"
    45  	// Auto indicates to automatically create a user namespace.
    46  	// Only used with the user namespace, invalid otherwise.
    47  	Auto NamespaceMode = "auto"
    48  
    49  	// DefaultKernelNamespaces is a comma-separated list of default kernel
    50  	// namespaces.
    51  	DefaultKernelNamespaces = "cgroup,ipc,net,uts"
    52  )
    53  
    54  // Namespace describes the namespace
    55  type Namespace struct {
    56  	NSMode NamespaceMode `json:"nsmode,omitempty"`
    57  	Value  string        `json:"string,omitempty"`
    58  }
    59  
    60  // IsDefault returns whether the namespace is set to the default setting (which
    61  // also includes the empty string).
    62  func (n *Namespace) IsDefault() bool {
    63  	return n.NSMode == Default || n.NSMode == ""
    64  }
    65  
    66  // IsHost returns a bool if the namespace is host based
    67  func (n *Namespace) IsHost() bool {
    68  	return n.NSMode == Host
    69  }
    70  
    71  // IsPath indicates via bool if the namespace is based on a path
    72  func (n *Namespace) IsPath() bool {
    73  	return n.NSMode == Path
    74  }
    75  
    76  // IsContainer indicates via bool if the namespace is based on a container
    77  func (n *Namespace) IsContainer() bool {
    78  	return n.NSMode == FromContainer
    79  }
    80  
    81  // IsPod indicates via bool if the namespace is based on a pod
    82  func (n *Namespace) IsPod() bool {
    83  	return n.NSMode == FromPod
    84  }
    85  
    86  // IsPrivate indicates the namespace is private
    87  func (n *Namespace) IsPrivate() bool {
    88  	return n.NSMode == Private
    89  }
    90  
    91  // IsAuto indicates the namespace is auto
    92  func (n *Namespace) IsAuto() bool {
    93  	return n.NSMode == Auto
    94  }
    95  
    96  // IsKeepID indicates the namespace is KeepID
    97  func (n *Namespace) IsKeepID() bool {
    98  	return n.NSMode == KeepID
    99  }
   100  
   101  func validateUserNS(n *Namespace) error {
   102  	if n == nil {
   103  		return nil
   104  	}
   105  	switch n.NSMode {
   106  	case Auto, KeepID:
   107  		return nil
   108  	}
   109  	return n.validate()
   110  }
   111  
   112  func validateNetNS(n *Namespace) error {
   113  	if n == nil {
   114  		return nil
   115  	}
   116  	switch n.NSMode {
   117  	case Slirp:
   118  		break
   119  	case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge:
   120  		break
   121  	default:
   122  		return errors.Errorf("invalid network %q", n.NSMode)
   123  	}
   124  
   125  	// Path and From Container MUST have a string value set
   126  	if n.NSMode == Path || n.NSMode == FromContainer {
   127  		if len(n.Value) < 1 {
   128  			return errors.Errorf("namespace mode %s requires a value", n.NSMode)
   129  		}
   130  	} else if n.NSMode != Slirp {
   131  		// All others except must NOT set a string value
   132  		if len(n.Value) > 0 {
   133  			return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode)
   134  		}
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // Validate perform simple validation on the namespace to make sure it is not
   141  // invalid from the get-go
   142  func (n *Namespace) validate() error {
   143  	if n == nil {
   144  		return nil
   145  	}
   146  	switch n.NSMode {
   147  	case "", Default, Host, Path, FromContainer, FromPod, Private:
   148  		// Valid, do nothing
   149  	case NoNetwork, Bridge, Slirp:
   150  		return errors.Errorf("cannot use network modes with non-network namespace")
   151  	default:
   152  		return errors.Errorf("invalid namespace type %s specified", n.NSMode)
   153  	}
   154  
   155  	// Path and From Container MUST have a string value set
   156  	if n.NSMode == Path || n.NSMode == FromContainer {
   157  		if len(n.Value) < 1 {
   158  			return errors.Errorf("namespace mode %s requires a value", n.NSMode)
   159  		}
   160  	} else {
   161  		// All others must NOT set a string value
   162  		if len(n.Value) > 0 {
   163  			return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode)
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  // ParseNamespace parses a namespace in string form.
   170  // This is not intended for the network namespace, which has a separate
   171  // function.
   172  func ParseNamespace(ns string) (Namespace, error) {
   173  	toReturn := Namespace{}
   174  	switch {
   175  	case ns == "pod":
   176  		toReturn.NSMode = FromPod
   177  	case ns == "host":
   178  		toReturn.NSMode = Host
   179  	case ns == "private", ns == "":
   180  		toReturn.NSMode = Private
   181  	case strings.HasPrefix(ns, "ns:"):
   182  		split := strings.SplitN(ns, ":", 2)
   183  		if len(split) != 2 {
   184  			return toReturn, errors.Errorf("must provide a path to a namespace when specifying ns:")
   185  		}
   186  		toReturn.NSMode = Path
   187  		toReturn.Value = split[1]
   188  	case strings.HasPrefix(ns, "container:"):
   189  		split := strings.SplitN(ns, ":", 2)
   190  		if len(split) != 2 {
   191  			return toReturn, errors.Errorf("must provide name or ID or a container when specifying container:")
   192  		}
   193  		toReturn.NSMode = FromContainer
   194  		toReturn.Value = split[1]
   195  	default:
   196  		return toReturn, errors.Errorf("unrecognized namespace mode %s passed", ns)
   197  	}
   198  
   199  	return toReturn, nil
   200  }
   201  
   202  // ParseCgroupNamespace parses a cgroup namespace specification in string
   203  // form.
   204  func ParseCgroupNamespace(ns string) (Namespace, error) {
   205  	toReturn := Namespace{}
   206  	// Cgroup is host for v1, private for v2.
   207  	// We can't trust c/common for this, as it only assumes private.
   208  	cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
   209  	if err != nil {
   210  		return toReturn, err
   211  	}
   212  	if cgroupsv2 {
   213  		switch ns {
   214  		case "host":
   215  			toReturn.NSMode = Host
   216  		case "private", "":
   217  			toReturn.NSMode = Private
   218  		default:
   219  			return toReturn, errors.Errorf("unrecognized namespace mode %s passed", ns)
   220  		}
   221  	} else {
   222  		toReturn.NSMode = Host
   223  	}
   224  	return toReturn, nil
   225  }
   226  
   227  // ParseUserNamespace parses a user namespace specification in string
   228  // form.
   229  func ParseUserNamespace(ns string) (Namespace, error) {
   230  	toReturn := Namespace{}
   231  	switch {
   232  	case ns == "auto":
   233  		toReturn.NSMode = Auto
   234  		return toReturn, nil
   235  	case strings.HasPrefix(ns, "auto:"):
   236  		split := strings.SplitN(ns, ":", 2)
   237  		if len(split) != 2 {
   238  			return toReturn, errors.Errorf("invalid setting for auto: mode")
   239  		}
   240  		toReturn.NSMode = Auto
   241  		toReturn.Value = split[1]
   242  		return toReturn, nil
   243  	case ns == "keep-id":
   244  		toReturn.NSMode = KeepID
   245  		return toReturn, nil
   246  	case ns == "":
   247  		toReturn.NSMode = Host
   248  		return toReturn, nil
   249  	}
   250  	return ParseNamespace(ns)
   251  }
   252  
   253  // ParseNetworkNamespace parses a network namespace specification in string
   254  // form.
   255  // Returns a namespace and (optionally) a list of CNI networks to join.
   256  func ParseNetworkNamespace(ns string) (Namespace, []string, error) {
   257  	toReturn := Namespace{}
   258  	var cniNetworks []string
   259  	// Net defaults to Slirp on rootless
   260  	switch {
   261  	case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
   262  		toReturn.NSMode = Slirp
   263  	case ns == string(FromPod):
   264  		toReturn.NSMode = FromPod
   265  	case ns == "" || ns == string(Default) || ns == string(Private):
   266  		if rootless.IsRootless() {
   267  			toReturn.NSMode = Slirp
   268  		} else {
   269  			toReturn.NSMode = Bridge
   270  		}
   271  	case ns == string(Bridge):
   272  		toReturn.NSMode = Bridge
   273  	case ns == string(NoNetwork):
   274  		toReturn.NSMode = NoNetwork
   275  	case ns == string(Host):
   276  		toReturn.NSMode = Host
   277  	case strings.HasPrefix(ns, "ns:"):
   278  		split := strings.SplitN(ns, ":", 2)
   279  		if len(split) != 2 {
   280  			return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying ns:")
   281  		}
   282  		toReturn.NSMode = Path
   283  		toReturn.Value = split[1]
   284  	case strings.HasPrefix(ns, string(FromContainer)+":"):
   285  		split := strings.SplitN(ns, ":", 2)
   286  		if len(split) != 2 {
   287  			return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:")
   288  		}
   289  		toReturn.NSMode = FromContainer
   290  		toReturn.Value = split[1]
   291  	default:
   292  		// Assume we have been given a list of CNI networks.
   293  		// Which only works in bridge mode, so set that.
   294  		cniNetworks = strings.Split(ns, ",")
   295  		toReturn.NSMode = Bridge
   296  	}
   297  
   298  	return toReturn, cniNetworks, nil
   299  }