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