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 }