github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/specgen/generate/namespaces.go (about) 1 package generate 2 3 import ( 4 "os" 5 6 "github.com/containers/common/pkg/capabilities" 7 "github.com/containers/libpod/libpod" 8 "github.com/containers/libpod/libpod/image" 9 "github.com/containers/libpod/pkg/specgen" 10 "github.com/cri-o/ocicni/pkg/ocicni" 11 spec "github.com/opencontainers/runtime-spec/specs-go" 12 "github.com/opencontainers/runtime-tools/generate" 13 "github.com/pkg/errors" 14 "github.com/sirupsen/logrus" 15 ) 16 17 func GenerateNamespaceContainerOpts(s *specgen.SpecGenerator, rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) { 18 var portBindings []ocicni.PortMapping 19 options := make([]libpod.CtrCreateOption, 0) 20 21 // Cgroups 22 switch { 23 case s.CgroupNS.IsPrivate(): 24 ns := s.CgroupNS.Value 25 if _, err := os.Stat(ns); err != nil { 26 return nil, err 27 } 28 case s.CgroupNS.IsContainer(): 29 connectedCtr, err := rt.LookupContainer(s.CgroupNS.Value) 30 if err != nil { 31 return nil, errors.Wrapf(err, "container %q not found", s.CgroupNS.Value) 32 } 33 options = append(options, libpod.WithCgroupNSFrom(connectedCtr)) 34 // TODO 35 //default: 36 // return nil, errors.New("cgroup name only supports private and container") 37 } 38 39 if s.CgroupParent != "" { 40 options = append(options, libpod.WithCgroupParent(s.CgroupParent)) 41 } 42 43 if s.CgroupsMode != "" { 44 options = append(options, libpod.WithCgroupsMode(s.CgroupsMode)) 45 } 46 47 // ipc 48 switch { 49 case s.IpcNS.IsHost(): 50 options = append(options, libpod.WithShmDir("/dev/shm")) 51 case s.IpcNS.IsContainer(): 52 connectedCtr, err := rt.LookupContainer(s.IpcNS.Value) 53 if err != nil { 54 return nil, errors.Wrapf(err, "container %q not found", s.IpcNS.Value) 55 } 56 options = append(options, libpod.WithIPCNSFrom(connectedCtr)) 57 options = append(options, libpod.WithShmDir(connectedCtr.ShmDir())) 58 } 59 60 // pid 61 if s.PidNS.IsContainer() { 62 connectedCtr, err := rt.LookupContainer(s.PidNS.Value) 63 if err != nil { 64 return nil, errors.Wrapf(err, "container %q not found", s.PidNS.Value) 65 } 66 options = append(options, libpod.WithPIDNSFrom(connectedCtr)) 67 } 68 69 // uts 70 switch { 71 case s.UtsNS.IsPod(): 72 connectedPod, err := rt.LookupPod(s.UtsNS.Value) 73 if err != nil { 74 return nil, errors.Wrapf(err, "pod %q not found", s.UtsNS.Value) 75 } 76 options = append(options, libpod.WithUTSNSFromPod(connectedPod)) 77 case s.UtsNS.IsContainer(): 78 connectedCtr, err := rt.LookupContainer(s.UtsNS.Value) 79 if err != nil { 80 return nil, errors.Wrapf(err, "container %q not found", s.UtsNS.Value) 81 } 82 83 options = append(options, libpod.WithUTSNSFrom(connectedCtr)) 84 } 85 86 if s.UseImageHosts { 87 options = append(options, libpod.WithUseImageHosts()) 88 } else if len(s.HostAdd) > 0 { 89 options = append(options, libpod.WithHosts(s.HostAdd)) 90 } 91 92 // User 93 94 switch { 95 case s.UserNS.IsPath(): 96 ns := s.UserNS.Value 97 if ns == "" { 98 return nil, errors.Errorf("invalid empty user-defined user namespace") 99 } 100 _, err := os.Stat(ns) 101 if err != nil { 102 return nil, err 103 } 104 if s.IDMappings != nil { 105 options = append(options, libpod.WithIDMappings(*s.IDMappings)) 106 } 107 case s.UserNS.IsContainer(): 108 connectedCtr, err := rt.LookupContainer(s.UserNS.Value) 109 if err != nil { 110 return nil, errors.Wrapf(err, "container %q not found", s.UserNS.Value) 111 } 112 options = append(options, libpod.WithUserNSFrom(connectedCtr)) 113 default: 114 if s.IDMappings != nil { 115 options = append(options, libpod.WithIDMappings(*s.IDMappings)) 116 } 117 } 118 119 options = append(options, libpod.WithUser(s.User)) 120 options = append(options, libpod.WithGroups(s.Groups)) 121 122 if len(s.PortMappings) > 0 { 123 portBindings = s.PortMappings 124 } 125 126 switch { 127 case s.NetNS.IsPath(): 128 ns := s.NetNS.Value 129 if ns == "" { 130 return nil, errors.Errorf("invalid empty user-defined network namespace") 131 } 132 _, err := os.Stat(ns) 133 if err != nil { 134 return nil, err 135 } 136 case s.NetNS.IsContainer(): 137 connectedCtr, err := rt.LookupContainer(s.NetNS.Value) 138 if err != nil { 139 return nil, errors.Wrapf(err, "container %q not found", s.NetNS.Value) 140 } 141 options = append(options, libpod.WithNetNSFrom(connectedCtr)) 142 case !s.NetNS.IsHost() && s.NetNS.NSMode != specgen.NoNetwork: 143 postConfigureNetNS := !s.UserNS.IsHost() 144 options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(s.NetNS.NSMode), s.CNINetworks)) 145 } 146 147 if len(s.DNSSearch) > 0 { 148 options = append(options, libpod.WithDNSSearch(s.DNSSearch)) 149 } 150 if len(s.DNSServer) > 0 { 151 // TODO I'm not sure how we are going to handle this given the input 152 if len(s.DNSServer) == 1 { //&& strings.ToLower(s.DNSServer[0].) == "none" { 153 options = append(options, libpod.WithUseImageResolvConf()) 154 } else { 155 var dnsServers []string 156 for _, d := range s.DNSServer { 157 dnsServers = append(dnsServers, d.String()) 158 } 159 options = append(options, libpod.WithDNS(dnsServers)) 160 } 161 } 162 if len(s.DNSOption) > 0 { 163 options = append(options, libpod.WithDNSOption(s.DNSOption)) 164 } 165 if s.StaticIP != nil { 166 options = append(options, libpod.WithStaticIP(*s.StaticIP)) 167 } 168 169 if s.StaticMAC != nil { 170 options = append(options, libpod.WithStaticMAC(*s.StaticMAC)) 171 } 172 return options, nil 173 } 174 175 func pidConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { 176 if s.PidNS.IsPath() { 177 return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value) 178 } 179 if s.PidNS.IsHost() { 180 return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) 181 } 182 if s.PidNS.IsContainer() { 183 logrus.Debugf("using container %s pidmode", s.PidNS.Value) 184 } 185 if s.PidNS.IsPod() { 186 logrus.Debug("using pod pidmode") 187 } 188 return nil 189 } 190 191 func utsConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, runtime *libpod.Runtime) error { 192 hostname := s.Hostname 193 var err error 194 if hostname == "" { 195 switch { 196 case s.UtsNS.IsContainer(): 197 utsCtr, err := runtime.LookupContainer(s.UtsNS.Value) 198 if err != nil { 199 return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", s.UtsNS.Value) 200 } 201 hostname = utsCtr.Hostname() 202 case s.NetNS.IsHost() || s.UtsNS.IsHost(): 203 hostname, err = os.Hostname() 204 if err != nil { 205 return errors.Wrap(err, "unable to retrieve hostname of the host") 206 } 207 default: 208 logrus.Debug("No hostname set; container's hostname will default to runtime default") 209 } 210 } 211 g.RemoveHostname() 212 if s.Hostname != "" || !s.UtsNS.IsHost() { 213 // Set the hostname in the OCI configuration only 214 // if specified by the user or if we are creating 215 // a new UTS namespace. 216 g.SetHostname(hostname) 217 } 218 g.AddProcessEnv("HOSTNAME", hostname) 219 220 if s.UtsNS.IsPath() { 221 return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value) 222 } 223 if s.UtsNS.IsHost() { 224 return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) 225 } 226 if s.UtsNS.IsContainer() { 227 logrus.Debugf("using container %s utsmode", s.UtsNS.Value) 228 } 229 return nil 230 } 231 232 func ipcConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { 233 if s.IpcNS.IsPath() { 234 return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value) 235 } 236 if s.IpcNS.IsHost() { 237 return g.RemoveLinuxNamespace(s.IpcNS.Value) 238 } 239 if s.IpcNS.IsContainer() { 240 logrus.Debugf("Using container %s ipcmode", s.IpcNS.Value) 241 } 242 return nil 243 } 244 245 func cgroupConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { 246 if s.CgroupNS.IsPath() { 247 return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value) 248 } 249 if s.CgroupNS.IsHost() { 250 return g.RemoveLinuxNamespace(s.CgroupNS.Value) 251 } 252 if s.CgroupNS.IsPrivate() { 253 return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") 254 } 255 if s.CgroupNS.IsContainer() { 256 logrus.Debugf("Using container %s cgroup mode", s.CgroupNS.Value) 257 } 258 return nil 259 } 260 261 func networkConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { 262 switch { 263 case s.NetNS.IsHost(): 264 logrus.Debug("Using host netmode") 265 if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { 266 return err 267 } 268 269 case s.NetNS.NSMode == specgen.NoNetwork: 270 logrus.Debug("Using none netmode") 271 case s.NetNS.NSMode == specgen.Bridge: 272 logrus.Debug("Using bridge netmode") 273 case s.NetNS.IsContainer(): 274 logrus.Debugf("using container %s netmode", s.NetNS.Value) 275 case s.NetNS.IsPath(): 276 logrus.Debug("Using ns netmode") 277 if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { 278 return err 279 } 280 case s.NetNS.IsPod(): 281 logrus.Debug("Using pod netmode, unless pod is not sharing") 282 case s.NetNS.NSMode == specgen.Slirp: 283 logrus.Debug("Using slirp4netns netmode") 284 default: 285 return errors.Errorf("unknown network mode") 286 } 287 288 if g.Config.Annotations == nil { 289 g.Config.Annotations = make(map[string]string) 290 } 291 292 if s.PublishImagePorts { 293 g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue 294 } else { 295 g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse 296 } 297 298 return nil 299 } 300 301 func userConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { 302 if s.UserNS.IsPath() { 303 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil { 304 return err 305 } 306 // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping 307 g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) 308 g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) 309 } 310 311 if s.IDMappings != nil { 312 if (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) && !s.UserNS.IsHost() { 313 if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { 314 return err 315 } 316 } 317 for _, uidmap := range s.IDMappings.UIDMap { 318 g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) 319 } 320 for _, gidmap := range s.IDMappings.GIDMap { 321 g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) 322 } 323 } 324 return nil 325 } 326 327 func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error { 328 // HANDLE CAPABILITIES 329 // NOTE: Must happen before SECCOMP 330 if s.Privileged { 331 g.SetupPrivileged(true) 332 } 333 334 useNotRoot := func(user string) bool { 335 if user == "" || user == "root" || user == "0" { 336 return false 337 } 338 return true 339 } 340 configSpec := g.Config 341 var err error 342 var caplist []string 343 bounding := configSpec.Process.Capabilities.Bounding 344 if useNotRoot(s.User) { 345 configSpec.Process.Capabilities.Bounding = caplist 346 } 347 caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop) 348 if err != nil { 349 return err 350 } 351 352 configSpec.Process.Capabilities.Bounding = caplist 353 configSpec.Process.Capabilities.Permitted = caplist 354 configSpec.Process.Capabilities.Inheritable = caplist 355 configSpec.Process.Capabilities.Effective = caplist 356 configSpec.Process.Capabilities.Ambient = caplist 357 if useNotRoot(s.User) { 358 caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop) 359 if err != nil { 360 return err 361 } 362 } 363 configSpec.Process.Capabilities.Bounding = caplist 364 365 // HANDLE SECCOMP 366 if s.SeccompProfilePath != "unconfined" { 367 seccompConfig, err := getSeccompConfig(s, configSpec, newImage) 368 if err != nil { 369 return err 370 } 371 configSpec.Linux.Seccomp = seccompConfig 372 } 373 374 // Clear default Seccomp profile from Generator for privileged containers 375 if s.SeccompProfilePath == "unconfined" || s.Privileged { 376 configSpec.Linux.Seccomp = nil 377 } 378 379 g.SetRootReadonly(s.ReadOnlyFilesystem) 380 for sysctlKey, sysctlVal := range s.Sysctl { 381 g.AddLinuxSysctl(sysctlKey, sysctlVal) 382 } 383 384 return nil 385 } 386 387 // GetNamespaceOptions transforms a slice of kernel namespaces 388 // into a slice of pod create options. Currently, not all 389 // kernel namespaces are supported, and they will be returned in an error 390 func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { 391 var options []libpod.PodCreateOption 392 var erroredOptions []libpod.PodCreateOption 393 for _, toShare := range ns { 394 switch toShare { 395 case "cgroup": 396 options = append(options, libpod.WithPodCgroups()) 397 case "net": 398 options = append(options, libpod.WithPodNet()) 399 case "mnt": 400 return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") 401 case "pid": 402 options = append(options, libpod.WithPodPID()) 403 case "user": 404 return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level") 405 case "ipc": 406 options = append(options, libpod.WithPodIPC()) 407 case "uts": 408 options = append(options, libpod.WithPodUTS()) 409 case "": 410 case "none": 411 return erroredOptions, nil 412 default: 413 return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare) 414 } 415 } 416 return options, nil 417 }