summaryrefslogtreecommitdiff
path: root/pkg/spec/namespaces.go
diff options
context:
space:
mode:
authorPeter Hunt <pehunt@redhat.com>2019-09-11 16:50:02 -0400
committerPeter Hunt <pehunt@redhat.com>2019-11-07 21:23:23 -0500
commitdcf3c742b1ac4d641d66810113f3d17441a412f4 (patch)
tree2112a19ba95287179b0a44bbd90719a86d317c5d /pkg/spec/namespaces.go
parent3463a7194c504790e73a1750109c1813a7c3cfe9 (diff)
downloadpodman-dcf3c742b1ac4d641d66810113f3d17441a412f4.tar.gz
podman-dcf3c742b1ac4d641d66810113f3d17441a412f4.tar.bz2
podman-dcf3c742b1ac4d641d66810113f3d17441a412f4.zip
Split up create config handling of namespaces and security
As it stands, createconfig is a huge struct. This works fine when the only caller is when we create a container with a fully created config. However, if we wish to share code for security and namespace configuration, a single large struct becomes unweildy, as well as difficult to configure with the single createConfigToOCISpec function. This PR breaks up namespace and security configuration into their own structs, with the eventual goal of allowing the namespace/security fields to be configured by the pod create cli, and allow the infra container to share this with the pod's containers. Signed-off-by: Peter Hunt <pehunt@redhat.com>
Diffstat (limited to 'pkg/spec/namespaces.go')
-rw-r--r--pkg/spec/namespaces.go433
1 files changed, 433 insertions, 0 deletions
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
new file mode 100644
index 000000000..a45137416
--- /dev/null
+++ b/pkg/spec/namespaces.go
@@ -0,0 +1,433 @@
+package createconfig
+
+import (
+ "net"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
+ var portBindings []ocicni.PortMapping
+ var err error
+ if len(c.PortBindings) > 0 {
+ portBindings, err = NatToOCIPortBindings(c.PortBindings)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create port bindings")
+ }
+ }
+
+ options := make([]libpod.CtrCreateOption, 0)
+ userNetworks := c.NetMode.UserDefined()
+ networks := make([]string, 0)
+
+ if IsPod(userNetworks) {
+ userNetworks = ""
+ }
+ if userNetworks != "" {
+ for _, netName := range strings.Split(userNetworks, ",") {
+ if netName == "" {
+ return nil, errors.Errorf("container networks %q invalid", userNetworks)
+ }
+ networks = append(networks, netName)
+ }
+ }
+
+ if c.NetMode.IsNS() {
+ ns := c.NetMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.NetMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
+ }
+ options = append(options, libpod.WithNetNSFrom(connectedCtr))
+ } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
+ postConfigureNetNS := userns.getPostConfigureNetNS()
+ options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
+ }
+
+ if len(c.DNSSearch) > 0 {
+ options = append(options, libpod.WithDNSSearch(c.DNSSearch))
+ }
+ if len(c.DNSServers) > 0 {
+ if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
+ options = append(options, libpod.WithUseImageResolvConf())
+ } else {
+ options = append(options, libpod.WithDNS(c.DNSServers))
+ }
+ }
+ if len(c.DNSOpt) > 0 {
+ options = append(options, libpod.WithDNSOption(c.DNSOpt))
+ }
+ if c.IPAddress != "" {
+ ip := net.ParseIP(c.IPAddress)
+ if ip == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
+ } else if ip.To4() == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
+ }
+ options = append(options, libpod.WithStaticIP(ip))
+ }
+
+ if c.MacAddress != "" {
+ mac, err := net.ParseMAC(c.MacAddress)
+ if err != nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
+ }
+ options = append(options, libpod.WithStaticMAC(mac))
+ }
+
+ return options, nil
+}
+
+func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
+ netMode := c.NetMode
+ if netMode.IsHost() {
+ logrus.Debug("Using host netmode")
+ if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
+ return err
+ }
+ } else if netMode.IsNone() {
+ logrus.Debug("Using none netmode")
+ } else if netMode.IsBridge() {
+ logrus.Debug("Using bridge netmode")
+ } else if netCtr := netMode.Container(); netCtr != "" {
+ logrus.Debugf("using container %s netmode", netCtr)
+ } else if IsNS(string(netMode)) {
+ logrus.Debug("Using ns netmode")
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
+ return err
+ }
+ } else if IsPod(string(netMode)) {
+ logrus.Debug("Using pod netmode, unless pod is not sharing")
+ } else if netMode.IsSlirp4netns() {
+ logrus.Debug("Using slirp4netns netmode")
+ } else if netMode.IsUserDefined() {
+ logrus.Debug("Using user defined netmode")
+ } else {
+ return errors.Errorf("unknown network mode")
+ }
+
+ if c.HTTPProxy {
+ for _, envSpec := range []string{
+ "http_proxy",
+ "HTTP_PROXY",
+ "https_proxy",
+ "HTTPS_PROXY",
+ "ftp_proxy",
+ "FTP_PROXY",
+ "no_proxy",
+ "NO_PROXY",
+ } {
+ envVal := os.Getenv(envSpec)
+ if envVal != "" {
+ g.AddProcessEnv(envSpec, envVal)
+ }
+ }
+ }
+
+ if g.Config.Annotations == nil {
+ g.Config.Annotations = make(map[string]string)
+ }
+
+ if c.PublishAll {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
+ } else {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
+ }
+
+ return nil
+}
+
+// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
+func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
+ var portBindings []ocicni.PortMapping
+ for containerPb, hostPb := range ports {
+ var pm ocicni.PortMapping
+ pm.ContainerPort = int32(containerPb.Int())
+ for _, i := range hostPb {
+ var hostPort int
+ var err error
+ pm.HostIP = i.HostIP
+ if i.HostPort == "" {
+ hostPort = containerPb.Int()
+ } else {
+ hostPort, err = strconv.Atoi(i.HostPort)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert host port to integer")
+ }
+ }
+
+ pm.HostPort = int32(hostPort)
+ pm.Protocol = containerPb.Proto()
+ portBindings = append(portBindings, pm)
+ }
+ }
+ return portBindings, nil
+}
+
+func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.CgroupMode.IsNS() {
+ ns := c.CgroupMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.CgroupMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
+ }
+ options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
+ }
+
+ if c.CgroupParent != "" {
+ options = append(options, libpod.WithCgroupParent(c.CgroupParent))
+ }
+
+ if c.Cgroups == "disabled" {
+ options = append(options, libpod.WithNoCgroups())
+ }
+
+ return options, nil
+}
+
+func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.UsernsMode.IsNS() {
+ ns := c.UsernsMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined user namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ } else if c.UsernsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
+ }
+ options = append(options, libpod.WithUserNSFrom(connectedCtr))
+ } else {
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ }
+
+ options = append(options, libpod.WithUser(c.User))
+ options = append(options, libpod.WithGroups(c.GroupAdd))
+
+ return options, nil
+}
+
+func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
+ if IsNS(string(c.UsernsMode)) {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); 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 (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
+ return err
+ }
+ }
+ for _, uidmap := range c.IDMappings.UIDMap {
+ g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+ }
+ for _, gidmap := range c.IDMappings.GIDMap {
+ g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+ }
+ return nil
+}
+
+func (c *UserConfig) getPostConfigureNetNS() bool {
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
+ return postConfigureNetNS
+}
+
+func (c *UserConfig) InNS(isRootless bool) bool {
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ return isRootless || (hasUserns && !c.UsernsMode.IsHost())
+}
+
+func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.IpcMode.IsHost() {
+ options = append(options, libpod.WithShmDir("/dev/shm"))
+ } else if c.IpcMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
+ }
+
+ options = append(options, libpod.WithIPCNSFrom(connectedCtr))
+ options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
+ }
+
+ return options, nil
+}
+
+func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
+ ipcMode := c.IpcMode
+ if IsNS(string(ipcMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
+ }
+ if ipcMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
+ }
+ if ipcCtr := ipcMode.Container(); ipcCtr != "" {
+ logrus.Debugf("Using container %s ipcmode", ipcCtr)
+ }
+
+ return nil
+}
+
+func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
+ cgroupMode := c.CgroupMode
+ if cgroupMode.IsDefaultValue() {
+ // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if unified {
+ cgroupMode = "private"
+ } else {
+ cgroupMode = "host"
+ }
+ }
+ if cgroupMode.IsNS() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
+ }
+ if cgroupMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
+ }
+ if cgroupMode.IsPrivate() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
+ }
+ if cgCtr := cgroupMode.Container(); cgCtr != "" {
+ logrus.Debugf("Using container %s cgroup mode", cgCtr)
+ }
+ return nil
+}
+
+func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.PidMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
+ }
+
+ options = append(options, libpod.WithPIDNSFrom(connectedCtr))
+ }
+
+ return options, nil
+}
+
+func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
+ pidMode := c.PidMode
+ if IsNS(string(pidMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
+ }
+ if pidMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
+ }
+ if pidCtr := pidMode.Container(); pidCtr != "" {
+ logrus.Debugf("using container %s pidmode", pidCtr)
+ }
+ if IsPod(string(pidMode)) {
+ logrus.Debug("using pod pidmode")
+ }
+ return nil
+}
+
+func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if IsPod(string(c.UtsMode)) {
+ options = append(options, libpod.WithUTSNSFromPod(pod))
+ }
+ if c.UtsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
+ }
+
+ options = append(options, libpod.WithUTSNSFrom(connectedCtr))
+ }
+ if c.NoHosts {
+ options = append(options, libpod.WithUseImageHosts())
+ }
+ if len(c.HostAdd) > 0 && !c.NoHosts {
+ options = append(options, libpod.WithHosts(c.HostAdd))
+ }
+
+ return options, nil
+}
+
+func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
+ hostname := c.Hostname
+ var err error
+ if hostname == "" {
+ if utsCtrID := c.UtsMode.Container(); utsCtrID != "" {
+ utsCtr, err := runtime.GetContainer(utsCtrID)
+ if err != nil {
+ return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
+ }
+ hostname = utsCtr.Hostname()
+ } else if net.NetMode.IsHost() || c.UtsMode.IsHost() {
+ hostname, err = os.Hostname()
+ if err != nil {
+ return errors.Wrap(err, "unable to retrieve hostname of the host")
+ }
+ } else {
+ logrus.Debug("No hostname set; container's hostname will default to runtime default")
+ }
+ }
+ g.RemoveHostname()
+ if c.Hostname != "" || !c.UtsMode.IsHost() {
+ // Set the hostname in the OCI configuration only
+ // if specified by the user or if we are creating
+ // a new UTS namespace.
+ g.SetHostname(hostname)
+ }
+ g.AddProcessEnv("HOSTNAME", hostname)
+
+ utsMode := c.UtsMode
+ if IsNS(string(utsMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
+ }
+ if utsMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
+ }
+ if utsCtr := utsMode.Container(); utsCtr != "" {
+ logrus.Debugf("using container %s utsmode", utsCtr)
+ }
+ return nil
+}