github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/spec/namespaces.go (about) 1 package createconfig 2 3 import ( 4 "net" 5 "os" 6 "strconv" 7 "strings" 8 9 "github.com/containers/podman/v2/libpod" 10 "github.com/containers/podman/v2/libpod/define" 11 "github.com/containers/podman/v2/pkg/cgroups" 12 "github.com/cri-o/ocicni/pkg/ocicni" 13 "github.com/docker/go-connections/nat" 14 spec "github.com/opencontainers/runtime-spec/specs-go" 15 "github.com/opencontainers/runtime-tools/generate" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 // DefaultKernelNamespaces is a comma-separated list of default kernel 21 // namespaces. 22 const DefaultKernelNamespaces = "cgroup,ipc,net,uts" 23 24 // ToCreateOptions converts the input to a slice of container create options. 25 func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) { 26 var portBindings []ocicni.PortMapping 27 var err error 28 if len(c.PortBindings) > 0 { 29 portBindings, err = NatToOCIPortBindings(c.PortBindings) 30 if err != nil { 31 return nil, errors.Wrapf(err, "unable to create port bindings") 32 } 33 } 34 35 options := make([]libpod.CtrCreateOption, 0) 36 userNetworks := c.NetMode.UserDefined() 37 networks := make([]string, 0) 38 39 if IsPod(userNetworks) { 40 userNetworks = "" 41 } 42 if userNetworks != "" { 43 for _, netName := range strings.Split(userNetworks, ",") { 44 if netName == "" { 45 return nil, errors.Errorf("container networks %q invalid", userNetworks) 46 } 47 networks = append(networks, netName) 48 } 49 } 50 51 switch { 52 case c.NetMode.IsNS(): 53 ns := c.NetMode.NS() 54 if ns == "" { 55 return nil, errors.Errorf("invalid empty user-defined network namespace") 56 } 57 _, err := os.Stat(ns) 58 if err != nil { 59 return nil, err 60 } 61 case c.NetMode.IsContainer(): 62 connectedCtr, err := runtime.LookupContainer(c.NetMode.Container()) 63 if err != nil { 64 return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container()) 65 } 66 options = append(options, libpod.WithNetNSFrom(connectedCtr)) 67 case !c.NetMode.IsHost() && !c.NetMode.IsNone(): 68 postConfigureNetNS := userns.getPostConfigureNetNS() 69 options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks)) 70 } 71 72 if len(c.DNSSearch) > 0 { 73 options = append(options, libpod.WithDNSSearch(c.DNSSearch)) 74 } 75 if len(c.DNSServers) > 0 { 76 if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" { 77 options = append(options, libpod.WithUseImageResolvConf()) 78 } else { 79 options = append(options, libpod.WithDNS(c.DNSServers)) 80 } 81 } 82 if len(c.DNSOpt) > 0 { 83 options = append(options, libpod.WithDNSOption(c.DNSOpt)) 84 } 85 if c.IPAddress != "" { 86 ip := net.ParseIP(c.IPAddress) 87 if ip == nil { 88 return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress) 89 } else if ip.To4() == nil { 90 return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress) 91 } 92 options = append(options, libpod.WithStaticIP(ip)) 93 } 94 95 if c.MacAddress != "" { 96 mac, err := net.ParseMAC(c.MacAddress) 97 if err != nil { 98 return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err) 99 } 100 options = append(options, libpod.WithStaticMAC(mac)) 101 } 102 103 return options, nil 104 } 105 106 // ConfigureGenerator configures the generator based according to the current 107 // state of the NetworkConfig. 108 func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error { 109 netMode := c.NetMode 110 netCtr := netMode.Container() 111 switch { 112 case netMode.IsHost(): 113 logrus.Debug("Using host netmode") 114 if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { 115 return err 116 } 117 case netMode.IsNone(): 118 logrus.Debug("Using none netmode") 119 case netMode.IsBridge(): 120 logrus.Debug("Using bridge netmode") 121 case netCtr != "": 122 logrus.Debugf("using container %s netmode", netCtr) 123 case IsNS(string(netMode)): 124 logrus.Debug("Using ns netmode") 125 if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil { 126 return err 127 } 128 case IsPod(string(netMode)): 129 logrus.Debug("Using pod netmode, unless pod is not sharing") 130 case netMode.IsSlirp4netns(): 131 logrus.Debug("Using slirp4netns netmode") 132 case netMode.IsUserDefined(): 133 logrus.Debug("Using user defined netmode") 134 default: 135 return errors.Errorf("unknown network mode") 136 } 137 138 if c.HTTPProxy { 139 for _, envSpec := range []string{ 140 "http_proxy", 141 "HTTP_PROXY", 142 "https_proxy", 143 "HTTPS_PROXY", 144 "ftp_proxy", 145 "FTP_PROXY", 146 "no_proxy", 147 "NO_PROXY", 148 } { 149 envVal := os.Getenv(envSpec) 150 if envVal != "" { 151 g.AddProcessEnv(envSpec, envVal) 152 } 153 } 154 } 155 156 if g.Config.Annotations == nil { 157 g.Config.Annotations = make(map[string]string) 158 } 159 160 if c.PublishAll { 161 g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue 162 } else { 163 g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse 164 } 165 166 return nil 167 } 168 169 // NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice 170 func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) { 171 var portBindings []ocicni.PortMapping 172 for containerPb, hostPb := range ports { 173 var pm ocicni.PortMapping 174 pm.ContainerPort = int32(containerPb.Int()) 175 for _, i := range hostPb { 176 var hostPort int 177 var err error 178 pm.HostIP = i.HostIP 179 if i.HostPort == "" { 180 hostPort = containerPb.Int() 181 } else { 182 hostPort, err = strconv.Atoi(i.HostPort) 183 if err != nil { 184 return nil, errors.Wrapf(err, "unable to convert host port to integer") 185 } 186 } 187 188 pm.HostPort = int32(hostPort) 189 pm.Protocol = containerPb.Proto() 190 portBindings = append(portBindings, pm) 191 } 192 } 193 return portBindings, nil 194 } 195 196 // ToCreateOptions converts the input to container create options. 197 func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { 198 options := make([]libpod.CtrCreateOption, 0) 199 if c.CgroupMode.IsNS() { 200 ns := c.CgroupMode.NS() 201 if ns == "" { 202 return nil, errors.Errorf("invalid empty user-defined network namespace") 203 } 204 _, err := os.Stat(ns) 205 if err != nil { 206 return nil, err 207 } 208 } else if c.CgroupMode.IsContainer() { 209 connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container()) 210 if err != nil { 211 return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container()) 212 } 213 options = append(options, libpod.WithCgroupNSFrom(connectedCtr)) 214 } 215 216 if c.CgroupParent != "" { 217 options = append(options, libpod.WithCgroupParent(c.CgroupParent)) 218 } 219 220 if c.Cgroups != "" { 221 options = append(options, libpod.WithCgroupsMode(c.Cgroups)) 222 } 223 224 return options, nil 225 } 226 227 // ToCreateOptions converts the input to container create options. 228 func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { 229 options := make([]libpod.CtrCreateOption, 0) 230 switch { 231 case c.UsernsMode.IsNS(): 232 ns := c.UsernsMode.NS() 233 if ns == "" { 234 return nil, errors.Errorf("invalid empty user-defined user namespace") 235 } 236 _, err := os.Stat(ns) 237 if err != nil { 238 return nil, err 239 } 240 options = append(options, libpod.WithIDMappings(*c.IDMappings)) 241 case c.UsernsMode.IsContainer(): 242 connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container()) 243 if err != nil { 244 return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container()) 245 } 246 options = append(options, libpod.WithUserNSFrom(connectedCtr)) 247 default: 248 options = append(options, libpod.WithIDMappings(*c.IDMappings)) 249 } 250 251 options = append(options, libpod.WithUser(c.User)) 252 options = append(options, libpod.WithGroups(c.GroupAdd)) 253 254 return options, nil 255 } 256 257 // ConfigureGenerator configures the generator according to the current state 258 // of the UserConfig. 259 func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error { 260 if IsNS(string(c.UsernsMode)) { 261 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil { 262 return err 263 } 264 // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping 265 g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) 266 g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) 267 } 268 269 if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() { 270 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { 271 return err 272 } 273 } 274 for _, uidmap := range c.IDMappings.UIDMap { 275 g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) 276 } 277 for _, gidmap := range c.IDMappings.GIDMap { 278 g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) 279 } 280 return nil 281 } 282 283 func (c *UserConfig) getPostConfigureNetNS() bool { 284 hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0 285 postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost() 286 return postConfigureNetNS 287 } 288 289 // InNS returns true if the UserConfig indicates to be in a dedicated user 290 // namespace. 291 func (c *UserConfig) InNS(isRootless bool) bool { 292 hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0 293 return isRootless || (hasUserns && !c.UsernsMode.IsHost()) 294 } 295 296 // ToCreateOptions converts the input to container create options. 297 func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { 298 options := make([]libpod.CtrCreateOption, 0) 299 if c.IpcMode.IsHost() { 300 options = append(options, libpod.WithShmDir("/dev/shm")) 301 } else if c.IpcMode.IsContainer() { 302 connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container()) 303 if err != nil { 304 return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container()) 305 } 306 307 options = append(options, libpod.WithIPCNSFrom(connectedCtr)) 308 options = append(options, libpod.WithShmDir(connectedCtr.ShmDir())) 309 } 310 311 return options, nil 312 } 313 314 // ConfigureGenerator configures the generator according to the current state 315 // of the IpcConfig. 316 func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error { 317 ipcMode := c.IpcMode 318 if IsNS(string(ipcMode)) { 319 return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode))) 320 } 321 if ipcMode.IsHost() { 322 return g.RemoveLinuxNamespace(string(spec.IPCNamespace)) 323 } 324 if ipcCtr := ipcMode.Container(); ipcCtr != "" { 325 logrus.Debugf("Using container %s ipcmode", ipcCtr) 326 } 327 328 return nil 329 } 330 331 // ConfigureGenerator configures the generator according to the current state 332 // of the CgroupConfig. 333 func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error { 334 cgroupMode := c.CgroupMode 335 if cgroupMode.IsDefaultValue() { 336 // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1. 337 unified, err := cgroups.IsCgroup2UnifiedMode() 338 if err != nil { 339 return err 340 } 341 if unified { 342 cgroupMode = "private" 343 } else { 344 cgroupMode = "host" 345 } 346 } 347 if cgroupMode.IsNS() { 348 return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode))) 349 } 350 if cgroupMode.IsHost() { 351 return g.RemoveLinuxNamespace(string(spec.CgroupNamespace)) 352 } 353 if cgroupMode.IsPrivate() { 354 return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") 355 } 356 if cgCtr := cgroupMode.Container(); cgCtr != "" { 357 logrus.Debugf("Using container %s cgroup mode", cgCtr) 358 } 359 return nil 360 } 361 362 // ToCreateOptions converts the input to container create options. 363 func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { 364 options := make([]libpod.CtrCreateOption, 0) 365 if c.PidMode.IsContainer() { 366 connectedCtr, err := runtime.LookupContainer(c.PidMode.Container()) 367 if err != nil { 368 return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container()) 369 } 370 371 options = append(options, libpod.WithPIDNSFrom(connectedCtr)) 372 } 373 374 return options, nil 375 } 376 377 // ConfigureGenerator configures the generator according to the current state 378 // of the PidConfig. 379 func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error { 380 pidMode := c.PidMode 381 if IsNS(string(pidMode)) { 382 return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode))) 383 } 384 if pidMode.IsHost() { 385 return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) 386 } 387 if pidCtr := pidMode.Container(); pidCtr != "" { 388 logrus.Debugf("using container %s pidmode", pidCtr) 389 } 390 if IsPod(string(pidMode)) { 391 logrus.Debug("using pod pidmode") 392 } 393 return nil 394 } 395 396 // ToCreateOptions converts the input to container create options. 397 func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) { 398 options := make([]libpod.CtrCreateOption, 0) 399 if IsPod(string(c.UtsMode)) { 400 options = append(options, libpod.WithUTSNSFromPod(pod)) 401 } 402 if c.UtsMode.IsContainer() { 403 connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container()) 404 if err != nil { 405 return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container()) 406 } 407 408 options = append(options, libpod.WithUTSNSFrom(connectedCtr)) 409 } 410 if c.NoHosts { 411 options = append(options, libpod.WithUseImageHosts()) 412 } 413 if len(c.HostAdd) > 0 && !c.NoHosts { 414 options = append(options, libpod.WithHosts(c.HostAdd)) 415 } 416 417 return options, nil 418 } 419 420 // ConfigureGenerator configures the generator according to the current state 421 // of the UtsConfig. 422 func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error { 423 hostname := c.Hostname 424 utsCtrID := c.UtsMode.Container() 425 var err error 426 if hostname == "" { 427 switch { 428 case utsCtrID != "": 429 utsCtr, err := runtime.LookupContainer(utsCtrID) 430 if err != nil { 431 return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID) 432 } 433 hostname = utsCtr.Hostname() 434 case net.NetMode.IsHost() || c.UtsMode.IsHost(): 435 hostname, err = os.Hostname() 436 if err != nil { 437 return errors.Wrap(err, "unable to retrieve hostname of the host") 438 } 439 default: 440 logrus.Debug("No hostname set; container's hostname will default to runtime default") 441 } 442 } 443 g.RemoveHostname() 444 if c.Hostname != "" || !c.UtsMode.IsHost() { 445 // Set the hostname in the OCI configuration only 446 // if specified by the user or if we are creating 447 // a new UTS namespace. 448 g.SetHostname(hostname) 449 } 450 g.AddProcessEnv("HOSTNAME", hostname) 451 452 utsMode := c.UtsMode 453 if IsNS(string(utsMode)) { 454 return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode))) 455 } 456 if utsMode.IsHost() { 457 return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) 458 } 459 if utsCtr := utsMode.Container(); utsCtr != "" { 460 logrus.Debugf("using container %s utsmode", utsCtr) 461 } 462 return nil 463 }