diff options
Diffstat (limited to 'pkg/specgen')
-rw-r--r-- | pkg/specgen/container_validate.go | 7 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 114 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 60 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 600 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 65 | ||||
-rw-r--r-- | pkg/specgen/generate/security.go | 110 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 102 | ||||
-rw-r--r-- | pkg/specgen/pod_validate.go | 8 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 33 |
9 files changed, 671 insertions, 428 deletions
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 9152e7ee7..df9c77cbc 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -86,18 +86,15 @@ func (s *SpecGenerator) Validate() error { // // ContainerNetworkConfig // - if !s.NetNS.IsPrivate() && s.ConfigureNetNS { - return errors.New("can only configure network namespace when creating a network a network namespace") - } // useimageresolveconf conflicts with dnsserver, dnssearch, dnsoption if s.UseImageResolvConf { - if len(s.DNSServer) > 0 { + if len(s.DNSServers) > 0 { return exclusiveOptions("UseImageResolvConf", "DNSServer") } if len(s.DNSSearch) > 0 { return exclusiveOptions("UseImageResolvConf", "DNSSearch") } - if len(s.DNSOption) > 0 { + if len(s.DNSOptions) > 0 { return exclusiveOptions("UseImageResolvConf", "DNSOption") } } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 7233acb8a..de3239fda 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -13,24 +13,36 @@ import ( ) func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) error { + var appendEntryPoint bool + // TODO add support for raw rootfs newImage, err := r.ImageRuntime().NewFromLocal(s.Image) if err != nil { return err } // Image stop signal - if s.StopSignal == nil && newImage.Config != nil { - sig, err := signal.ParseSignalNameOrNumber(newImage.Config.StopSignal) + if s.StopSignal == nil { + stopSignal, err := newImage.StopSignal(ctx) + if err != nil { + return err + } + sig, err := signal.ParseSignalNameOrNumber(stopSignal) if err != nil { return err } s.StopSignal = &sig } + // Image envs from the image if they don't exist // already - if newImage.Config != nil && len(newImage.Config.Env) > 0 { - envs, err := envLib.ParseSlice(newImage.Config.Env) + env, err := newImage.Env(ctx) + if err != nil { + return err + } + + if len(env) > 0 { + envs, err := envLib.ParseSlice(env) if err != nil { return err } @@ -41,16 +53,29 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } } + labels, err := newImage.Labels(ctx) + if err != nil { + return err + } + // labels from the image that dont exist already - if config := newImage.Config; config != nil { - for k, v := range config.Labels { - if _, exists := s.Labels[k]; !exists { - s.Labels[k] = v - } + for k, v := range labels { + if _, exists := s.Labels[k]; !exists { + s.Labels[k] = v } } // annotations + + // Add annotations from the image + annotations, err := newImage.Annotations(ctx) + if err != nil { + return err + } + for k, v := range annotations { + annotations[k] = v + } + // in the event this container is in a pod, and the pod has an infra container // we will want to configure it as a type "container" instead defaulting to // the behavior of a "sandbox" container @@ -59,36 +84,47 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // VM, which is the default behavior // - "container" denotes the container should join the VM of the SandboxID // (the infra container) - s.Annotations = make(map[string]string) + if len(s.Pod) > 0 { - s.Annotations[ann.SandboxID] = s.Pod - s.Annotations[ann.ContainerType] = ann.ContainerTypeContainer - } - // - // Next, add annotations from the image - annotations, err := newImage.Annotations(ctx) - if err != nil { - return err + annotations[ann.SandboxID] = s.Pod + annotations[ann.ContainerType] = ann.ContainerTypeContainer } - for k, v := range annotations { + + // now pass in the values from client + for k, v := range s.Annotations { annotations[k] = v } + s.Annotations = annotations // entrypoint - if config := newImage.Config; config != nil { - if len(s.Entrypoint) < 1 && len(config.Entrypoint) > 0 { - s.Entrypoint = config.Entrypoint - } - if len(s.Command) < 1 && len(config.Cmd) > 0 { - s.Command = config.Cmd - } - if len(s.Command) < 1 && len(s.Entrypoint) < 1 { - return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image") - } - // workdir - if len(s.WorkDir) < 1 && len(config.WorkingDir) > 1 { - s.WorkDir = config.WorkingDir + entrypoint, err := newImage.Entrypoint(ctx) + if err != nil { + return err + } + if len(s.Entrypoint) < 1 && len(entrypoint) > 0 { + appendEntryPoint = true + s.Entrypoint = entrypoint + } + command, err := newImage.Cmd(ctx) + if err != nil { + return err + } + if len(s.Command) < 1 && len(command) > 0 { + if appendEntryPoint { + s.Command = entrypoint } + s.Command = append(s.Command, command...) + } + if len(s.Command) < 1 && len(s.Entrypoint) < 1 { + return errors.Errorf("No command provided or as CMD or ENTRYPOINT in this image") + } + // workdir + workingDir, err := newImage.WorkingDir(ctx) + if err != nil { + return err + } + if len(s.WorkDir) < 1 && len(workingDir) > 1 { + s.WorkDir = workingDir } if len(s.SeccompProfilePath) < 1 { @@ -99,15 +135,17 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat s.SeccompProfilePath = p } - if user := s.User; len(user) == 0 { - switch { + if len(s.User) == 0 { + s.User, err = newImage.User(ctx) + if err != nil { + return err + } + // TODO This should be enabled when namespaces actually work //case usernsMode.IsKeepID(): // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID()) - case newImage.Config == nil || (newImage.Config != nil && len(newImage.Config.User) == 0): + if len(s.User) == 0 { s.User = "0" - default: - s.User = newImage.Config.User } } if err := finishThrottleDevices(s); err != nil { @@ -116,7 +154,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // Unless already set via the CLI, check if we need to disable process // labels or set the defaults. if len(s.SelinuxOpts) == 0 { - if err := SetLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil { + if err := setLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil { return err } } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 264e0ff8e..1be77d315 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -23,7 +23,61 @@ func MakeContainer(rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Contai return nil, err } - options, err := createContainerOptions(rt, s) + // If joining a pod, retrieve the pod for use. + var pod *libpod.Pod + if s.Pod != "" { + foundPod, err := rt.LookupPod(s.Pod) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) + } + pod = foundPod + } + + // Set defaults for unset namespaces + if s.PidNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod) + if err != nil { + return nil, err + } + s.PidNS = defaultNS + } + if s.IpcNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod) + if err != nil { + return nil, err + } + s.IpcNS = defaultNS + } + if s.UtsNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod) + if err != nil { + return nil, err + } + s.UtsNS = defaultNS + } + if s.UserNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod) + if err != nil { + return nil, err + } + s.UserNS = defaultNS + } + if s.NetNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod) + if err != nil { + return nil, err + } + s.NetNS = defaultNS + } + if s.CgroupNS.IsDefault() { + defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod) + if err != nil { + return nil, err + } + s.CgroupNS = defaultNS + } + + options, err := createContainerOptions(rt, s, pod) if err != nil { return nil, err } @@ -47,7 +101,7 @@ func MakeContainer(rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Contai return rt.NewContainer(context.Background(), runtimeSpec, options...) } -func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator) ([]libpod.CtrCreateOption, error) { +func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) { var options []libpod.CtrCreateOption var err error @@ -123,7 +177,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator) ([]lib options = append(options, libpod.WithPrivileged(s.Privileged)) // Get namespace related options - namespaceOptions, err := GenerateNamespaceContainerOpts(s, rt) + namespaceOptions, err := GenerateNamespaceOptions(s, rt, pod) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index cdd7d86da..4ec1e859c 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -2,317 +2,389 @@ package generate import ( "os" + "strings" - "github.com/containers/common/pkg/capabilities" + "github.com/containers/common/pkg/config" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/cgroups" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/specgen" - "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -func GenerateNamespaceContainerOpts(s *specgen.SpecGenerator, rt *libpod.Runtime) ([]libpod.CtrCreateOption, error) { - var portBindings []ocicni.PortMapping - options := make([]libpod.CtrCreateOption, 0) +// Get the default namespace mode for any given namespace type. +func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) { + // The default for most is private + toReturn := specgen.Namespace{} + toReturn.NSMode = specgen.Private - // Cgroups - switch { - case s.CgroupNS.IsPrivate(): - ns := s.CgroupNS.Value - if _, err := os.Stat(ns); err != nil { - return nil, err + // Ensure case insensitivity + nsType = strings.ToLower(nsType) + + // If the pod is not nil - check shared namespaces + if pod != nil { + podMode := false + switch { + case nsType == "pid" && pod.SharesPID(): + podMode = true + case nsType == "ipc" && pod.SharesIPC(): + podMode = true + case nsType == "uts" && pod.SharesUTS(): + podMode = true + case nsType == "user" && pod.SharesUser(): + podMode = true + case nsType == "net" && pod.SharesNet(): + podMode = true + case nsType == "cgroup" && pod.SharesCgroup(): + podMode = true } - case s.CgroupNS.IsContainer(): - connectedCtr, err := rt.LookupContainer(s.CgroupNS.Value) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.CgroupNS.Value) + if podMode { + toReturn.NSMode = specgen.FromPod + return toReturn, nil } - options = append(options, libpod.WithCgroupNSFrom(connectedCtr)) - // TODO - //default: - // return nil, errors.New("cgroup name only supports private and container") } - if s.CgroupParent != "" { - options = append(options, libpod.WithCgroupParent(s.CgroupParent)) + // If we have containers.conf and are not using cgroupns, use that. + if cfg != nil && nsType != "cgroup" { + switch nsType { + case "pid": + return specgen.ParseNamespace(cfg.Containers.PidNS) + case "ipc": + return specgen.ParseNamespace(cfg.Containers.IPCNS) + case "uts": + return specgen.ParseNamespace(cfg.Containers.UTSNS) + case "user": + // TODO: This may not work for --userns=auto + return specgen.ParseNamespace(cfg.Containers.UserNS) + case "net": + ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS) + return ns, err + } } - if s.CgroupsMode != "" { - options = append(options, libpod.WithCgroupsMode(s.CgroupsMode)) + switch nsType { + case "pid", "ipc", "uts": + // PID, IPC, UTS both default to private, do nothing + case "user": + // User namespace always defaults to host + toReturn.NSMode = specgen.Host + case "net": + // Net defaults to Slirp on rootless, Bridge otherwise. + if rootless.IsRootless() { + toReturn.NSMode = specgen.Slirp + } else { + toReturn.NSMode = specgen.Bridge + } + case "cgroup": + // Cgroup is host for v1, private for v2. + // We can't trust c/common for this, as it only assumes private. + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return toReturn, err + } + if !cgroupsv2 { + toReturn.NSMode = specgen.Host + } + default: + return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %s passed", nsType) } - // ipc - switch { - case s.IpcNS.IsHost(): - options = append(options, libpod.WithShmDir("/dev/shm")) - case s.IpcNS.IsContainer(): - connectedCtr, err := rt.LookupContainer(s.IpcNS.Value) + return toReturn, nil +} + +// GenerateNamespaceOptions generates container creation options for all +// namespaces in a SpecGenerator. +// Pod is the pod the container will join. May be nil is the container is not +// joining a pod. +// TODO: Consider grouping options that are not directly attached to a namespace +// elsewhere. +func GenerateNamespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) { + toReturn := []libpod.CtrCreateOption{} + + // If pod is not nil, get infra container. + var infraCtr *libpod.Container + if pod != nil { + infraID, err := pod.InfraContainerID() if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.IpcNS.Value) + // This is likely to be of the fatal kind (pod was + // removed) so hard fail + return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID()) + } + if infraID != "" { + ctr, err := rt.GetContainer(infraID) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID) + } + infraCtr = ctr } - options = append(options, libpod.WithIPCNSFrom(connectedCtr)) - options = append(options, libpod.WithShmDir(connectedCtr.ShmDir())) } - // pid - if s.PidNS.IsContainer() { - connectedCtr, err := rt.LookupContainer(s.PidNS.Value) + errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container") + + // PID + switch s.PidNS.NSMode { + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra + } + toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr)) + case specgen.FromContainer: + pidCtr, err := rt.LookupContainer(s.PidNS.Value) if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.PidNS.Value) + return nil, errors.Wrapf(err, "error looking up container to share pid namespace with") } - options = append(options, libpod.WithPIDNSFrom(connectedCtr)) + toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) } - // uts - switch { - case s.UtsNS.IsPod(): - connectedPod, err := rt.LookupPod(s.UtsNS.Value) - if err != nil { - return nil, errors.Wrapf(err, "pod %q not found", s.UtsNS.Value) + // IPC + switch s.IpcNS.NSMode { + case specgen.Host: + // Force use of host /dev/shm for host namespace + toReturn = append(toReturn, libpod.WithShmDir("/dev/shm")) + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra } - options = append(options, libpod.WithUTSNSFromPod(connectedPod)) - case s.UtsNS.IsContainer(): - connectedCtr, err := rt.LookupContainer(s.UtsNS.Value) + toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr)) + case specgen.FromContainer: + ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.UtsNS.Value) + return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with") } - - options = append(options, libpod.WithUTSNSFrom(connectedCtr)) + toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr)) + toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir())) } - if s.UseImageHosts { - options = append(options, libpod.WithUseImageHosts()) - } else if len(s.HostAdd) > 0 { - options = append(options, libpod.WithHosts(s.HostAdd)) + // UTS + switch s.UtsNS.NSMode { + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra + } + toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) + case specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) + if err != nil { + return nil, errors.Wrapf(err, "error looking up container to share uts namespace with") + } + toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) } // User - - switch { - case s.UserNS.IsPath(): - ns := s.UserNS.Value - if ns == "" { - return nil, errors.Errorf("invalid empty user-defined user namespace") + switch s.UserNS.NSMode { + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra } - _, err := os.Stat(ns) + toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr)) + case specgen.FromContainer: + userCtr, err := rt.LookupContainer(s.UserNS.Value) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "error looking up container to share user namespace with") } - if s.IDMappings != nil { - options = append(options, libpod.WithIDMappings(*s.IDMappings)) + toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) + } + + if s.IDMappings != nil { + toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) + } + if s.User != "" { + toReturn = append(toReturn, libpod.WithUser(s.User)) + } + if len(s.Groups) > 0 { + toReturn = append(toReturn, libpod.WithGroups(s.Groups)) + } + + // Cgroup + switch s.CgroupNS.NSMode { + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra } - case s.UserNS.IsContainer(): - connectedCtr, err := rt.LookupContainer(s.UserNS.Value) + toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr)) + case specgen.FromContainer: + cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.UserNS.Value) - } - options = append(options, libpod.WithUserNSFrom(connectedCtr)) - default: - if s.IDMappings != nil { - options = append(options, libpod.WithIDMappings(*s.IDMappings)) + return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with") } + toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) } - options = append(options, libpod.WithUser(s.User)) - options = append(options, libpod.WithGroups(s.Groups)) - - if len(s.PortMappings) > 0 { - portBindings = s.PortMappings + if s.CgroupParent != "" { + toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent)) } - switch { - case s.NetNS.IsPath(): - ns := s.NetNS.Value - if ns == "" { - return nil, errors.Errorf("invalid empty user-defined network namespace") - } - _, err := os.Stat(ns) - if err != nil { - return nil, err + if s.CgroupsMode != "" { + toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode)) + } + + // Net + // TODO image ports + // TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we + // are in bridge mode. + postConfigureNetNS := !s.UserNS.IsHost() + switch s.NetNS.NSMode { + case specgen.FromPod: + if pod == nil || infraCtr == nil { + return nil, errNoInfra } - case s.NetNS.IsContainer(): - connectedCtr, err := rt.LookupContainer(s.NetNS.Value) + toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr)) + case specgen.FromContainer: + netCtr, err := rt.LookupContainer(s.NetNS.Value) if err != nil { - return nil, errors.Wrapf(err, "container %q not found", s.NetNS.Value) + return nil, errors.Wrapf(err, "error looking up container to share net namespace with") } - options = append(options, libpod.WithNetNSFrom(connectedCtr)) - case !s.NetNS.IsHost() && s.NetNS.NSMode != specgen.NoNetwork: - postConfigureNetNS := !s.UserNS.IsHost() - options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(s.NetNS.NSMode), s.CNINetworks)) + toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) + case specgen.Slirp: + toReturn = append(toReturn, libpod.WithNetNS(s.PortMappings, postConfigureNetNS, "slirp4netns", nil)) + case specgen.Bridge: + toReturn = append(toReturn, libpod.WithNetNS(s.PortMappings, postConfigureNetNS, "bridge", s.CNINetworks)) } - if len(s.DNSSearch) > 0 { - options = append(options, libpod.WithDNSSearch(s.DNSSearch)) + if s.UseImageHosts { + toReturn = append(toReturn, libpod.WithUseImageHosts()) + } else if len(s.HostAdd) > 0 { + toReturn = append(toReturn, libpod.WithHosts(s.HostAdd)) } - if len(s.DNSServer) > 0 { - // TODO I'm not sure how we are going to handle this given the input - if len(s.DNSServer) == 1 { //&& strings.ToLower(s.DNSServer[0].) == "none" { - options = append(options, libpod.WithUseImageResolvConf()) - } else { - var dnsServers []string - for _, d := range s.DNSServer { - dnsServers = append(dnsServers, d.String()) - } - options = append(options, libpod.WithDNS(dnsServers)) + if len(s.DNSSearch) > 0 { + toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch)) + } + if s.UseImageResolvConf { + toReturn = append(toReturn, libpod.WithUseImageResolvConf()) + } else if len(s.DNSServers) > 0 { + var dnsServers []string + for _, d := range s.DNSServers { + dnsServers = append(dnsServers, d.String()) } + toReturn = append(toReturn, libpod.WithDNS(dnsServers)) } - if len(s.DNSOption) > 0 { - options = append(options, libpod.WithDNSOption(s.DNSOption)) + if len(s.DNSOptions) > 0 { + toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions)) } if s.StaticIP != nil { - options = append(options, libpod.WithStaticIP(*s.StaticIP)) + toReturn = append(toReturn, libpod.WithStaticIP(*s.StaticIP)) } - if s.StaticMAC != nil { - options = append(options, libpod.WithStaticMAC(*s.StaticMAC)) + toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC)) } - return options, nil + + return toReturn, nil } -func pidConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { - if s.PidNS.IsPath() { - return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value) - } - if s.PidNS.IsHost() { - return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime) error { + // PID + switch s.PidNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.PidNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified PID namespace path %q", s.PidNS.Value) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil { + return err + } } - if s.PidNS.IsContainer() { - logrus.Debugf("using container %s pidmode", s.PidNS.Value) + + // IPC + switch s.IpcNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.IpcNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified IPC namespace path %q", s.IpcNS.Value) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil { + return err + } } - if s.PidNS.IsPod() { - logrus.Debug("using pod pidmode") + + // UTS + switch s.UtsNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.UtsNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified UTS namespace path %q", s.UtsNS.Value) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil { + return err + } } - return nil -} -func utsConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, runtime *libpod.Runtime) error { hostname := s.Hostname - var err error if hostname == "" { switch { - case s.UtsNS.IsContainer(): - utsCtr, err := runtime.LookupContainer(s.UtsNS.Value) + case s.UtsNS.NSMode == specgen.FromContainer: + utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", s.UtsNS.Value) + return errors.Wrapf(err, "error looking up container to share uts namespace with") } hostname = utsCtr.Hostname() - case s.NetNS.IsHost() || s.UtsNS.IsHost(): - hostname, err = os.Hostname() + case s.NetNS.NSMode == specgen.Host || s.UtsNS.NSMode == specgen.Host: + tmpHostname, err := os.Hostname() if err != nil { return errors.Wrap(err, "unable to retrieve hostname of the host") } + hostname = tmpHostname default: logrus.Debug("No hostname set; container's hostname will default to runtime default") } } + g.RemoveHostname() - if s.Hostname != "" || !s.UtsNS.IsHost() { - // Set the hostname in the OCI configuration only - // if specified by the user or if we are creating - // a new UTS namespace. + if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host { + // Set the hostname in the OCI configuration only if specified by + // the user or if we are creating a new UTS namespace. + // TODO: Should we be doing this for pod or container shared + // namespaces? g.SetHostname(hostname) } g.AddProcessEnv("HOSTNAME", hostname) - if s.UtsNS.IsPath() { - return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value) - } - if s.UtsNS.IsHost() { - return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) - } - if s.UtsNS.IsContainer() { - logrus.Debugf("using container %s utsmode", s.UtsNS.Value) - } - return nil -} - -func ipcConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { - if s.IpcNS.IsPath() { - return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value) - } - if s.IpcNS.IsHost() { - return g.RemoveLinuxNamespace(s.IpcNS.Value) - } - if s.IpcNS.IsContainer() { - logrus.Debugf("Using container %s ipcmode", s.IpcNS.Value) - } - return nil -} - -func cgroupConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { - if s.CgroupNS.IsPath() { - return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value) - } - if s.CgroupNS.IsHost() { - return g.RemoveLinuxNamespace(s.CgroupNS.Value) - } - if s.CgroupNS.IsPrivate() { - return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") - } - if s.CgroupNS.IsContainer() { - logrus.Debugf("Using container %s cgroup mode", s.CgroupNS.Value) - } - return nil -} - -func networkConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { - switch { - case s.NetNS.IsHost(): - logrus.Debug("Using host netmode") - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return err - } - - case s.NetNS.NSMode == specgen.NoNetwork: - logrus.Debug("Using none netmode") - case s.NetNS.NSMode == specgen.Bridge: - logrus.Debug("Using bridge netmode") - case s.NetNS.IsContainer(): - logrus.Debugf("using container %s netmode", s.NetNS.Value) - case s.NetNS.IsPath(): - logrus.Debug("Using ns netmode") - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { - return err + // User + switch s.UserNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.UserNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified user namespace path %s", s.UserNS.Value) } - case s.NetNS.IsPod(): - logrus.Debug("Using pod netmode, unless pod is not sharing") - case s.NetNS.NSMode == specgen.Slirp: - logrus.Debug("Using slirp4netns netmode") - default: - return errors.Errorf("unknown network mode") - } - - if g.Config.Annotations == nil { - g.Config.Annotations = make(map[string]string) - } - - if s.PublishImagePorts { - g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue - } else { - g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse - } - - return nil -} - -func userConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) error { - if s.UserNS.IsPath() { if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil { return err } // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) - } - - if s.IDMappings != nil { - if (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) && !s.UserNS.IsHost() { - if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { - return err - } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { + return err + } + if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) { + return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace") } for _, uidmap := range s.IDMappings.UIDMap { g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) @@ -321,64 +393,52 @@ func userConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator) err g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) } } - return nil -} - -func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error { - // HANDLE CAPABILITIES - // NOTE: Must happen before SECCOMP - if s.Privileged { - g.SetupPrivileged(true) - } - useNotRoot := func(user string) bool { - if user == "" || user == "root" || user == "0" { - return false + // Cgroup + switch s.CgroupNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.CgroupNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified cgroup namespace path %s", s.CgroupNS.Value) } - return true - } - configSpec := g.Config - var err error - var caplist []string - bounding := configSpec.Process.Capabilities.Bounding - if useNotRoot(s.User) { - configSpec.Process.Capabilities.Bounding = caplist - } - caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop) - if err != nil { - return err - } - - configSpec.Process.Capabilities.Bounding = caplist - configSpec.Process.Capabilities.Permitted = caplist - configSpec.Process.Capabilities.Inheritable = caplist - configSpec.Process.Capabilities.Effective = caplist - configSpec.Process.Capabilities.Ambient = caplist - if useNotRoot(s.User) { - caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop) - if err != nil { + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil { + return err + } + case specgen.Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil { return err } } - configSpec.Process.Capabilities.Bounding = caplist - // HANDLE SECCOMP - if s.SeccompProfilePath != "unconfined" { - seccompConfig, err := getSeccompConfig(s, configSpec, newImage) - if err != nil { + // Net + switch s.NetNS.NSMode { + case specgen.Path: + if _, err := os.Stat(s.NetNS.Value); err != nil { + return errors.Wrapf(err, "cannot find specified network namespace path %s", s.NetNS.Value) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { + return err + } + case specgen.Host: + if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { + return err + } + case specgen.Private, specgen.NoNetwork: + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil { return err } - configSpec.Linux.Seccomp = seccompConfig } - // Clear default Seccomp profile from Generator for privileged containers - if s.SeccompProfilePath == "unconfined" || s.Privileged { - configSpec.Linux.Seccomp = nil + if g.Config.Annotations == nil { + g.Config.Annotations = make(map[string]string) } - - g.SetRootReadonly(s.ReadOnlyFilesystem) - for sysctlKey, sysctlVal := range s.Sysctl { - g.AddLinuxSysctl(sysctlKey, sysctlVal) + if s.PublishImagePorts { + g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue + } else { + g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse } return nil diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 0ed091f9a..8ca95016e 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -12,6 +12,42 @@ import ( "github.com/opencontainers/runtime-tools/generate" ) +func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { + var ( + kernelMax uint64 = 1048576 + isRootless = rootless.IsRootless() + nofileSet = false + nprocSet = false + ) + + if s.Rlimits == nil { + g.Config.Process.Rlimits = nil + return nil + } + + for _, u := range s.Rlimits { + name := "RLIMIT_" + strings.ToUpper(u.Type) + if name == "RLIMIT_NOFILE" { + nofileSet = true + } else if name == "RLIMIT_NPROC" { + nprocSet = true + } + g.AddProcessRlimits(name, u.Hard, u.Soft) + } + + // If not explicitly overridden by the user, default number of open + // files and number of processes to the maximum they can be set to + // (without overriding a sysctl) + if !nofileSet && !isRootless { + g.AddProcessRlimits("RLIMIT_NOFILE", kernelMax, kernelMax) + } + if !nprocSet && !isRootless { + g.AddProcessRlimits("RLIMIT_NPROC", kernelMax, kernelMax) + } + + return nil +} + func SpecGenToOCI(s *specgen.SpecGenerator, rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) { var ( inUserNS bool @@ -176,35 +212,12 @@ func SpecGenToOCI(s *specgen.SpecGenerator, rt *libpod.Runtime, newImage *image. g.AddProcessEnv(name, val) } - // TODO rlimits and ulimits needs further refinement by someone more - // familiar with the code. - //if err := addRlimits(config, &g); err != nil { - // return nil, err - //} - - // NAMESPACES - - if err := pidConfigureGenerator(s, &g); err != nil { - return nil, err - } - - if err := userConfigureGenerator(s, &g); err != nil { - return nil, err - } - - if err := networkConfigureGenerator(s, &g); err != nil { + if err := addRlimits(s, &g); err != nil { return nil, err } - if err := utsConfigureGenerator(s, &g, rt); err != nil { - return nil, err - } - - if err := ipcConfigureGenerator(s, &g); err != nil { - return nil, err - } - - if err := cgroupConfigureGenerator(s, &g); err != nil { + // NAMESPACES + if err := specConfigureNamespaces(s, &g, rt); err != nil { return nil, err } configSpec := g.Config diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index ef4b3b47a..e2da9e976 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -1,15 +1,22 @@ package generate import ( + "strings" + + "github.com/containers/common/pkg/capabilities" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/util" + "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -// SetLabelOpts sets the label options of the SecurityConfig according to the +// setLabelOpts sets the label options of the SecurityConfig according to the // input. -func SetLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { if !runtime.EnableLabeling() || s.Privileged { s.SelinuxOpts = label.DisableSecOpt() return nil @@ -48,12 +55,10 @@ func SetLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s return nil } -// ConfigureGenerator configures the generator according to the input. -/* -func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error { +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image) error { // HANDLE CAPABILITIES // NOTE: Must happen before SECCOMP - if c.Privileged { + if s.Privileged { g.SetupPrivileged(true) } @@ -63,56 +68,66 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon } return true } - configSpec := g.Config var err error - var defaultCaplist []string + var caplist []string bounding := configSpec.Process.Capabilities.Bounding - if useNotRoot(user.User) { - configSpec.Process.Capabilities.Bounding = defaultCaplist + if useNotRoot(s.User) { + configSpec.Process.Capabilities.Bounding = caplist } - defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop) + caplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, s.CapAdd, s.CapDrop) if err != nil { return err } + privCapsRequired := []string{} + + // If the container image specifies an label with a + // capabilities.ContainerImageLabel then split the comma separated list + // of capabilities and record them. This list indicates the only + // capabilities, required to run the container. + var capsRequiredRequested []string + for key, val := range s.Labels { + if util.StringInSlice(key, capabilities.ContainerImageLabels) { + capsRequiredRequested = strings.Split(val, ",") + } + } + if !s.Privileged && len(capsRequiredRequested) > 0 { - privCapRequired := []string{} - - if !c.Privileged && len(c.CapRequired) > 0 { - // Pass CapRequired in CapAdd field to normalize capabilities names - capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil) + // Pass capRequiredRequested in CapAdd field to normalize capabilities names + capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) if err != nil { - logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ",")) + logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) } else { - // Verify all capRequiered are in the defaultCapList - for _, cap := range capRequired { - if !util.StringInSlice(cap, defaultCaplist) { - privCapRequired = append(privCapRequired, cap) + // Verify all capRequiered are in the capList + for _, cap := range capsRequired { + if !util.StringInSlice(cap, caplist) { + privCapsRequired = append(privCapsRequired, cap) } } } - if len(privCapRequired) == 0 { - defaultCaplist = capRequired + if len(privCapsRequired) == 0 { + caplist = capsRequired } else { - logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ",")) + logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) } } - configSpec.Process.Capabilities.Bounding = defaultCaplist - configSpec.Process.Capabilities.Permitted = defaultCaplist - configSpec.Process.Capabilities.Inheritable = defaultCaplist - configSpec.Process.Capabilities.Effective = defaultCaplist - configSpec.Process.Capabilities.Ambient = defaultCaplist - if useNotRoot(user.User) { - defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop) + + configSpec.Process.Capabilities.Bounding = caplist + configSpec.Process.Capabilities.Permitted = caplist + configSpec.Process.Capabilities.Inheritable = caplist + configSpec.Process.Capabilities.Effective = caplist + configSpec.Process.Capabilities.Ambient = caplist + if useNotRoot(s.User) { + caplist, err = capabilities.MergeCapabilities(bounding, s.CapAdd, s.CapDrop) if err != nil { return err } } - configSpec.Process.Capabilities.Bounding = defaultCaplist + configSpec.Process.Capabilities.Bounding = caplist // HANDLE SECCOMP - if c.SeccompProfilePath != "unconfined" { - seccompConfig, err := getSeccompConfig(c, configSpec) + if s.SeccompProfilePath != "unconfined" { + seccompConfig, err := getSeccompConfig(s, configSpec, newImage) if err != nil { return err } @@ -120,35 +135,14 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon } // Clear default Seccomp profile from Generator for privileged containers - if c.SeccompProfilePath == "unconfined" || c.Privileged { + if s.SeccompProfilePath == "unconfined" || s.Privileged { configSpec.Linux.Seccomp = nil } - for _, opt := range c.SecurityOpts { - // Split on both : and = - splitOpt := strings.Split(opt, "=") - if len(splitOpt) == 1 { - splitOpt = strings.Split(opt, ":") - } - if len(splitOpt) < 2 { - continue - } - switch splitOpt[0] { - case "label": - configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1] - case "seccomp": - configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1] - case "apparmor": - configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1] - } - } - - g.SetRootReadonly(c.ReadOnlyRootfs) - for sysctlKey, sysctlVal := range c.Sysctl { + g.SetRootReadonly(s.ReadOnlyFilesystem) + for sysctlKey, sysctlVal := range s.Sysctl { g.AddLinuxSysctl(sysctlKey, sysctlVal) } return nil } - -*/ diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 2e7f80fe8..4f35b31bf 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -1,6 +1,8 @@ package specgen import ( + "strings" + "github.com/pkg/errors" ) @@ -39,6 +41,12 @@ type Namespace struct { Value string `json:"string,omitempty"` } +// IsDefault returns whether the namespace is set to the default setting (which +// also includes the empty string). +func (n *Namespace) IsDefault() bool { + return n.NSMode == Default || n.NSMode == "" +} + // IsHost returns a bool if the namespace is host based func (n *Namespace) IsHost() bool { return n.NSMode == Host @@ -69,11 +77,24 @@ func validateNetNS(n *Namespace) error { return nil } switch n.NSMode { - case Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge, Slirp: + case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge, Slirp: break default: return errors.Errorf("invalid network %q", n.NSMode) } + + // Path and From Container MUST have a string value set + if n.NSMode == Path || n.NSMode == FromContainer { + if len(n.Value) < 1 { + return errors.Errorf("namespace mode %s requires a value", n.NSMode) + } + } else { + // All others must NOT set a string value + if len(n.Value) > 0 { + return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) + } + } + return nil } @@ -83,6 +104,15 @@ func (n *Namespace) validate() error { if n == nil { return nil } + switch n.NSMode { + case "", Default, Host, Path, FromContainer, FromPod, Private: + // Valid, do nothing + case NoNetwork, Bridge, Slirp: + return errors.Errorf("cannot use network modes with non-network namespace") + default: + return errors.Errorf("invalid namespace type %s specified", n.NSMode) + } + // Path and From Container MUST have a string value set if n.NSMode == Path || n.NSMode == FromContainer { if len(n.Value) < 1 { @@ -96,3 +126,73 @@ func (n *Namespace) validate() error { } return nil } + +// ParseNamespace parses a namespace in string form. +// This is not intended for the network namespace, which has a separate +// function. +func ParseNamespace(ns string) (Namespace, error) { + toReturn := Namespace{} + switch { + case ns == "host": + toReturn.NSMode = Host + case ns == "private": + toReturn.NSMode = Private + case strings.HasPrefix(ns, "ns:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, errors.Errorf("must provide a path to a namespace when specifying ns:") + } + toReturn.NSMode = Path + toReturn.Value = split[1] + case strings.HasPrefix(ns, "container:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, errors.Errorf("must provide name or ID or a container when specifying container:") + } + toReturn.NSMode = FromContainer + toReturn.Value = split[1] + default: + return toReturn, errors.Errorf("unrecognized namespace mode %s passed", ns) + } + + return toReturn, nil +} + +// ParseNetworkNamespace parses a network namespace specification in string +// form. +// Returns a namespace and (optionally) a list of CNI networks to join. +func ParseNetworkNamespace(ns string) (Namespace, []string, error) { + toReturn := Namespace{} + var cniNetworks []string + switch { + case ns == "bridge": + toReturn.NSMode = Bridge + case ns == "none": + toReturn.NSMode = NoNetwork + case ns == "host": + toReturn.NSMode = Host + case ns == "private": + toReturn.NSMode = Private + case strings.HasPrefix(ns, "ns:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying ns:") + } + toReturn.NSMode = Path + toReturn.Value = split[1] + case strings.HasPrefix(ns, "container:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:") + } + toReturn.NSMode = FromContainer + toReturn.Value = split[1] + default: + // Assume we have been given a list of CNI networks. + // Which only works in bridge mode, so set that. + cniNetworks = strings.Split(ns, ",") + toReturn.NSMode = Bridge + } + + return toReturn, cniNetworks, nil +} diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 9e9659fa9..f2f90e58d 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -1,14 +1,16 @@ package specgen import ( - "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" ) var ( // ErrInvalidPodSpecConfig describes an error given when the podspecgenerator is invalid ErrInvalidPodSpecConfig error = errors.New("invalid pod spec") + // containerConfig has the default configurations defined in containers.conf + containerConfig = util.DefaultContainerConfig() ) func exclusivePodOptions(opt1, opt2 string) error { @@ -96,10 +98,10 @@ func (p *PodSpecGenerator) Validate() error { } } if len(p.InfraImage) < 1 { - p.InfraImage = define.DefaultInfraImage + p.InfraImage = containerConfig.Engine.InfraImage } if len(p.InfraCommand) < 1 { - p.InfraCommand = []string{define.DefaultInfraCommand} + p.InfraCommand = []string{containerConfig.Engine.InfraCommand} } return nil } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 1a05733f9..e102a3234 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -5,7 +5,6 @@ import ( "syscall" "github.com/containers/image/v5/manifest" - "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -283,25 +282,20 @@ type ContainerNetworkConfig struct { // namespace. // Mandatory. NetNS Namespace `json:"netns,omitempty"` - // ConfigureNetNS is whether Libpod will configure the container's - // network namespace to send and receive traffic. - // Only available is NetNS is private - conflicts with other NetNS - // modes. - ConfigureNetNS bool `json:"configure_netns,omitempty"` // StaticIP is the a IPv4 address of the container. - // Only available if ConfigureNetNS is true. + // Only available if NetNS is set to Bridge. // Optional. StaticIP *net.IP `json:"static_ip,omitempty"` // StaticIPv6 is a static IPv6 address to set in the container. - // Only available if ConfigureNetNS is true. + // Only available if NetNS is set to Bridge. // Optional. StaticIPv6 *net.IP `json:"static_ipv6,omitempty"` // StaticMAC is a static MAC address to set in the container. - // Only available if ConfigureNetNS is true. + // Only available if NetNS is set to bridge. // Optional. StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"` // PortBindings is a set of ports to map into the container. - // Only available if ConfigureNetNS is true. + // Only available if NetNS is set to bridge or slirp. // Optional. PortMappings []ocicni.PortMapping `json:"portmappings,omitempty"` // PublishImagePorts will publish ports specified in the image to random @@ -312,31 +306,31 @@ type ContainerNetworkConfig struct { // If this list is empty, the default CNI network will be joined // instead. If at least one entry is present, we will not join the // default network (unless it is part of this list). - // Only available if ConfigureNetNS is true. + // Only available if NetNS is set to bridge. // Optional. CNINetworks []string `json:"cni_networks,omitempty"` // UseImageResolvConf indicates that resolv.conf should not be managed // by Podman, but instead sourced from the image. // Conflicts with DNSServer, DNSSearch, DNSOption. UseImageResolvConf bool `json:"use_image_resolve_conf,omitempty"` - // DNSServer is a set of DNS servers that will be used in the + // DNSServers is a set of DNS servers that will be used in the // container's resolv.conf, replacing the host's DNS Servers which are // used by default. // Conflicts with UseImageResolvConf. // Optional. - DNSServer []net.IP `json:"dns_server,omitempty"` + DNSServers []net.IP `json:"dns_server,omitempty"` // DNSSearch is a set of DNS search domains that will be used in the // container's resolv.conf, replacing the host's DNS search domains // which are used by default. // Conflicts with UseImageResolvConf. // Optional. DNSSearch []string `json:"dns_search,omitempty"` - // DNSOption is a set of DNS options that will be used in the + // DNSOptions is a set of DNS options that will be used in the // container's resolv.conf, replacing the host's DNS options which are // used by default. // Conflicts with UseImageResolvConf. // Optional. - DNSOption []string `json:"dns_option,omitempty"` + DNSOptions []string `json:"dns_option,omitempty"` // UseImageHosts indicates that /etc/hosts should not be managed by // Podman, and instead sourced from the image. // Conflicts with HostAdd. @@ -402,18 +396,9 @@ type Volumes struct { // NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs func NewSpecGenerator(image string) *SpecGenerator { - networkConfig := ContainerNetworkConfig{ - NetNS: Namespace{ - NSMode: Bridge, - }, - } csc := ContainerStorageConfig{Image: image} - if rootless.IsRootless() { - networkConfig.NetNS.NSMode = Slirp - } return &SpecGenerator{ ContainerStorageConfig: csc, - ContainerNetworkConfig: networkConfig, } } |