github.com/containers/podman/v4@v4.9.4/pkg/specgen/generate/namespaces.go (about) 1 //go:build !remote 2 // +build !remote 3 4 package generate 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/containers/common/libimage" 11 "github.com/containers/common/libnetwork/types" 12 "github.com/containers/common/pkg/config" 13 "github.com/containers/podman/v4/libpod" 14 "github.com/containers/podman/v4/libpod/define" 15 "github.com/containers/podman/v4/pkg/namespaces" 16 "github.com/containers/podman/v4/pkg/rootless" 17 "github.com/containers/podman/v4/pkg/specgen" 18 "github.com/containers/podman/v4/pkg/util" 19 spec "github.com/opencontainers/runtime-spec/specs-go" 20 "github.com/sirupsen/logrus" 21 ) 22 23 const host = "host" 24 25 // Get the default namespace mode for any given namespace type. 26 func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) { 27 // The default for most is private 28 toReturn := specgen.Namespace{} 29 toReturn.NSMode = specgen.Private 30 31 // Ensure case insensitivity 32 nsType = strings.ToLower(nsType) 33 34 // If the pod is not nil - check shared namespaces 35 if pod != nil && pod.HasInfraContainer() { 36 podMode := false 37 switch { 38 case nsType == "pid" && pod.SharesPID(): 39 if pod.NamespaceMode(spec.PIDNamespace) == host { 40 toReturn.NSMode = specgen.Host 41 return toReturn, nil 42 } 43 podMode = true 44 case nsType == "ipc" && pod.SharesIPC(): 45 if pod.NamespaceMode(spec.IPCNamespace) == host { 46 toReturn.NSMode = specgen.Host 47 return toReturn, nil 48 } 49 podMode = true 50 case nsType == "uts" && pod.SharesUTS(): 51 if pod.NamespaceMode(spec.UTSNamespace) == host { 52 toReturn.NSMode = specgen.Host 53 return toReturn, nil 54 } 55 podMode = true 56 case nsType == "user" && pod.SharesUser(): 57 // user does not need a special check for host, this is already validated on pod creation 58 // if --userns=host then pod.SharesUser == false 59 podMode = true 60 case nsType == "net" && pod.SharesNet(): 61 if pod.NetworkMode() == host { 62 toReturn.NSMode = specgen.Host 63 return toReturn, nil 64 } 65 podMode = true 66 case nsType == "cgroup" && pod.SharesCgroup(): 67 if pod.NamespaceMode(spec.CgroupNamespace) == host { 68 toReturn.NSMode = specgen.Host 69 return toReturn, nil 70 } 71 podMode = true 72 } 73 if podMode { 74 toReturn.NSMode = specgen.FromPod 75 return toReturn, nil 76 } 77 } 78 79 if cfg == nil { 80 cfg = &config.Config{} 81 } 82 switch nsType { 83 case "pid": 84 return specgen.ParseNamespace(cfg.Containers.PidNS) 85 case "ipc": 86 return specgen.ParseIPCNamespace(cfg.Containers.IPCNS) 87 case "uts": 88 return specgen.ParseNamespace(cfg.Containers.UTSNS) 89 case "user": 90 return specgen.ParseUserNamespace(cfg.Containers.UserNS) 91 case "cgroup": 92 return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) 93 case "net": 94 ns, _, _, err := specgen.ParseNetworkFlag(nil, false) 95 return ns, err 96 } 97 98 return toReturn, fmt.Errorf("invalid namespace type %q passed: %w", nsType, define.ErrInvalidArg) 99 } 100 101 // namespaceOptions generates container creation options for all 102 // namespaces in a SpecGenerator. 103 // Pod is the pod the container will join. May be nil is the container is not 104 // joining a pod. 105 // TODO: Consider grouping options that are not directly attached to a namespace 106 // elsewhere. 107 func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, imageData *libimage.ImageData) ([]libpod.CtrCreateOption, error) { 108 toReturn := []libpod.CtrCreateOption{} 109 110 // If pod is not nil, get infra container. 111 var infraCtr *libpod.Container 112 if pod != nil { 113 infraID, err := pod.InfraContainerID() 114 if err != nil { 115 // This is likely to be of the fatal kind (pod was 116 // removed) so hard fail 117 return nil, fmt.Errorf("looking up pod %s infra container: %w", pod.ID(), err) 118 } 119 if infraID != "" { 120 ctr, err := rt.GetContainer(infraID) 121 if err != nil { 122 return nil, fmt.Errorf("retrieving pod %s infra container %s: %w", pod.ID(), infraID, err) 123 } 124 infraCtr = ctr 125 } 126 } 127 128 errNoInfra := fmt.Errorf("cannot use pod namespace as container is not joining a pod or pod has no infra container: %w", define.ErrInvalidArg) 129 130 // PID 131 switch s.PidNS.NSMode { 132 case specgen.FromPod: 133 if pod == nil || infraCtr == nil { 134 return nil, errNoInfra 135 } 136 toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr)) 137 case specgen.FromContainer: 138 pidCtr, err := rt.LookupContainer(s.PidNS.Value) 139 if err != nil { 140 return nil, fmt.Errorf("looking up container to share pid namespace with: %w", err) 141 } 142 if rootless.IsRootless() && pidCtr.NamespaceMode(spec.PIDNamespace, pidCtr.ConfigNoCopy().Spec) == host { 143 // Treat this the same as host, the problem is the runtime tries to do a 144 // setns call and this will fail when it is the host ns as rootless user. 145 s.PidNS.NSMode = specgen.Host 146 } else { 147 toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) 148 } 149 } 150 151 // IPC 152 switch s.IpcNS.NSMode { 153 case specgen.Host: 154 // Force use of host /dev/shm for host namespace 155 toReturn = append(toReturn, libpod.WithShmDir("/dev/shm")) 156 case specgen.FromPod: 157 if pod == nil || infraCtr == nil { 158 return nil, errNoInfra 159 } 160 toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr)) 161 toReturn = append(toReturn, libpod.WithShmDir(infraCtr.ShmDir())) 162 case specgen.FromContainer: 163 ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) 164 if err != nil { 165 return nil, fmt.Errorf("looking up container to share ipc namespace with: %w", err) 166 } 167 if ipcCtr.ConfigNoCopy().NoShmShare { 168 return nil, fmt.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) 169 } 170 if rootless.IsRootless() && ipcCtr.NamespaceMode(spec.IPCNamespace, ipcCtr.ConfigNoCopy().Spec) == host { 171 // Treat this the same as host, the problem is the runtime tries to do a 172 // setns call and this will fail when it is the host ns as rootless user. 173 s.IpcNS.NSMode = specgen.Host 174 toReturn = append(toReturn, libpod.WithShmDir("/dev/shm")) 175 } else { 176 toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr)) 177 if !ipcCtr.ConfigNoCopy().NoShm { 178 toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir())) 179 } 180 } 181 case specgen.None: 182 toReturn = append(toReturn, libpod.WithNoShm(true)) 183 case specgen.Private: 184 toReturn = append(toReturn, libpod.WithNoShmShare(true)) 185 } 186 187 // UTS 188 switch s.UtsNS.NSMode { 189 case specgen.FromPod: 190 if pod == nil || infraCtr == nil { 191 return nil, errNoInfra 192 } 193 if pod.NamespaceMode(spec.UTSNamespace) == host { 194 // adding infra as a nsCtr is not what we want to do when uts == host 195 // this leads the new ctr to try to add an ns path which is should not in this mode 196 logrus.Debug("pod has host uts, not adding infra as a nsCtr") 197 s.UtsNS = specgen.Namespace{NSMode: specgen.Host} 198 } else { 199 toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) 200 } 201 case specgen.FromContainer: 202 utsCtr, err := rt.LookupContainer(s.UtsNS.Value) 203 if err != nil { 204 return nil, fmt.Errorf("looking up container to share uts namespace with: %w", err) 205 } 206 if rootless.IsRootless() && utsCtr.NamespaceMode(spec.UTSNamespace, utsCtr.ConfigNoCopy().Spec) == host { 207 // Treat this the same as host, the problem is the runtime tries to do a 208 // setns call and this will fail when it is the host ns as rootless user. 209 s.UtsNS.NSMode = specgen.Host 210 } else { 211 toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) 212 } 213 } 214 215 // User 216 switch s.UserNS.NSMode { 217 case specgen.KeepID: 218 opts, err := namespaces.UsernsMode(s.UserNS.String()).GetKeepIDOptions() 219 if err != nil { 220 return nil, err 221 } 222 if opts.UID == nil && opts.GID == nil { 223 toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) 224 } 225 226 // If user is not overridden, set user in the container 227 // to user running Podman. 228 if s.User == "" { 229 _, uid, gid, err := util.GetKeepIDMapping(opts) 230 if err != nil { 231 return nil, err 232 } 233 toReturn = append(toReturn, libpod.WithUser(fmt.Sprintf("%d:%d", uid, gid))) 234 } 235 case specgen.FromPod: 236 if pod == nil || infraCtr == nil { 237 return nil, errNoInfra 238 } 239 // Inherit the user from the infra container if it is set and --user has not 240 // been set explicitly 241 if infraCtr.User() != "" && s.User == "" { 242 toReturn = append(toReturn, libpod.WithUser(infraCtr.User())) 243 } 244 toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr)) 245 case specgen.FromContainer: 246 userCtr, err := rt.LookupContainer(s.UserNS.Value) 247 if err != nil { 248 return nil, fmt.Errorf("looking up container to share user namespace with: %w", err) 249 } 250 toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) 251 } 252 253 // This wipes the UserNS settings that get set from the infra container 254 // when we are inheriting from the pod. So only apply this if the container 255 // is not being created in a pod. 256 if s.IDMappings != nil { 257 if pod == nil { 258 toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) 259 } else if pod.HasInfraContainer() && (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) { 260 return nil, fmt.Errorf("cannot specify a new uid/gid map when entering a pod with an infra container: %w", define.ErrInvalidArg) 261 } 262 } 263 if s.User != "" { 264 toReturn = append(toReturn, libpod.WithUser(s.User)) 265 } 266 if len(s.Groups) > 0 { 267 toReturn = append(toReturn, libpod.WithGroups(s.Groups)) 268 } 269 270 // Cgroup 271 switch s.CgroupNS.NSMode { 272 case specgen.FromPod: 273 if pod == nil || infraCtr == nil { 274 return nil, errNoInfra 275 } 276 toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr)) 277 case specgen.FromContainer: 278 cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) 279 if err != nil { 280 return nil, fmt.Errorf("looking up container to share cgroup namespace with: %w", err) 281 } 282 if rootless.IsRootless() && cgroupCtr.NamespaceMode(spec.CgroupNamespace, cgroupCtr.ConfigNoCopy().Spec) == host { 283 // Treat this the same as host, the problem is the runtime tries to do a 284 // setns call and this will fail when it is the host ns as rootless user. 285 s.CgroupNS.NSMode = specgen.Host 286 } else { 287 toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) 288 } 289 } 290 291 if s.CgroupParent != "" { 292 toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent)) 293 } 294 295 if s.CgroupsMode != "" { 296 toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode)) 297 } 298 299 postConfigureNetNS := !s.UserNS.IsHost() 300 301 switch s.NetNS.NSMode { 302 case specgen.FromPod: 303 if pod == nil || infraCtr == nil { 304 return nil, errNoInfra 305 } 306 toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr)) 307 case specgen.FromContainer: 308 netCtr, err := rt.LookupContainer(s.NetNS.Value) 309 if err != nil { 310 return nil, fmt.Errorf("looking up container to share net namespace with: %w", err) 311 } 312 if rootless.IsRootless() && netCtr.NamespaceMode(spec.NetworkNamespace, netCtr.ConfigNoCopy().Spec) == host { 313 // Treat this the same as host, the problem is the runtime tries to do a 314 // setns call and this will fail when it is the host ns as rootless user. 315 s.NetNS.NSMode = specgen.Host 316 } else { 317 toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) 318 } 319 case specgen.Slirp: 320 portMappings, expose, err := createPortMappings(s, imageData) 321 if err != nil { 322 return nil, err 323 } 324 val := "slirp4netns" 325 if s.NetNS.Value != "" { 326 val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) 327 } 328 toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) 329 case specgen.Pasta: 330 portMappings, expose, err := createPortMappings(s, imageData) 331 if err != nil { 332 return nil, err 333 } 334 val := "pasta" 335 toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) 336 case specgen.Bridge, specgen.Private, specgen.Default: 337 portMappings, expose, err := createPortMappings(s, imageData) 338 if err != nil { 339 return nil, err 340 } 341 342 rtConfig, err := rt.GetConfigNoCopy() 343 if err != nil { 344 return nil, err 345 } 346 // if no network was specified use add the default 347 if len(s.Networks) == 0 { 348 // backwards config still allow the old cni networks list and convert to new format 349 if len(s.CNINetworks) > 0 { 350 logrus.Warn(`specgen "cni_networks" option is deprecated use the "networks" map instead`) 351 networks := make(map[string]types.PerNetworkOptions, len(s.CNINetworks)) 352 for _, net := range s.CNINetworks { 353 networks[net] = types.PerNetworkOptions{} 354 } 355 s.Networks = networks 356 } else { 357 // no networks given but bridge is set so use default network 358 s.Networks = map[string]types.PerNetworkOptions{ 359 rtConfig.Network.DefaultNetwork: {}, 360 } 361 } 362 } 363 // rename the "default" network to the correct default name 364 if opts, ok := s.Networks["default"]; ok { 365 s.Networks[rtConfig.Network.DefaultNetwork] = opts 366 delete(s.Networks, "default") 367 } 368 toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.Networks)) 369 } 370 371 if s.UseImageHosts { 372 toReturn = append(toReturn, libpod.WithUseImageHosts()) 373 } else if len(s.HostAdd) > 0 { 374 toReturn = append(toReturn, libpod.WithHosts(s.HostAdd)) 375 } 376 if len(s.DNSSearch) > 0 { 377 toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch)) 378 } 379 if s.UseImageResolvConf { 380 toReturn = append(toReturn, libpod.WithUseImageResolvConf()) 381 } else if len(s.DNSServers) > 0 { 382 var dnsServers []string 383 for _, d := range s.DNSServers { 384 dnsServers = append(dnsServers, d.String()) 385 } 386 toReturn = append(toReturn, libpod.WithDNS(dnsServers)) 387 } 388 if len(s.DNSOptions) > 0 { 389 toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions)) 390 } 391 if s.NetworkOptions != nil { 392 toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions)) 393 } 394 395 return toReturn, nil 396 } 397 398 // GetNamespaceOptions transforms a slice of kernel namespaces 399 // into a slice of pod create options. Currently, not all 400 // kernel namespaces are supported, and they will be returned in an error 401 func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOption, error) { 402 var options []libpod.PodCreateOption 403 var erroredOptions []libpod.PodCreateOption 404 if ns == nil { 405 // set the default namespaces 406 ns = strings.Split(specgen.DefaultKernelNamespaces, ",") 407 } 408 for _, toShare := range ns { 409 switch toShare { 410 case "cgroup": 411 options = append(options, libpod.WithPodCgroup()) 412 case "net": 413 options = append(options, libpod.WithPodNet()) 414 case "mnt": 415 return erroredOptions, fmt.Errorf("mount sharing functionality not supported on pod level") 416 case "pid": 417 options = append(options, libpod.WithPodPID()) 418 case "user": 419 continue 420 case "ipc": 421 options = append(options, libpod.WithPodIPC()) 422 case "uts": 423 options = append(options, libpod.WithPodUTS()) 424 case "": 425 case "none": 426 return erroredOptions, nil 427 default: 428 return erroredOptions, fmt.Errorf("invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare) 429 } 430 } 431 return options, nil 432 }