github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/namespaces.go (about) 1 package generate 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/containers/common/pkg/config" 10 "github.com/containers/podman/v2/libpod" 11 "github.com/containers/podman/v2/libpod/define" 12 "github.com/containers/podman/v2/libpod/image" 13 "github.com/containers/podman/v2/pkg/rootless" 14 "github.com/containers/podman/v2/pkg/specgen" 15 "github.com/containers/podman/v2/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 == "cgroup" && pod.SharesCgroup(): 46 podMode = true 47 } 48 if podMode { 49 toReturn.NSMode = specgen.FromPod 50 return toReturn, nil 51 } 52 } 53 54 if cfg == nil { 55 cfg = &config.Config{} 56 } 57 switch nsType { 58 case "pid": 59 return specgen.ParseNamespace(cfg.Containers.PidNS) 60 case "ipc": 61 return specgen.ParseNamespace(cfg.Containers.IPCNS) 62 case "uts": 63 return specgen.ParseNamespace(cfg.Containers.UTSNS) 64 case "user": 65 return specgen.ParseUserNamespace(cfg.Containers.UserNS) 66 case "cgroup": 67 return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) 68 case "net": 69 ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS) 70 return ns, err 71 } 72 73 return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType) 74 } 75 76 // namespaceOptions generates container creation options for all 77 // namespaces in a SpecGenerator. 78 // Pod is the pod the container will join. May be nil is the container is not 79 // joining a pod. 80 // TODO: Consider grouping options that are not directly attached to a namespace 81 // elsewhere. 82 func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, img *image.Image) ([]libpod.CtrCreateOption, error) { 83 toReturn := []libpod.CtrCreateOption{} 84 85 // If pod is not nil, get infra container. 86 var infraCtr *libpod.Container 87 if pod != nil { 88 infraID, err := pod.InfraContainerID() 89 if err != nil { 90 // This is likely to be of the fatal kind (pod was 91 // removed) so hard fail 92 return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID()) 93 } 94 if infraID != "" { 95 ctr, err := rt.GetContainer(infraID) 96 if err != nil { 97 return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID) 98 } 99 infraCtr = ctr 100 } 101 } 102 103 errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container") 104 105 // PID 106 switch s.PidNS.NSMode { 107 case specgen.FromPod: 108 if pod == nil || infraCtr == nil { 109 return nil, errNoInfra 110 } 111 toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr)) 112 case specgen.FromContainer: 113 pidCtr, err := rt.LookupContainer(s.PidNS.Value) 114 if err != nil { 115 return nil, errors.Wrapf(err, "error looking up container to share pid namespace with") 116 } 117 toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) 118 } 119 120 // IPC 121 switch s.IpcNS.NSMode { 122 case specgen.Host: 123 // Force use of host /dev/shm for host namespace 124 toReturn = append(toReturn, libpod.WithShmDir("/dev/shm")) 125 case specgen.FromPod: 126 if pod == nil || infraCtr == nil { 127 return nil, errNoInfra 128 } 129 toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr)) 130 toReturn = append(toReturn, libpod.WithShmDir(infraCtr.ShmDir())) 131 case specgen.FromContainer: 132 ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) 133 if err != nil { 134 return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with") 135 } 136 toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr)) 137 toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir())) 138 } 139 140 // UTS 141 switch s.UtsNS.NSMode { 142 case specgen.FromPod: 143 if pod == nil || infraCtr == nil { 144 return nil, errNoInfra 145 } 146 toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) 147 case specgen.FromContainer: 148 utsCtr, err := rt.LookupContainer(s.UtsNS.Value) 149 if err != nil { 150 return nil, errors.Wrapf(err, "error looking up container to share uts namespace with") 151 } 152 toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) 153 } 154 155 // User 156 switch s.UserNS.NSMode { 157 case specgen.KeepID: 158 if rootless.IsRootless() { 159 toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) 160 } else { 161 // keep-id as root doesn't need a user namespace 162 s.UserNS.NSMode = specgen.Host 163 } 164 case specgen.FromPod: 165 if pod == nil || infraCtr == nil { 166 return nil, errNoInfra 167 } 168 toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr)) 169 case specgen.FromContainer: 170 userCtr, err := rt.LookupContainer(s.UserNS.Value) 171 if err != nil { 172 return nil, errors.Wrapf(err, "error looking up container to share user namespace with") 173 } 174 toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) 175 } 176 177 if s.IDMappings != nil { 178 toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) 179 } 180 if s.User != "" { 181 toReturn = append(toReturn, libpod.WithUser(s.User)) 182 } 183 if len(s.Groups) > 0 { 184 toReturn = append(toReturn, libpod.WithGroups(s.Groups)) 185 } 186 187 // Cgroup 188 switch s.CgroupNS.NSMode { 189 case specgen.FromPod: 190 if pod == nil || infraCtr == nil { 191 return nil, errNoInfra 192 } 193 toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr)) 194 case specgen.FromContainer: 195 cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) 196 if err != nil { 197 return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with") 198 } 199 toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) 200 } 201 202 if s.CgroupParent != "" { 203 toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent)) 204 } 205 206 if s.CgroupsMode != "" { 207 toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode)) 208 } 209 210 // Net 211 // TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we 212 // are in bridge mode. 213 postConfigureNetNS := !s.UserNS.IsHost() 214 switch s.NetNS.NSMode { 215 case specgen.FromPod: 216 if pod == nil || infraCtr == nil { 217 return nil, errNoInfra 218 } 219 toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr)) 220 case specgen.FromContainer: 221 netCtr, err := rt.LookupContainer(s.NetNS.Value) 222 if err != nil { 223 return nil, errors.Wrapf(err, "error looking up container to share net namespace with") 224 } 225 toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) 226 case specgen.Slirp: 227 portMappings, err := createPortMappings(ctx, s, img) 228 if err != nil { 229 return nil, err 230 } 231 val := "slirp4netns" 232 if s.NetNS.Value != "" { 233 val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) 234 } 235 toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil)) 236 case specgen.Private: 237 fallthrough 238 case specgen.Bridge: 239 portMappings, err := createPortMappings(ctx, s, img) 240 if err != nil { 241 return nil, err 242 } 243 toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "bridge", s.CNINetworks)) 244 } 245 246 if s.UseImageHosts { 247 toReturn = append(toReturn, libpod.WithUseImageHosts()) 248 } else if len(s.HostAdd) > 0 { 249 toReturn = append(toReturn, libpod.WithHosts(s.HostAdd)) 250 } 251 if len(s.DNSSearch) > 0 { 252 toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch)) 253 } 254 if s.UseImageResolvConf { 255 toReturn = append(toReturn, libpod.WithUseImageResolvConf()) 256 } else if len(s.DNSServers) > 0 { 257 var dnsServers []string 258 for _, d := range s.DNSServers { 259 dnsServers = append(dnsServers, d.String()) 260 } 261 toReturn = append(toReturn, libpod.WithDNS(dnsServers)) 262 } 263 if len(s.DNSOptions) > 0 { 264 toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions)) 265 } 266 if s.StaticIP != nil { 267 toReturn = append(toReturn, libpod.WithStaticIP(*s.StaticIP)) 268 } 269 if s.StaticMAC != nil { 270 toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC)) 271 } 272 if s.NetworkOptions != nil { 273 toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions)) 274 } 275 276 return toReturn, nil 277 } 278 279 func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { 280 // PID 281 switch s.PidNS.NSMode { 282 case specgen.Path: 283 if _, err := os.Stat(s.PidNS.Value); err != nil { 284 return errors.Wrap(err, "cannot find specified PID namespace path") 285 } 286 if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { 287 return err 288 } 289 case specgen.Host: 290 if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { 291 return err 292 } 293 case specgen.Private: 294 if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { 295 return err 296 } 297 } 298 299 // IPC 300 switch s.IpcNS.NSMode { 301 case specgen.Path: 302 if _, err := os.Stat(s.IpcNS.Value); err != nil { 303 return errors.Wrap(err, "cannot find specified IPC namespace path") 304 } 305 if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { 306 return err 307 } 308 case specgen.Host: 309 if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { 310 return err 311 } 312 case specgen.Private: 313 if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { 314 return err 315 } 316 } 317 318 // UTS 319 switch s.UtsNS.NSMode { 320 case specgen.Path: 321 if _, err := os.Stat(s.UtsNS.Value); err != nil { 322 return errors.Wrap(err, "cannot find specified UTS namespace path") 323 } 324 if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { 325 return err 326 } 327 case specgen.Host: 328 if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { 329 return err 330 } 331 case specgen.Private: 332 if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { 333 return err 334 } 335 } 336 337 hostname := s.Hostname 338 if hostname == "" { 339 switch { 340 case s.UtsNS.NSMode == specgen.FromPod: 341 hostname = pod.Hostname() 342 case s.UtsNS.NSMode == specgen.FromContainer: 343 utsCtr, err := rt.LookupContainer(s.UtsNS.Value) 344 if err != nil { 345 return errors.Wrapf(err, "error looking up container to share uts namespace with") 346 } 347 hostname = utsCtr.Hostname() 348 case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: 349 tmpHostname, err := os.Hostname() 350 if err != nil { 351 return errors.Wrap(err, "unable to retrieve hostname of the host") 352 } 353 hostname = tmpHostname 354 default: 355 logrus.Debug("No hostname set; container's hostname will default to runtime default") 356 } 357 } 358 359 g.RemoveHostname() 360 if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { 361 // Set the hostname in the OCI configuration only if specified by 362 // the user or if we are creating a new UTS namespace. 363 // TODO: Should we be doing this for pod or container shared 364 // namespaces? 365 g.SetHostname(hostname) 366 } 367 g.AddProcessEnv("HOSTNAME", hostname) 368 369 // User 370 switch s.UserNS.NSMode { 371 case specgen.Path: 372 if _, err := os.Stat(s.UserNS.Value); err != nil { 373 return errors.Wrap(err, "cannot find specified user namespace path") 374 } 375 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil { 376 return err 377 } 378 // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping 379 g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) 380 g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) 381 case specgen.Host: 382 if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil { 383 return err 384 } 385 case specgen.KeepID: 386 var ( 387 err error 388 uid, gid int 389 ) 390 s.IDMappings, uid, gid, err = util.GetKeepIDMapping() 391 if err != nil { 392 return err 393 } 394 g.SetProcessUID(uint32(uid)) 395 g.SetProcessGID(uint32(gid)) 396 fallthrough 397 case specgen.Private: 398 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { 399 return err 400 } 401 if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) { 402 return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace") 403 } 404 for _, uidmap := range s.IDMappings.UIDMap { 405 g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) 406 } 407 for _, gidmap := range s.IDMappings.GIDMap { 408 g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) 409 } 410 } 411 412 // Cgroup 413 switch s.CgroupNS.NSMode { 414 case specgen.Path: 415 if _, err := os.Stat(s.CgroupNS.Value); err != nil { 416 return errors.Wrap(err, "cannot find specified cgroup namespace path") 417 } 418 if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { 419 return err 420 } 421 case specgen.Host: 422 if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { 423 return err 424 } 425 case specgen.Private: 426 if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { 427 return err 428 } 429 } 430 431 // Net 432 switch s.NetNS.NSMode { 433 case specgen.Path: 434 if _, err := os.Stat(s.NetNS.Value); err != nil { 435 return errors.Wrap(err, "cannot find specified network namespace path") 436 } 437 if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { 438 return err 439 } 440 case specgen.Host: 441 if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { 442 return err 443 } 444 case specgen.Private, specgen.NoNetwork: 445 if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { 446 return err 447 } 448 } 449 450 if g.Config.Annotations == nil { 451 g.Config.Annotations = make(map[string]string) 452 } 453 if s.PublishExposedPorts { 454 g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue 455 } else { 456 g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse 457 } 458 459 return nil 460 } 461 462 // GetNamespaceOptions transforms a slice of kernel namespaces 463 // into a slice of pod create options. Currently, not all 464 // kernel namespaces are supported, and they will be returned in an error 465 func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { 466 var options []libpod.PodCreateOption 467 var erroredOptions []libpod.PodCreateOption 468 if ns == nil { 469 //set the default namespaces 470 ns = strings.Split(specgen.DefaultKernelNamespaces, ",") 471 } 472 for _, toShare := range ns { 473 switch toShare { 474 case "cgroup": 475 options = append(options, libpod.WithPodCgroups()) 476 case "net": 477 options = append(options, libpod.WithPodNet()) 478 case "mnt": 479 return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") 480 case "pid": 481 options = append(options, libpod.WithPodPID()) 482 case "user": 483 continue 484 case "ipc": 485 options = append(options, libpod.WithPodIPC()) 486 case "uts": 487 options = append(options, libpod.WithPodUTS()) 488 case "": 489 case "none": 490 return erroredOptions, nil 491 default: 492 return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare) 493 } 494 } 495 return options, nil 496 }