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